【问题标题】:Why is this random generator always output the same number [duplicate]为什么这个随机发生器总是输出相同的数字[重复]
【发布时间】:2020-02-22 08:55:43
【问题描述】:

我不明白为什么它每次都输出相同的数字,即使它每次都应该输出不同的数字。 rand() % 10 + 1,应该随机输出 1 到 10 之间的数字,但我的代码每次只输出相同的 5 个数字。

#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
    int v1;
    for(v1=0; v1 < 10; v1++) {
        v1 = rand() % 10 + 1;
        cout << v1 << endl;
    }
    return 0;
}

【问题讨论】:

  • 要让rand()(一个糟糕的 PRNG)工作,你必须给它一个种子。
  • 了解rand() 的工作原理(特别是用户代码未设置默认种子时的默认种子)。当您使用它时,请阅读有关现代 C++ &lt;random&gt; 库的信息,它在几乎所有可能的方式上都优于 rand()。也就是说,您使用v1 作为both 你的循环控制 你的“随机”值的持有者有什么特别的原因吗?你认为这会如何影响循环??
  • 你需要播种,就像 Chipster 所说的那样。如果不播种,您将继续收到相同的数字。
  • 也许我的链接很接近骗子。另外,也许有代表的人可以保护。还有一个选项是我们中的一个人写一个答案。
  • @Chipster 不要停在那里。我什至不确定真正的 problem 是什么(不是代码错误,而是这段代码试图解决的实际问题)。显然缺少一些播种,但是对于 rand-result 循环控制,是否可以重用 v1?问题描述表明要寻找 5 个数字,但在循环控制中绝对没有任何东西可以确保这一点。我真的很好奇这段代码真的应该做什么。

标签: c++


【解决方案1】:

您的代码的预期目标充其量只是粗略的,并且您的问题描述对它没有任何帮助。代码本身可以采用多种方式。我认为您正在尝试从均匀分布中抽取十个随机数。这样做的现代方法是这样的:

简单均匀分布

#include <iostream>
#include <random>

int main()
{
    std::mt19937 prng{ std::random_device{}() };
    std::uniform_int_distribution<> dist(1, 10);

    for (int i = 0; i < 10; ++i)
        std::cout << dist(prng) << ' ';
    std::cout.put('\n');
}

注意 mersenne-twister prng std::mt19937 的播种是通过使用 std::random_device 的某个熵源的库接口完成的。然后,该 prng 用于提供 std::uniform_int_distribution 的重要机制,该机制仅限于域 {1..10} 包括在内。请注意,这不会防止重复。下面显示了一些示例运行(您尝试时最好有所不同):

输出(可变)

7 8 3 4 4 5 8 5 5 3

选择不替换

如果您有兴趣在不重复的情况下绘制随机选择,有很多方法可以做到这一点,但对于像这样的小型域,我更喜欢的一种方法是简单地对数字进行排序,随机打乱它们,然后迭代画出你的随机顺序。同样,我们将使用一个正确播种的 mersenne-twister prng,但这次我们将使用它作为库提供的随机序列的随机源,该序列我们填充了我们非常有限的域来绘制:

#include <iostream>
#include <random>
#include <vector>
#include <numeric>
#include <algorithm>

int main()
{
    std::mt19937 rng{ std::random_device{}() };
    std::vector<int> vec(10);

    std::iota(vec.begin(), vec.end(), 1);
    std::shuffle(vec.begin(), vec.end(), rng);

    for (auto x : vec)
        std::cout << x << ' ';
    std::cout.put('\n');
}

输出(可变)

6 7 10 2 4 8 3 1 5 9

上面用到的值得注意的函数:

  • std::iota 是一种简单的升序数字序列生成算法
  • std::shuffle 是一种随机 shuffle 算法,利用您提供的 RNG 源来执行 shuffle 操作。

您的代码

上述两种方法都比您似乎想要做的更好。 Bias introduced with modulo against rand() 可能是一个真正的问题,只有您确定它是否真的很重要。如果您想防范它,请使用我之前展示的方法之一(无论哪种方法适合您的需要)。

也就是说,您的代码可能只是缺少正确的种子。根据库标准,在查看 srand 之前对 rand() 的任何调用都将表现得就像使用了 srand(1) 一样。这解释了您从跑步到跑步的重复结果。假设您将v1 用作控制循环变量来自rand 模计算的基准持有者本身就是一个错误,解决这两个问题如下所示:

#include <iostream>
#include <cstdlib>
#include <ctime>

int main()
{
    std::srand(static_cast<unsigned>(std::time(nullptr)));

    for (int i = 0; i < 10; ++i)
        std::cout << 1 + std::rand() % 10 << ' ';
    std::cout.put('\n');
}

输出(可变)

9 1 10 6 8 7 5 9 4 10

也就是说,我告诫不要这样做。有很多事情你可以犯错误,偏见总是在后台等待咬人。根据需要使用其他方法之一

【讨论】:

  • 我刚刚意识到我在很多年前就谈到了播种的话题。如果您不介意我捎带:What is the proper way of seeding std::mt19937 with std::chrono::high_resolution_clock inside a class? 如果您不同意,请删除它。
  • @WhozCraig 感谢您的意见,我对 c++ 和一般编码仍然很陌生,所以我特别不知道该做什么和不该做什么,因为有很多方法可以解决一个问题。还有太多我目前不知道的库,所以我仅限于我知道的和我觉得更容易理解的库。但我会通读这个并尝试理解其中的区别。
【解决方案2】:

你需要使用这个

#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;
int main()
{
    srand(time(nullptr));
    int v1;
    for(v1=0; v1 < 10; v1++) {
        v1 = rand() % 10 + 1;
        cout << v1 << endl;
    }
    return 0;
}

【讨论】:

  • 这是一种糟糕的做法! - 没有人需要它!
  • srand() 只是一种悲伤,悲伤的方式接近它,我猜:D
  • 有人可以发布一个生成随机数的首选方法,而不是抨击这个完全合适的答案吗?
  • @JimmyG 有一个相当大的第一章谷歌了:en.cppreference.com/w/cpp/header/random
猜你喜欢
  • 2019-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-25
  • 2011-05-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多