关于Golang随机数生成重复数字

最近在尝试用Golang写一个随机图API,但是关于随机数的生成产生了一些问题

原来的代码是这样的

func findImage(s string) []byte {
    imageList := make([]string, 0)
    //遍历目录
    err := filepath.Walk(s,
        func(p string, f os.FileInfo, err error) error {
            if f == nil {
                return nil
            }
            if f.IsDir() {
                return nil
            }
            ext := path.Ext(f.Name())
            if ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".gif" {
                imageList = append(imageList, p)
            }
            return nil
        })
    if err != nil {
        log.Fatal("in finding img ", err)
    }
    //返回随机图片
    r := rand.New(rand.NewSource(time.Now().Unix()))
    i := r.Intn(len(imageList))
    img := imageList[i]
    b, err := ioutil.ReadFile(img)
    if err != nil {
        log.Fatal("in opening img ", err)
    }
    return b
}

我已经注意到需要用系统时间来作为生成随机数的种子,来避免随机序列不变的问题,但是事实上仍然出现了大量的重复数字,反映到程序里就是当快速刷新网页的时候,大概率连续显示的都是同一张图片

采用下列代码再次实验

for i := 0; i < 5; i++ {
    r := rand.New(rand.NewSource(time.Now().Unix()))
    fmt.Println(r.Intn(100))
}

结果打印出来的都是同样的数字

由于每次刷新网页该函数都会被调用一次,从而每次都新建了一个rand的对象,但是计算机的处理速度很快,导致时间戳的分辨率不够,一秒内有多个对象被创建,从而它们的种子都是同一个,所以随机序列也是一样的,导致每次从序列取得第一个数字都是一样的

解决方案是将rand对象提升为全局变量,提前声明,这样对象只会被创建一次,大大避免了重复数字的产生

完整代码

package randimg

import (
    "io/ioutil"
    "log"
    "math/rand"
    "net/http"
    "os"
    "path"
    "path/filepath"
    "time"

    "github.com/Unknwon/goconfig"
)

var imageDirPath string
var rnd = rand.New(rand.NewSource(time.Now().Unix()))

//GetRandomImage 返回随机图片
func GetRandomImage(w http.ResponseWriter, r *http.Request) {
    imageList := make([]string, 0)
    //遍历目录
    err := filepath.Walk(imageDirPath,
        func(p string, f os.FileInfo, err error) error {
            if f == nil {
                return nil
            }
            if f.IsDir() {
                return nil
            }
            ext := path.Ext(f.Name())
            if ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".gif" {
                imageList = append(imageList, p)
            }
            return nil
        })
    if err != nil {
        log.Fatal("in finding img ", err)
    }
    //返回随机图片
    img := imageList[rnd.Intn(len(imageList))]
    b, err := ioutil.ReadFile(img)
    if err != nil {
        log.Fatal("in opening img ", err)
    }
    w.Write(b)
}

func init() {
    cfg, err := goconfig.LoadConfigFile("./config/randimg.ini")
    if err != nil {
        log.Fatal(err)
    }
    imageDirPath, err = cfg.GetValue(goconfig.DEFAULT_SECTION, "ImageDirPath")
    if err != nil {
        log.Fatal(err)
    }
}

添加新评论

已有 1 条评论

|´・ω・)ノ 刚刚在留言板留言辣,为了防止没看见就再过来留个评论XD