【问题标题】:srand doesn't help me creating random numberssrand 不能帮助我创建随机数
【发布时间】:2015-12-12 14:39:47
【问题描述】:

我正在尝试洗牌。我随机创建两个数字并将它们的值分配给彼此 100 次。但它不会随机创建。最奇怪的是,我在 "counter++;" 行设置了一个断点。观察程序是否真的每次都选择不同的卡片。当我调试时,如果我点击继续按钮太快,它每次都会创建相同的 index1 和相同的 index2(但 index1 不等于 index2)。我想这可能是由于时间。 (当我在继续按钮上单击太快或根本没有放置断点时,由于相同的时间,它会创建相同的数字。) 但如果是这样的话,为什么程序会为 index1 和 index2 创建不同的值呢?他们的行之间没有断点。所以时间一定很短。它虽然创建了不同的索引。我很困惑。如何摆脱?

编辑:解决方案是主要使用 srand 一次。

void mix(string *a){
        srand(time(NULL));
        if (finisher == 100){
            return;
        }

        string b;
        int pos1;
        pos1 = rand() % 52;
        b = a[pos1];
        int pos2;
        pos2 = rand() % 52;
        cout << a[pos1] << "  " << a[pos2] << endl; //These lines are only for testing 
        a[pos1] = a[pos2]; 
        a[pos2] = b; 
        cout << a[pos1] << "  " << a[pos2] << endl; //These lines are only for testing 
        counter++;
        srand(time(NULL));
        mix(a);

    }

【问题讨论】:

  • 还请记住,srand 不会生成真正的随机数,因为这在计算机上是不可能的,从长远来看,预计会得到比高数字更多的低数字。

标签: c++ random srand


【解决方案1】:

如果您使用srand 设置特定种子,rand 将生成特定的伪随机整数序列
来自man rand

srand() 函数将其参数设置为新序列的种子 rand() 返回的伪随机整数。 这些序列 可以通过调用 srand() 使用相同的种子值来重复。

time(NULL)每秒只会改变一次,所以你会遇到这样的行为。
此外,正如某些 cmets 中提到的@LightnessRacesinOrbit,您应该调用srand 一次

从 C++11 开始,您拥有 &lt;random&gt; 标头。 Use that instead.

【讨论】:

    【解决方案2】:

    根据http://en.cppreference.com/w/cpp/numeric/random/srand

    注意事项:

    一般来说,伪随机数生成器应该只播种一次,在调用 rand() 和程序开始之前。不应重复播种,或者每次您希望生成一批新的伪随机数时都重新播种。

    标准做法是将调用 time(0) 的结果用作种子。但是 time() 返回一个 time_t 值,time_t 不保证是整数类型。但实际上,每个主要实现都将 time_t 定义为整数类型,这也是 POSIX 所要求的。

    顺便说一下,为什么不使用C++11随机数生成器,它会产生随机整数值i,均匀分布在闭区间[a,b]上,即,根据离散概率函数分布。见http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution

    提供的示例代码是该 cppreference 页面。

    #include <random>
    #include <iostream>
     
    int main()
    {
        std::random_device rd;
        std::mt19937 gen(rd());
        std::uniform_int_distribution<> dis(0, 51);
     
        for (int n=0; n<10; ++n)
            std::cout << dis(gen) << ' ';
        std::cout << '\n';
    }
    

    【讨论】:

      【解决方案3】:

      您可能在调用 srand 时遇到问题。 您的程序在一秒钟内多次运行 shuffle_cards 函数。 这意味着您在srand(time(NULL)); 中提供给 srand 的时间戳在不止一次调用中是相同的。

      具有相同种子的两个不同初始化将在随后对 rand 的调用中生成相同的连续结果。

      因此,两次 rand() 调用都会生成与第一次调用 shuffle_cards 函数时相同的 index1 和 index2 数字。

      解决方法是不调用 srand 或者只调用一次,例如在程序启动时,或者减少调用它的频率。

      【讨论】:

      • 应该调用 srand 但是,是的,只调用一次。
      【解决方案4】:

      正如其他人告诉你的那样,部分问题在于所有那些对srand 的调用。只调用一次。

      这不是你问的,但代码不是洗牌的好方法。首先,shuffle 应该写成循环,而不是调用自己:

      void shuffle(string* a) {
          for (int i = 0; i < 100; ++i) {
              int index1 = std::rand() % 52;
              int index2 = std::rand() % 52;
              string temp = a[index1];
              a[index1] = a[index2];
              a[index2] = temp;
          }
      }
      

      请注意,此代码使用初始化,而不是定义未初始化的变量然后为其赋值。另外,我重新排列了顺序,以便更清楚地了解发生了什么。

      但这仍然不是洗牌的好方法;它通常会使大部分甲板保持不变。这是一个很好理解的问题。阅读Fisher-Yates shuffle

      void shuffle(string* a) {
          for (int i = 0; i < 51; ++i) {
              int idx = std::rand() % (52 - i) + i;
              string temp = a[idx];
              a[idx] = a[i];
              a[i] = temp;
          }
      }
      

      这可以通过使用标准库的swap 函数来改进:

      void shuffle(string* a) {
          for (int i = 0; i < 51; ++i) {
              int idx = std::rand() % (52 - i) + i;
              std::swap(a[i], a[idx];
          }
      }
      

      而且,除非您将此作为学习练习,否则您可以摆脱所有这些代码:

      void shuffle(string* a) {
          std::random_shuffle(a, a + 52);
      }
      

      请注意,我假设这里的 stringchar 的一些时髦的 typedef 或类似字符的东西,而不是 std::string,对于这些代码都无法正常工作。

      【讨论】:

        猜你喜欢
        • 2021-07-12
        • 1970-01-01
        • 2011-02-28
        • 1970-01-01
        • 1970-01-01
        • 2012-09-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多