【问题标题】:How can I do "weighted" random number generation that favors some numbers over others, depending on user input?如何根据用户输入进行“加权”随机数生成,使某些数字优于其他数字?
【发布时间】:2019-11-05 11:48:38
【问题描述】:

首先,我相信这也是基于数学的,所以 mods,如果这不是解决此类问题的正确位置,请随时将其迁移到适当的位置。我在这里问,因为我希望在编程中实现这个问题,特别是在 C++ 中。

假设以下场景: - 我想RNG一个0到8之间的数字 - 有一个额外的输入考虑到正在生成的数字,以这种方式为某些数字提供更好的赔率(我会尽量清楚):

如果用户输入'0',则要生成的数字有以下机会出现:

'0' - weight of 5,
'1' - weight of 4,
'2' - weight of 3,
'3' - weight of 2,
'4' - weight of 1,
'5' - weight of 0,
'6' - weight of 0,
'7' - weight of 0,
'8' - weight of 0,

所以得到随机“0”的几率是得到“4”的5倍,得到“5”、“6”、“7”和“8”的几率为零。

如果用户输入“4”,则权重如下:

'0' - weight of 1,
'1' - weight of 2,
'2' - weight of 3,
'3' - weight of 4,
'4' - weight of 5,
'5' - weight of 4,
'6' - weight of 3,
'7' - weight of 2,
'8' - weight of 1

在这种情况下,所有数字都有机会出现,但有利于生成数字“4”。 此外,数字“3”和“5”出现的机会相同,但概率低于数字“4”。

等等。

这可以翻译成如下图所示的权重表:

Table of Weights image

考虑到我已经把问题说清楚了(如果没有,请告诉我!我会尝试用更好的方式解释),如何设计解决这个问题的方法?

我标记 c++ 是因为这是我正在学习的语言,但这通常可以应用于任何语言。

最好的问候!

【问题讨论】:

    标签: c++ math random probability weighted


    【解决方案1】:

    按照@Evg 的建议使用std::discrete_distribution<>

    我做了这个完全符合我要求的示例代码!

    没有 random_device 因为我在 Android 上编码...

    #include <iostream>
    #include <vector>
    #include <chrono>
    #include <random>
    
    int main()
    {
        std::vector<int> v {};
        std::cout << "Input vector size:\n";
        size_t vSize {0};
        std::cin >> vSize;
        v.resize(vSize);
        std::cout << "input max weight:\n";
        int maxWeight {0};
        std::cin >> maxWeight;
        std::cout << "Input the vector position for max weight:\n";
        int pos {0};
        std::cin >> pos;
        std::cout << "filling vector with weights...\n";
        for (int i {pos - 1}, j {0}; i < static_cast<int>(v.size()); ++i, ++j)
        {
            v[static_cast<size_t>(i)] = maxWeight - j > 0 ? maxWeight - j : 0;
        }
        for (int i {pos - 1}, j {0}; i >= 0; --i, ++j)
        {
            v[static_cast<size_t>(i)] = maxWeight - j > 0 ? maxWeight - j : 0;
        }
        std::cout << "Done.\n\nPrinting the array weight map:\n";
        for (int element : v)
        {
            std::cout << element << '\n';
        }
        using chrono_t = std::chrono::high_resolution_clock;
        const auto seed {chrono_t::now().time_since_epoch().count()};
        std::mt19937 mt {static_cast<unsigned long int>(seed)};
        std::discrete_distribution<> rng(v.begin(), v.end());
        std::cout << "- - - Generating 1 000 000 numbers: - - -\n";
        std::vector<int> numCount {};
        numCount.resize(vSize, 0);
        for (int i {0}; i < 1000000; ++i)
        {
            ++numCount[static_cast<size_t>(rng(mt))];
        }
        {//scope
            int i {0};
            for (auto element : numCount)
            {
                std::cout << "Number of '" << i << "'s: " << element << '\n';
                ++i;
            }
        }//scope
        std::cout << '\n';
        return 0;
    }
    

    这是一个与我在问题中添加的表格相匹配的示例运行:

    Input vector size:
    9
    input max weight:
    5
    Input the vector position for max weight:
    5
    filling vector with weights...
    Done. 
    Printing the array weight map:
    1
    2
    3
    4
    5
    4
    3
    2
    1
    - - - Generating 1 000 000 numbers: - - -
    Number of '0's: 40032
    Number of '1's: 79854
    Number of '2's: 120201
    Number of '3's: 160531
    Number of '4's: 200083
    Number of '5's: 159642
    Number of '6's: 119873
    Number of '7's: 80057
    Number of '8's: 39727
    

    确实很简单,但是当您只是在学习时,事情可能看起来比实际情况要复杂。

    问候!

    【讨论】:

    • 您可以使用std::absstd::max 将两个for 循环合并为一个:for (auto i = std::max(0, pos - maxWeight); i &lt; std::min((int)v.size(), pos + maxWeight); ++i) v[i] = maxWeight - std::abs(i - pos); 用于从零开始的pos
    • 哦,这很有用!只是好奇,你是因为懒惰而使用 c 风格转换还是有其他原因?
    • 这里只是为了演示这个想法。 static_cast 绝对是更好的选择。
    【解决方案2】:

    我的方法是

    根据用户输入添加所有权重,在第一个示例中为 5+4+3+2+1+0+0+0 (sum=15),即 1+2+3+4+5+4+ 3+2+1 (sum=25) 在第 2 次。

    在区间[0,sum-1]中选择一个随机整数

    根据您的表格进行映射,例如0-4 => 0、5-8 => 1 等等

    【讨论】:

    • 这也是一个非常好的方法。我必须提高我的数学技能。我也会尝试这种方式并发布一些结果。
    【解决方案3】:

    看起来你想要std::discrete_distribution

    std::discrete_distribution 在区间[0, n) 上产生随机整数,其中每个整数i 的概率定义为w_i = i/S,即ith 整数的权重除以所有整数的总和n 权重。 [cppreference.com]

    如果你的表是std::vector&lt;unsigned int&gt; weights,你可以写

    std::random_device rd;
    std::mt19937 gen(rd());
    std::discrete_distribution<> d(weights.begin(), weights.end());
    

    然后使用d(gen)获取随机数。

    如果weights 数组在编译时具有已知的静态大小,则可以使用完全相同的语法使用std::array

    【讨论】:

    • 天哪!让我试试!!我会留下一些反馈并发布一些测试结果。
    • 我在自我回答中发布了一些结果。感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 2018-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-09
    • 1970-01-01
    相关资源
    最近更新 更多