从n个连续数字中随机选择数字

时间:2013-01-26 09:05:51

标签: c# algorithm

给定一个从0开始的n个连续数的整数数组,即

0,1,2,..n

我希望随机选择n/2个数字。

n=5 然后一个可能的集合将是0,3,5

如何轻松实现这一目标?

6 个答案:

答案 0 :(得分:3)

您可以遍历数字并确定每个数字在结果中的概率:

int n = 5;
int left = (n + 1) / 2;
int[] result = new int[left];
Random rnd = new Random();

for (int i = 0; left > 0; i++) {
  if (rnd.Next(n + 1 - i) < left) {
    result[result.Length - left] = i;
    left--;
  }
}

注意:这将始终产生排​​序结果。

编辑:

这是一个测试运行创建200000000个结果,计算生成的组合(其中二进制数表示组合,例如100110是0,3,4):

010011 : 9999164
110001 : 10003346
010101 : 9990975
100101 : 9998154
101001 : 10006305
100110 : 10003350
101010 : 10000583
101100 : 9995335
011001 : 10000007
001011 : 10001492
001110 : 10001158
100011 : 9994680
110100 : 9998226
110010 : 9999954
011010 : 10002269
000111 : 10004752
010110 : 9996886
011100 : 9999196
111000 : 10001094
001101 : 10003074

答案 1 :(得分:2)

我发现这样做的最简单方法是不完整Fisher-Yates shuffle。在n / 2次迭代后停止。

shuffle实际上适用于两个数组,即随机选择的数字和尚未使用的数字池,因此可供选择。实际上,两个数组的总大小是原始数组长度,因此可以通过分区将它们存储到位。

在n / 2次迭代之后,表示已选择的数字的分区是从原始数组中随机选择的。

另一种看待这种情况的方法是,完全混洗结果的前n / 2个数字不会被shuffle的n / 2 + 1或后续迭代改变。

答案 2 :(得分:2)

使用Fisher-Yates Shuffle,然后选择阵列中的第一个n/2项。

正如@Patricia Shanahan在她的回答中指出的那样,只需要使用Fisher-Yates对阵列中的第一个n/2项进行洗牌。

答案 3 :(得分:0)

一种方法是使用Next对象生成向量数的索引。您将新的生成值添加到ArrayList或HashSet等结构中以记住它。对于索引的每个新生成,您都要验证此结构中是否存在该值。

答案 4 :(得分:0)

有效使用二进制属性对选择进行编码的方法是:

  1. 生成随机32位无符号整数。

  2. 这样做n / 32次,并将它们放在名为mask

  3. 的数组中
  4. 这些随机整数的位选择所选的数字。具体地说,如果字大小是32,那么如果(掩码[i / 32]&gt;&gt;(i%32))&amp;&amp; 1是真的。

  5. 由于随机32位整数平均有16 0和16 1,因此该方法将接近您的n / 2值。

    假设你获得的实际1的数量是W,并且与n / 2相差k,k = W - n / 2

    如果您选择的k数太少,则生成0到n范围内的随机整数,转到该数字索引的位,并将您遇到的前0改为1,向前搜索。

    这样做k次。

    如果您的k太多,请将您遇到的前1个更改为零。

    这是一个优势,它也是存储子集的最紧凑的结构,每个位选择一个整数。你只需要范围,这个面具阵列,你可以选择。

    另外,你需要生成比其他shuffle方法少16倍的随机数,使其更有效率。

    最后,效率的提高会随着字数的增加而增加。对于64位无符号长整数,您需要的随机数比“n / 2互换置换方法”少32倍

    祝你好运!

答案 5 :(得分:0)

algorithm时是不允许Linq?

int[] arrInts = new int[] {0, 1, 2, 3, 4, 5, 6};
var r = new Random();
var randomInts = arrInts.OrderBy(i => r.Next(arrInts.Length))
                        .Take(arrInts.Length/2)
                        .ToArray();

修改:我对性能不是100%肯定,但在.AsParallel()之前使用.OrderBy()可能更好。

我喜欢使用Linq,因为它非常易读,编写算法花了大约5秒钟,而不是花费很多分钟来编写“手动”排序和循环。