可能重复:
Unique (non-repeating) random numbers in O(1)?
How do you efficiently generate a list of K non-repeating integers between 0 and an upper bound N
我想在某个diapason中生成随机数,我必须确定,每个新数字都不是前者的副本。一种解决方案是将以前生成的数字存储在容器中,并且每个新数字都检查容器。如果容器中有这样的数字,那么我们生成agin,否则我们使用并将其添加到容器中。但是对于每个新的数字,这个操作变得越来越慢。有没有更好的方法,或任何可以更快地工作并确保代的唯一性的兰特函数?
编辑:是的,有一个限制(例如从0到1.000.000.000)。但我想生成100.000个唯一数字! (如果解决方案是使用Qt功能,那将会很棒。)
答案 0 :(得分:15)
随机数是否有范围?如果您对随机数有限制并且继续生成唯一的随机数,那么您将以随机顺序得到x..y中所有数字的列表,其中x-y是随机数的有效范围。如果是这种情况,您可以通过简单地生成所有数字的列表x..y并对其进行混洗来大大提高速度,而不是生成数字。
答案 1 :(得分:10)
我认为有三种可能的方法,取决于范围大小,以及您可以使用其他算法所需的性能模式。
根据所需的速度,您可以将所有列表存储在数据库中。除了速度之外,它们不需要在内存中。
答案 2 :(得分:8)
填写一份包含您需要的号码的列表,然后随机播放列表并从一端选择您的号码。
答案 3 :(得分:4)
如果您使用简单的32位线性同余RNG(例如所谓的"Minimal Standard"),您所要做的就是存储您使用的种子值并将每个生成的数字与它进行比较。如果你再次达到这个值,你的序列就会开始重复,你就会失去价值。这是O(1),但当然限于2 ^ 32-1值(虽然我想你也可以使用64位版本)。
答案 4 :(得分:3)
我相信有一类伪随机数生成器具有您想要的属性:Linear congruential generator。如果正确定义,它将生成一个从0到N-1的整数列表,在您使用列表中的所有数字之前,没有两个数字重复。
#include <stdint.h>
/*
* Choose these values as follows:
*
* The MODULUS and INCREMENT must be relatively prime.
* The MULTIPLIER-1 must be divisible by all prime factors of the MODULUS.
* The MULTIPLIER-1 must be divisible by 4, if the MODULUS is divisible by 4.
*
* In addition, modulus must be <= 2**32 (0x0000000100000000ULL).
*
* A small example would be 8, 5, 3.
* A larger example would be 256, 129, 251.
* A useful example would be 0x0000000100000000ULL, 1664525, 1013904223.
*/
#define MODULUS (0x0000000100000000ULL)
#define MULTIPLIER (1664525)
#define INCREMENT (1013904223)
static uint64_t seed;
uint32_t lcg( void ) {
uint64_t temp;
temp = seed * MULTIPLIER + INCREMENT; // 64-bit intermediate product
seed = temp % MODULUS; // 32-bit end-result
return (uint32_t) seed;
}
您所要做的就是选择一个MODULUS,使其大于给定运行中您需要的数字。
答案 5 :(得分:2)
如果有这样的模式,这不是随机的吗?
据我所知,您必须存储和过滤所有不需要的数字......
答案 6 :(得分:2)
unsigned int N = 1000;
vector <unsigned int> vals(N);
for(unsigned int i = 0; i < vals.size(); ++i)
vals[i] = i;
std::random_shuffle(vals.begin(), vals.end());
unsigned int random_number_1 = vals[0];
unsigned int random_number_2 = vals[1];
unsigned int random_number_3 = vals[2];
//etc
答案 7 :(得分:1)
如果不能重复,则不是随意的。
编辑:
此外..
如果它们不能重复,它们就不适合有限的计算机
答案 8 :(得分:1)
您可以将数字存储在矢量中,并通过索引(1..n-1)获取它们。每次随机生成后,从向量中删除索引编号,然后在区间1..n-2中生成下一个编号。等
答案 9 :(得分:0)
如果您可以通过前一个值计算值,则没有问题,LFSR和LCG就可以了。如果您不希望另一个输出值可以计算,则可以使用counter mode中的块密码生成输出序列,前提是密码块长度等于输出长度。
答案 10 :(得分:0)
首先,随机和伪随机之间存在巨大差异。没有办法从确定性过程(例如计算机)生成完全随机的数字,而不会引入一些物理过程,如击键或其他熵源之间的延迟。
保存所有生成的数字的方法会相当快地减慢计算速度;您拥有的数字越多,您的存储需求就越大,直到您填满所有可用内存。使用众所周知的伪随机数生成器(如Linear Congruential Generator)会有更好的方法(如某人已经建议的那样);它超级快,只需要模块化乘法和加法,其背后的理论在Vol。中得到了很多提及。 Knuth的TAOCP 2。这样,所涉及的理论在重复之前保证了相当长的时间,并且所需的唯一存储是所使用的参数和种子。
答案 11 :(得分:0)
假设size = 100.000然后创建一个具有此大小的数组。创建随机数,然后将它们放入array.Problem是该数字的索引? randomNumber%size将为您提供索引。
当您输入下一个号码时,请将该功能用于索引并检查此值是否存在。如果不存在则将其存在然后创建新数字然后尝试。你可以用这种方式以最快的方式创造。这种方式的失败是你永远不会找到最后一节相同的数字。
例如最后一节是 1231232444556 3458923444556
即使它们完全不同,但最后一部分相同,您的列表中也不会有这样的数字。
答案 12 :(得分:0)
特别是给定所需数量的值,您需要一个线性反馈移位寄存器。
为什么?
没有随机播放步骤,也无需跟踪您已经触及的值。只要你的时间少于整个期间,你应该没事。
事实证明,Wikipedia article有一些C ++代码示例,它们比我给你的任何东西都更加经得起考验。请注意,您需要从循环内部提取值 - 循环只是迭代移位寄存器。您可以在代码段here中看到这一点。
(是的,我知道有人提到这一点,简要介绍一下 - 看到我正在修改。鉴于它没有在这里提出并且是解决海报问题的最佳方法,我认为它应该是再次提起。)
答案 13 :(得分:0)
这是我提出的一个有趣的解决方案:
假设您有1到1000的数字 - 并且您没有足够的内存。
您可以将所有1000个数字放入一个数组中,然后逐个删除它们,但是会出现内存溢出错误。
您可以将数组拆分为两个,因此您有一个1-500的数组和一个空数组
然后,您可以检查数字1中是否存在数字,或者第二个数组中是否存在数字。
假设你有1000个数字,你可以从1-1000得到一个随机数。如果小于500,请检查阵列1并将其删除(如果存在)。如果它不在数组2中,您可以添加它。
这减少了你的内存使用量。
如果使用递归传播,则可以将500数组拆分为250个空数组。
假设空数组不使用空格,可以减少内存使用量。
搜索速度也会快得多,因为如果你将其分解很多,你会生成一个数字,例如29.它小于500,小于250,小于125,小于62,小于31,大于15,所以你做那6次计算,然后检查包含平均16/2项的数组 - 总共8项。
我应该对此搜索申请专利,但我敢打赌它已经存在!
答案 14 :(得分:0)
您可以为每个可能的数字为1位数组分配足够的内存。并检查/设置每个生成的数字的位。例如,对于0到65535之间的数字,您只需要8192(8kb)的内存。
答案 15 :(得分:0)
对于到目前为止返回的结果数量,不会有纯功能方法不是O(n ^ 2) - 每次生成一个数字时,都需要来检查目前为止的每个结果。另外,想想你回来后会发生什么,例如1000中的第1000个数字 - 您将需要平均1000次尝试,直到随机算法出现最后一个未使用的数字,每次尝试都需要与已生成的数字进行平均499.5次比较。
从这一点可以清楚地看出,您发布的描述并不完全符合您的要求。正如其他人所说的那样,更好的方法是采用例如提前1000个数字,随机播放,然后逐步从该列表中返回数字。这将保证您不会返回任何重复项,并在初始设置后的O(1)时间内返回数字。
答案 16 :(得分:0)
如果随机数的范围无关紧要,您可以使用非常大范围的随机数,并希望您不会发生任何碰撞。如果你的范围是你想要创造的元素数量的数十亿倍,那么碰撞的可能性很小但仍然存在。如果数字不具有实际的随机分布,您可以使用两个部分编号{counter} {random x digits}来确保唯一编号但不会随机分布。
答案 17 :(得分:0)
随机生成器无法根据先前输出的值输出值,因为它们不是随机的。但是,您可以通过使用不同的随机值池来提高性能,每个值都使用不同的盐值组合,这将根据您拥有的池数量来划分数量。
答案 18 :(得分:0)
您需要多少个随机数?也许你可以将shuffle algorithm应用于预先计算的随机数组?
答案 19 :(得分:-1)
使用Hashset泛型类。此类不包含相同的值。您可以输入所有生成的数字,然后你可以在Hashset中使用它们。你也可以检查它是否存在.Hashset可以以最快的方式确定项目的存在。当列表变大时,哈希集不会变慢,这是最大的特点。
例如:
HashSet<int> array = new HashSet<int>();
array.Add(1);
array.Add(2);
array.Add(1);
foreach (var item in array)
{
Console.WriteLine(item);
}
Console.ReadKey();