给定一个从0开始的n个连续数的整数数组,即
0,1,2,..n
我希望随机选择n/2
个数字。
说n=5
然后一个可能的集合将是0,3,5
。
如何轻松实现这一目标?
答案 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)
有效使用二进制属性对选择进行编码的方法是:
生成随机32位无符号整数。
这样做n / 32次,并将它们放在名为mask
这些随机整数的位选择所选的数字。具体地说,如果字大小是32,那么如果(掩码[i / 32]&gt;&gt;(i%32))&amp;&amp; 1是真的。
由于随机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秒钟,而不是花费很多分钟来编写“手动”排序和循环。