无重复的随机数组生成

Random array generation with no duplicates

本文关键字:数组 随机      更新时间:2023-10-16

我正在尝试创建一个生成没有重复值的随机数组的东西。我已经看了其他答案,但似乎没有一个能帮助我理解。我想不出一种方法来真正生成不包含重复的随机数。以下是我迄今为止所尝试的:

srand(time(NULL));
int numbers [4];
for (int x=0; x!=4;x++)
{
numbers[x] = 1 + (rand() % 4) ;
printf("%d ", numbers[x]);
}

您开始用从0开始的连续元素填充容器

std::iota(begin(vec), end(vec), 0);

然后你给自己一个不错的随机数生成器,并将其正确地植入

std::mt19937 rng(std::random_device{}());

最后你用rng 打乱元素

std::shuffle(begin(vec), end(vec), rng);

生活在coliru


在某些实现中,random_device无法正常工作(最显著的是windows上的gcc),您必须使用替代种子,即当前时间→chrono

首先,rand()生成随机数,但不包含重复数。

如果要生成不重复的随机数组,则rand()方法根本不起作用。

假设您想生成一个1000个数字的数组。在最好的情况下,假设您生成了前999个没有重复的数字,最后一个想法是生成最后一个数字得到这个数字的概率是1/1000所以生成这个数字几乎需要很长时间。在实践中,只有10个数字会带来很大的麻烦。

最好的方法是通过递增(或严格单调序列)生成所有数字,对它们进行洗牌。在这种情况下,将有无重复

下面是一个如何使用10个数字的例子。即使有1000个数字,它也在起作用。

注:从Jhon Leehey的回答来看,函数足够了。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void shuffle(int *arr, size_t n)
{
if (n > 1) 
{
size_t i;
srand(time(NULL));
for (i = 0; i < n - 1; i++) 
{
size_t j = i + rand() / (RAND_MAX / (n - i) + 1);
int t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
}
int main()
{
int i;
int arr[10];
for (i=0; i<10; i++){
arr[i] = i;
}
shuffle(arr, 10);
for (i=0; i<10; i++){
printf("%d ", arr[i]);
}
}

有两种解决方案可供选择:

  1. 使用rand()之类的东西生成随机数并检查重复项。

  2. 找到一个严格单调(最好是严格递增)的数学序列,并将其项作为数组的成员。然后,您可以洗牌。结果不会是真正随机的,但使用rand()也不会。rand()使用了类似的tehnique,这就是为什么我们需要用一些变化的东西来设定种子,比如时间。例如,您可以使用时间来生成序列的第一个元素,使用一个好的序列,您的结果至少会不错。请注意,序列必须是严格单调的,以避免产生重复。顺序不必太复杂。例如,如果您将unix时间模10000作为第一个项,然后使用类似x[i]=x[i-1]+3*x[i-2]的递归生成其他项,应该是可以的。当然,你也可以使用更复杂的序列,但要小心溢出(因为你不能对结果应用模运算符,因为它不会再增加了)和你想要的位数。

srand(time(NULL));
const int N = 4;
int numbers [N];
bool isAlreadyAdded(int value, int index)
{
for( int i = 0; i < index; i ++)
if( numbers[i] == value)
return true;
return false;
}
for (int x=0; x!=N;x++)
{
int tmp = 1 + (rand() % N) ;
while( x !=0 && isAlreadyAdded(tmp, x))
tmp = 1 + (rand() % N) ;
numbers[x] = tmp;
printf("%d ", numbers[x]);
}

这只是一种方式。它应该起作用,当然还有更好的方法

这个怎么样:

#define NUMS (10)
int randomSequence[NUMS] = {0}, i = 0, randomNum; 
bool numExists[NUMS] = {false};
while(i != NUMS)
{
randomNum = rand() % NUMS;
if(numExists[randomNum] == false)
{
randomSequence[i++] = randomNum;
numExists[randomNum] = true;
}
}

当然,NUMS越大,执行while循环所需的时间就越长。

在c++中,您只需要:

std::random_shuffle()

http://www.cplusplus.com/reference/algorithm/random_shuffle/

int numbers [4];
for (int x=0; x!=4;x++)
{
numbers[x] = x;
}
std::random_shuffle(numbers, numbers +4);

更新:好吧,我一直在想一个合适的映射函数可以从每个索引变成一个随机数,但再想想,我意识到这可能很难。以下内容应该有效:

int size = 10;
int range = 100;
std::set<int> sample;
while(sample.size() != size)
sample.insert(rand() % range); // Or whatever random source.
std::vector<int> result(sample.begin(), sample.end());
std::random_shuffle ( result.begin(), result.end() );

您可以使用自己的随机数生成器,该生成器的序列大于或等于数组的长度。提到http://en.wikipedia.org/wiki/Linear_congruential_generator#Period_length以获取说明。

所以您需要表达式Xn+1=(aXn+c)mod m的LCG。值m必须至少和数组的长度一样大。检查最大序列长度的"如果且仅当"条件,并确保您的数字满足这些条件。

因此,您将能够为大多数用途生成具有令人满意随机性的随机数,这保证了在前m个调用中不会重复任何数字。

生成每个随机数后,循环使用前面的值并进行比较。如果匹配,请重新生成一个新值,然后重试。

如果你想在不维护访问索引的情况下伪随机遍历大空间,你应该看看我几年前参与的这个项目,了解基本技术。http://packetfactory.openwall.net/projects/ipspace/index.html

您应该能够根据自己的目的对其进行调整,源代码位于页面底部。