【问题标题】:C++ No-repeat random number generatorC++ 无重复随机数生成器
【发布时间】:2018-06-25 17:01:46
【问题描述】:

我需要在 C++ 中创建算法实现以生成随机数到 f.e 表而不重复和列表。

我创建了类似的代码,但是当我将 n=32769 放入控制台程序停止工作时,它就停止工作了。当我将数字放在 0-32768 范围内时,它就可以工作。知道这段代码有什么问题吗? 编译时我没有错误/警告。

#include <stdio.h>
#include <iostream>
#include <ctime>


int main()
{
    clock_t start = clock();
    int n;
    std::cout << "n:";
    std::cin >> n;
    bool *used_numbers = new bool[n];
    memset(used_numbers, false, sizeof(used_numbers[0]) * n);
    int *permutation = new int[n];
    srand(unsigned(std::time(NULL)));
     int rnd_number;



    for (int i = 0; i < n; i++)
    {
        rnd_number = rand() % n;
        if (!used_numbers[rnd_number])
        {
            permutation[i] = rnd_number;
            used_numbers[rnd_number] = true;
        }
        else
            i--;
    }
    std::cout << "Permutation: \n ";
    for (int k = 0; k < n; k++)
    {
        std::cout << permutation[k] << " ";
    }
    std::cout << std::endl;

    printf("[Debug]: %lu ms\n", clock() - start);




    getchar();
    system("pause");
    return 0;
}

【问题讨论】:

  • 停止工作是什么意思?它会崩溃吗?它输出错误的东西吗?是不是无限循环?
  • "编译时我没有错误/警告。" 如果程序编译,并不意味着它可以正常工作。
  • 打印时您机器上的RAND_MAX 的值是多少?
  • 我猜你正在使用的rand() 的实现会生成[0 .. 32767] 范围内的值,这并不罕见。生成 32768 个值将不可避免地创建至少一个副本。检查RAND_MAX的值。
  • 这不是直接的问题,但是这个算法还有很多不足之处。使用std::shuffle。或者,如果您想了解理论,请阅读Fisher-Yates shuffle

标签: c++ arrays algorithm random permutation


【解决方案1】:
rand() % n

永远不会给你一个大于 RAND_MAX 的数字。 RAND_MAX 是 rand() 生成的数字的范围。

如果您使用大于 RAND_MAX 的 n 值,您将在绘制第一个 RAND_MAX 数字后永远循环。简单地说,没有数字可画了。

您需要改进您的解决方案,以便能够生成更大的数字,或者使用更好的方法,例如随机排列更大的数字列表。

您的算法有很多问题,但立即简单的解决方法是:

rnd_number = (rand() * (RAND_MAX + 1) + rand()) % n;

【讨论】:

  • 这个替换创建了一个稍微倾斜的生成器。例如,如果最左边的 rand() 调用返回 0,而最右边的调用返回 RAND_MAX,您将获得 RAND_MAX 值。但是如果最左边的返回 1,最右边的返回 0,你也会得到 RAND_MAX。左边的乘数应该是(RAND_MAX + 1)。然而,赞成,因为它是一个有效的答案。
  • @PeteBecker 你是对的。但就 diy-random-schemes 的偏度而言,它比我见过的东西要温和得多。像 rand()*rand()。 :-)。仍然,已修复,谢谢。
  • rand()*rand()?哇。
  • 这不是一个有效的答案,因为rand的算法是未指定的,所以它可能会产生重复的结果等等。
  • @VTT 我们在这里不是在谈论加密安全。 OP 编写了一个算法并遇到了问题。 OP 最可能需要的是了解哪里出了问题。这个答案指出了问题所在和最小的修复。答案可能并不完美,但“无效”正在推动它。你的 LFSR 很有趣,但显然高于它对 OP 有价值的水平
【解决方案2】:

我只想说明以下问题的一些其他解决方案,它们可能会更快(或使用一些更短的代码)(在某些情况下)。

假设您需要集合 {0, 1, .., n - 1} 的所有元素,选择最后一个未选择元素(在最后一次迭代中)的概率等于 1/n,这是预期的值是 n 次迭代,只选择一个元素。

  1. vector<int> p;
    for(int i = 0; i < n; i++) p.push_back(i);
    random_shuffle(p.begin(), p.end();
    

然后你可以取向量 p 的前 K 个元素(或整个向量,如果你需要整个排列)。

  1. 在每次迭代中,您可以选择一个介于 1 和 numbers_left (N - numbers_already_chosen) 之间的随机数 i,然后取出尚未选择的第 i 个数字。它也可能效率低下,但使用一些二叉搜索 + 二叉索引树,你可以得到 O(Nlog^2N),甚至更好。

【讨论】:

    【解决方案3】:

    如果您需要生成不重复的非加密安全数字序列,那么您可以使用Linear-feedback shift register。示例生成 65535 个项目(来自 Wiki):

    #include <iostream>
    #include <set>
    #include <cassert>
    #include <cstdint>
    
    int main(void)
    {
        // Any nonzero start state will work.
        const ::std::uint16_t start_state{0xACE1u};
        ::std::uint16_t lfsr{start_state};
        ::std::uint64_t period{0};
    #ifndef NDEBUG
        ::std::set<::std::uint16_t> used_items{};
    #endif // #ifndef NDEBUG
        do
        {
            // Get LSB (i.e., the output bit).
            bool lsb{0 != (lfsr & 1)};
            // Shift register
            lfsr >>= 1;
            // If the output bit is 1, apply toggle mask.
            if(lsb)
            {
                lfsr ^= 0xB400u;
            }
            // verify that current value has never appeared before
            assert(used_items.emplace(lfsr).second);
            std::cout << lfsr << "\n";
            ++period;
        }
        while(lfsr != start_state);
        ::std::cout << "period " << period << "\n";
        ::std::cout.flush();
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-22
      • 2012-12-17
      • 2012-08-10
      • 2012-11-16
      • 1970-01-01
      • 1970-01-01
      • 2014-03-11
      相关资源
      最近更新 更多