如何高效产生多个不重复的随机数

《编程珠玑》有这样一个练习题:

如果认真考虑了习题3,你将会面对生成小于n且没有重复的k个整数的问题。最简单的方法就是使用前k个正整数。这个极端的数据集合将不会明显地改变位图方法的运行时间,但是可能会歪曲系统排序的运行时间。如何生成位于0至之间的k个不同的随机顺序的随机整数?尽量使你的程序简短且高效。

这个题目,很多网友给出的解答是这样的:

  1. 每产生一个,都跟前面的随机数比较如果重复,就重新产生。但这方法不太好,且比较次数呈线性增长,越往后次数越多。
  2. 按顺序产生这些数,但随机产生它们的位置。然后选择前面要的个数即可:
    var a[100] int
    for i:=0; i<100; i++{
        a[i]=i
    }
    for i:=99; i>0; i--{
        index := rand.Int(100)
        a[i], a[index] = a[index],a[i]
    }

但是我觉得这样效益太差,比如要从 0 到 10000000 (一千万)中产生 500 个数,数组要交换 9999999 次,所以做了如下改进:

  • a) 生成指定范围的数组,记大小为 n,其中 n = max - min + 1 (包括范围边界);
  • b) 用 min 到 max 的数填充数组;
  • c) 随机产生一个下标 index ,范围 [0,n),取出数 index;
  • d) 如果 index 不是最后一个数(n-1),和 n-1 数交换,缩减 n ,重复步骤 b,直到生成数需要的随机数。

代码实现如下:

func generate(max, min, count int) []int {
    if min > max { // 交换最大最小范围
        max, min = min, max
    }
    if count > max-min { // 不可能,范围大小不够产生 count 个随机数
        return nil
    }
    n := max - min + 1
    if n < 0 { // 范围太大了,溢出...
        return nil
    }
    base := make([]int, n)
    for i, index := min, 0; i <= max; i++ { // 装填数组
        base[index] = i
        index++
    }
    result := make([]int, count)
    rd := rand.New(rand.NewSource(time.Now().UnixNano()))
    for i := 0; i < count; i, n = i-1, n-1 {
        ri := rd.Intn(n)     // 随机产生下标
        result[i] = base[ri] // 取数据
        if ri != n-1 {       // 如果不是最后一个,和最后一个交换,好缩减 n
            base[ri], base[n-1] = base[n-1], base[ri]
        }
    }
    return result
}

猜你喜欢

转载自www.cnblogs.com/asp-poor-as-john/p/10363777.html
今日推荐