如何使随机数独一无二

How to make Random Numbers unique

本文关键字:独一无二 随机数 何使      更新时间:2023-10-16

我正在制作一个随机数生成器。它询问用户希望在数字中包含多少位数字。例如,如果他们输入2,它将生成10到99之间的随机数。我制作了生成器,但我的问题是数字不是唯一的。

这是我的密码。我不知道为什么它不生成唯一的数字。我以为srand(时间(空))能做到。

void TargetGen::randomNumberGen()
{
srand (time(NULL));
if (intLength == 1)
{
    for (int i = 0; i< intQuantity; i++)
    {
        int min = 1;
        int max = 9;
        int number1 = rand();
        if (intQuantity > max)
        {
            intQuantity = max;
        }
        cout << number1 % max + min << "t";
    }
}
else if (intLength == 2)
{
    for (int i = 0; i<intQuantity; i++)
    {
        int min = 10;
        int max = 90;
        int number1 = rand();
        if (intQuantity > max)
        {
            intQuantity = max;
        }
        cout << number1 % max + min << "t";

    }
}
if (intLength == 3)
{
    for (int i = 0; i<intQuantity; i++)
    {
        int min = 100;
        int max = 900;
        int number1 = rand();
        if (intQuantity > max)
        {
            intQuantity = max;
        }
        cout << number1 % max + min << "t";
    }
}
else if (intLength == 4)
{
    for (int i = 0; i<intQuantity; i++)
    {
        int min = 1000;
        int max = 9000;
        int number1 = rand();
        if (intQuantity > max)
        {
            intQuantity = max;
        }
        cout << number1 % max + min << "t";
    }
}
if (intLength == 5)
{
    for (int i = 0; i<intQuantity; i++)
    {
        int min = 10000;
        int max = 90000;
        int number1 = rand();
        if (intQuantity > max)
        {
            intQuantity = max;
        }
        cout << number1 % max + min << "t";
    }
}
else if (intLength == 6)
{
    for (int i = 0; i<intQuantity; i++)
    {
        int min = 100000;
        int max = 900000;
        int number1 = rand();
        if (intQuantity > max)
        {
            intQuantity = max;
        }
        cout << number1 % max + min << "t";

    }
}
if (intLength == 7)
{
    for (int i = 0; i<intQuantity; i++)
    {
        int min = 1000000;
        int max = 9000000;
        int number1 = rand();
        if (intQuantity > max)
        {
            intQuantity = max;
        }
        cout << number1 % max + min << "t";
    }
}
else if (intLength == 8)
{
    for (int i = 0; i <intQuantity; i++)
    {
        int min = 10000000;
        int max = 89999999;
        int number1 = rand();
        if (intQuantity > max)
        {
            intQuantity = max;
        }
        cout << number1 % max + min << "t";
    }
}
if (intLength == 9)
{
    for (int i = 0; i < intQuantity; i++)
    {
        int min = 100000000;
        int max = 900000000;
        int number1 = rand();
        if (intQuantity > max)
        {
            intQuantity = max;
        }
        cout << number1 % max + min << "t";
    }
}
}

好吧,我想我找到了一种不用数组的方法,但在我切换到fisher-yates方法之前,它不起作用。有人能告诉我为什么这不起作用吗?它本质上应该是将随机数放入变量numGen中。然后在变量b=到numgen中。只是为了保持numGen过去的值,所以当循环通过并生成另一个随机数时,它会将其与旧数字进行比较,如果不等于旧数字,它就会输出它。如果它等于旧数字而不是输出它,它会解增量i,这样它就可以在循环中运行,而不会完全跳过这个数字。然而,当我这样做的时候,是无限循环。我不知道为什么。

if(intLength==1){

    for (int i = 0; i< intQuantity; ++i)
    {
        int min = 1;
        int max = 9;
        int number1 = rand();
        int numGen = number1 % max + min;

        if (intQuantity > max)
        {
            intQuantity = max;
        }
        for (int k = 0; k < 1; k++)
        {
            cout << numGen << "t";
            int b = numGen;
        }
        int b = numGen;
        if (b != numGen )
        {
            cout << numGen << "t";
        }
        else
        {
            i--;
        }
    }
}

每个人都对随机数有着有趣的期望——显然,你希望随机数是唯一的!如果您使用任何好的随机数生成器,您的随机数将永远不会被保证是唯一的。

最明显的是,如果你想生成[1,2]范围内的随机数,并且你要生成两个数,你会(通常期望)以相同的概率得到以下四种可能性之一:

1,2
2,1
1,1
2,2

要求一个好的随机数生成器生成前两个,而不是后两个是没有意义的。

