可能基于数百万骰子面的随机数生成器

时间:2018-12-05 19:22:54

标签: c++

我正在尝试创建一个旨在确定视频游戏角色可以控制多少个元素的程序。在这种情况下,一般人群可以控制至少1个元素。但是我希望每千个人中有1个人拥有2个元素可以控制,每10,000个人中有1个人可以控制3个元素,每100,000个人中有1个人可以控制4个元素。

该程序首先询问人口中有多少人。这将等于骰子有多少面。然后,它循环遍历,生成随机数,然后输出有多少人可以控制多少个元素。

我遇到了一个与我的数据类型有关的问题。即使我输入了1百万的人口,循环也迭代了100万次,但从未将数字滚动到5位以上。因此从技术上讲,超过99,999的人口永远不会拥有3或4个元素。它似乎并没有超过50k ish,并且我记得读过一种数据类型仅能处理65k ish的数字。那是原因吗?我应该使用哪种数据类型来滚动0到数百万之间的随机数?

#include <iostream>
#include <ctime>
#include <iomanip>

using namespace std;


int main()
{
    int min = 1, max, sides, roll;
    double oneCounter = 0.0, twoCounter = 0.0, threeCounter = 0.0, fourCounter = 0.0;

    unsigned long seed = time(0);
    srand(seed);
    cout << "How many characters would you like to generate?: ";
    cin >> sides;
    max = sides;

    cout << "\n";

    for (int i = 0; i < sides; i++)
    {
        roll = rand() % (max - min + 1) + min;

        if (roll % 100000 == 0)
        {
            cout << roll << ": You will have 4 elements" <<endl;
            fourCounter++;
        }
        else if (roll % 10000 == 0)
        {
            cout << roll << ": You will have 3 elements" <<endl;
            threeCounter++;
        }
        else if (roll % 1000 == 0)
        {
            cout << roll << ": You will have 2 elements" <<endl;
            twoCounter++;
        }
        else
        {
            cout << roll << ": You will have 1 element." <<endl;
            oneCounter++;
        }
    }

    cout << left;
    cout << "\n==============================" <<endl;
    cout <<setw(11) << "Potential"
         << setw(10) << "QTY"
         << setw(10) << "Pct"
         << endl;
    cout << "==============================" <<endl;
    cout << setw(11) << "Single: " 
         << setw(10) << oneCounter
         << setw(10) << (oneCounter/sides)*100 <<endl;

    cout << setw(11) << "Double: " 
         << setw(10) << twoCounter 
         << setw(10) << (twoCounter/sides)*100 <<endl;

    cout << setw(11) << "Triple: " 
         << setw(10) << threeCounter 
         << setw(10) << (threeCounter/sides)*100 <<endl;

    cout << setw(11) << "Quadruple: " 
         << setw(10) << fourCounter 
         << setw(10) << (fourCounter/sides)*100 <<endl;


    cout << "\n\n";
    return 0;
}

我为10万人(侧面)提供了结果的屏幕截图。概率看起来不错,但是数字永远不会超过5位数字。

enter image description here

2 个答案:

答案 0 :(得分:1)

此问题与变量的类型范围无关,因为int范围从–2,147,483,648到2,147,483,647(使用32位编译器,请参见下面的注释)。

此处的错误是rand()函数仅返回最大为stdlib.h文件中定义的最大数字,即32767。您应该使用标准的随机库标头来生成随机数。我建议看看这个答案,看看如何轻松地做到这一点,How to generate very Large random number in c++

编辑:请参阅下面的user4581301的注释。我假设使用的是整数类型范围,但是在这里长时间使用以确保您要获取的大量数字可以容纳在变量中可能是最安全的。

答案 1 :(得分:1)

rand来自黑暗时代,那时程序员不得不四处寻找以获取他们所能获得的一切。如果他们有一个8位CPU,他们就会庆祝。他们牺牲了16位长子。我是第二个孩子的好事,是吗?

不。开玩笑。我父亲是一名会计师。我大哥很好。

无论如何,rand可能不足以生成所需的数字。某些实现仍将输出的上限设置为15位,与您过去从整数获得的最佳正值匹配。 rand还必须在资源严重受限的计算机上快速运行,因此,它需要做出假设并要求使用flat-out suck的使用模式。

快进到今天,有时整数是64位,并且您的口袋里的计算能力可能比初次编写C时整个世界更大。我们可以做的更好。您可以使用std::uniform_int_distribution作为rand的替代品,它将生成所需大小的数字,但我们可以做得更好。

注意:在此我要做的就是修改the example code from std::discrete_distribution以使用不同的权重进行演示。

#include <iostream>
#include <map>
#include <random>

int main()
{
    std::random_device rd;
    std::mt19937 gen(rd());
    std::discrete_distribution<> d({ 1.0, 
                                     1.0 / 1000.0, 
                                     1.0 / 10000.0, 
                                     1.0 / 100000.0 });
    std::map<int, int> m;
    for (int n = 0; n<10000000; ++n) {
        ++m[d(gen)];
    }
    for (auto p : m) {
        std::cout << p.first + 1 << " generated " << p.second << " times\n";
    }
}

这将为您完成所有魔术,并按给定的重量按比例吐出1-4。实际上,Asker程序中用于选择可以控制的元素数量所需的所有代码都已消失。

这确实很接近,也许足够接近,但是如您所见,加起来的总和略大于1。如果您在100000中有1个事件,它将取代10000中的1、1000中的1和1中1的事件。我们想要更像

的东西
#include <iostream>
#include <map>
#include <random>

int main()
{
    std::random_device rd;
    std::mt19937 gen(rd());
    std::discrete_distribution<> d({ 100000 - 1000, 
                                     1000 - 100,
                                     100 - 1, 
                                     1});
    std::map<int, int> m;
    for (int n = 0; n<1000000; ++n) {
        ++m[d(gen)];
    }
    for (auto p : m) {
        std::cout << p.first + 1 << " generated " << p.second << " times\n";
    }
}

注意事项:

确保random_device为您的系统提供了有用的种子。 MinGW的random_device实现有一个烦人的习惯,总是返回0,这是一个完全有效的随机数,但是如果您一直都得到它(实际上是Randall Number Generator)仍然没用。