【问题标题】:How to make Random Numbers unique如何使随机数唯一
【发布时间】:2014-04-13 23:33:45
【问题描述】:

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

这是我的代码。我不确定为什么它没有生成唯一编号。我认为 srand(time(null)) 会这样做。

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 以便它在循环中运行而不会完全跳过该数字。然而,当我这样做是无限循环。我不确定为什么。

如果 (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--;
        }

    }

}

【问题讨论】:

  • 你为什么认为这会产生唯一的数字?
  • 你想要多少个数字?例如,您不能在 10 到 99 之间获得 200 个唯一数字。
  • 我认为 srand(time(NULL)) 不会自然而然地重复它们。我现在在想我必须将它生成的整数输入一个数组然后比较它们。但我不确定我将如何实现它。
  • 你可能会更接近&lt;random&gt;,但不,他们不会每次都产生唯一的数字。
  • 我还从用户那里得到他们想要多少数字的输入。但我有一个检查器,可以查看用户输入的数字是否超过最大值,然后它将默认为该范围内的最大数字数量。

标签: c++


【解决方案1】:

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

为了使这一点最明显,如果您想生成范围 [1, 2] 内的随机数,并且要生成两个数字,您将(通常期望)以相等的概率获得以下四种可能性之一:

1、2
2、1
1, 1
2、2

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

现在,花点时间想一想,如果您要求在同一范围内生成 三个 数字...1、2,会发生什么??

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

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

一种方法是密切关注已选择的号码。您可以将它们放在一组中,如果您早先获得了一个,则可以重新选择。但是,这仅在您选择与您的范围相比的一小组数字时才有效;如果您选择了大部分范围,则该过程的结束将无效。

如果您要选择的数字计数对应于范围的大部分,那么使用范围的数组,并使用良好的洗牌算法来洗牌周围的数字是一个更好的解决方案。 (Fisher-Yates shuffle 应该可以解决问题。)

【讨论】:

    【解决方案2】:

    提示 0: 使用数论中的 Quadratic residue;如果整数 q 与模 p 完全平方一致,则称为模 p 的二次余数;即,如果存在整数 x 使得:

    x2 ≡ q (mod p)

    提示 1: 定理:假设 p 是一个素数,只要 2x

    02 ≡ 0 (mod 13)

    12 ≡ 1 (mod 13)

    22 ≡ 4 (mod 13)

    32 ≡ 9 (mod 13)

    42 ≡ 3 (mod 13)

    52 ≡ 12 (mod 13)

    62 ≡ 10 (mod 13)

    提示 2: 定理:假设 p 是一个素数,使得 p ≡ 3 (mod 4),不仅 x2%p(即二次余数)对于 2x 是唯一的2%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 ≡ 3 (mod 4) 小于 232,即 4294967291。
    • 尽管此方法为您提供了用于生成随机数的 1 对 1 映射,但它存在聚类问题。
    • 为了提高上述方法的随机性,将其与 其他独特的随机映射方法,例如 XOR 运算符。

    【讨论】:

      【解决方案3】:

      我假设您可以想出一种方法来确定要使用多少个数字。这很简单,因为用户输入 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 整数的随机数组,没有重复。

      【讨论】:

        【解决方案4】:

        这里有两种可能的方法来生成一个范围内的唯一随机数。

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

        【讨论】:

        • 效率快速下降:生日悖论
        • @Deduplicator,同意,但这取决于他想要生成多少。这就是我建议两者的原因,但我会更新以使其更加明确。
        【解决方案5】:

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

        要生成唯一编号,您必须:

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

        【讨论】:

          【解决方案6】:

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

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

          number1 % max
          

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


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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-02-05
            • 2013-09-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-11-20
            • 1970-01-01
            • 2013-03-19
            相关资源
            最近更新 更多