现在,花一点时间思考一下,如果你要求在同一范围内生成三个数字,会发生什么。。。1,2,然后呢??

因此,唯一性不是,也不会是随机数生成器的属性。

不过,你的具体问题可能需要独特性。在这种情况下,您需要做一些额外的工作来确保唯一性。

一种方法是保留一个已经选择了数字的选项卡。你可以把它们放在一套里,如果你得到了之前得到的,可以重新挑选。然而,只有当你选择了一小组与你的范围相比较的数字时,这才是有效的;如果你选择了大部分的范围,那么这个过程的结束是无效的。

如果你要挑选的数字计数与范围的大部分相对应,那么使用范围的数组,并使用良好的洗牌算法来洗牌是更好的解决方案。(费雪-耶茨洗牌应该能起作用。)

提示0:使用数论中的二次残差;一个整数q称为模p的二次残差,如果它与模p,使得:

x2lect q(mod p)

提示1:定理:假设p是素数,只要2x<p.例如:

02选择0(mod 13)

12Select1(mod 13)

22Select4(mod 13)

32Select9(mod 13)

42Select3(mod 13)

52lect 12(mod 13)

62Select10(mod 13)

提示2:定理:假设p是素数,使得p≠3(mod 4),不仅x2%p(即二次残差)对于2x<p,但是p-x2%p对于2x>p也是唯一的。例如:

02%11=0

12%11=1

22%11=4

32%11=9

42%11=5

52%11=3

11-62%11=8

11-72%11=6

11-82%11=2

11-92%11=7

11-102%11=10

因此,这个方法为我们提供了一个在小于p的整数上的完美的1对1置换,其中p可以是任何素数,使得p≠3(mod 4)。

提示3:

unsigned int UniqueRandomMapping(unsigned int x)
{
    const unsigned int p = 11; //any prime number satisfying p ≡ 3 (mod 4)
    unsigned int r = ((unsigned long long) x * x) % p;
    if (x <= p / 2) return r;
    else return p - r;
}

我不担心输入数字不正确(例如超出范围)。

备注

  • 对于32位整数,您可以选择最大素数,使得p elec 3(mod 4)小于232,即4294967291
  • 尽管这种方法为生成随机数提供了一个1对1的映射,但它也存在集群问题
  • 为了提高上述方法的随机性,请将其与其他独特的随机映射方法,如XOR算子

我假设您可以想出一种方法来计算要使用的数字数量。这很简单,因为用户输入2等于10-99,3等于100-999,等等

如果你想自己实现唯一的、随机生成的数字,请查看这些链接。

http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

下面是一个非常相似的实现:https://stackoverflow.com/a/196065/2142219

本质上,您正在创建一个X整数的数组,所有这些整数都设置为其索引的值。您可以随机选择一个介于0和MAX之间的索引,取该索引处的值并将其与最大值进行交换。MAX随后递减1,您可以通过随机选择0和MAX-1之间的索引来重复此操作。

这会给您一个0-999整数的随机数组,没有重复项。

以下是在一个范围内生成唯一随机数的两种可能方法。

  1. 跟踪已经使用std::set生成的数字,只要它们已经在集合中,就丢弃并重新生成数字。如果您想生成大量随机数,则不建议使用这种方法,因为存在生日悖论
  2. 生成给定范围内的所有数字,对它们进行随机排列,然后输出用户想要的任意数量

标准随机生成器永远不会生成唯一的数字,在这种情况下,它们将不独立。

要生成唯一的数字,您必须:

  • 保存所有生成的数字,并将新数字与旧数字进行比较,如果有巧合,则重新生成

  • 使用random_shuffle函数:http://en.cppreference.com/w/cpp/algorithm/random_shuffle提前获取所有序列

首先,srand()/rand()通常具有2^32的周期,这意味着在调用srand()之后,rand()将在对rand()的前2^32次调用期间内部迭代不同的整数。尽管如此,rand()很可能返回一个小于32位的结果:例如0RAND_MAX之间的int,其中RAND_MAX是2^31-1或2^15-1,因此作为rand()的调用者,您可能会看到重复的结果。不过,你可能读到了关于这一时期的文章,或者有人在意识到这一点的情况下发表的评论,不知何故,它被误认为是独一无二的。。。。

其次,对rand()的任何调用都会生成一个比您想要的大得多的数字,而您正在这样做。。。

number1 % max

"number1 % max"的结果在0 <= N <= max的范围内,但随机数本身可能是大于max的任意倍数。换句话说,两个不同的随机数相差max的倍数,在您的程序中仍然会为number1 % max产生相同的结果。


要获得一个范围内不同的随机数,可以用所有的数字预填充std::vector,然后再填充std::shuffle