【问题标题】:Biased Random Number Generator有偏随机数发生器
【发布时间】:2011-02-16 01:30:56
【问题描述】:

我正在寻找一个可以有偏差的随机数生成器。例如,假设我想要一个 1-5 之间的随机数,概率为:

1:出现 20% 的时间
2:出现 10% 的时间
3:出现 40% 的时间
4:出现 25% 的时间
5:出现 5% 的时间

标准库或其他库中有什么可以做到这一点吗?或者,有没有一种有效的方法可以自己做到这一点?

【问题讨论】:

  • 我希望你不是为赌场写软件!
  • 哈哈不,我敢肯定赌场会雇用更聪明一点的人。
  • 从昨天开始:stackoverflow.com/questions/2772882/… 这是同一问题的早期版本的重复(我懒得找到)。您在搜索中可能遗漏的词是“离散”,这很重要,因为以下许多答案更适用于连续分布。
  • 复制并粘贴 c# 的测试代码 ... stackoverflow.com/a/33991225/294884 ... 可以轻松处理任何数组

标签: c++ random


【解决方案1】:

对于您的问题,只需从该列表中统一选择一个随机元素即可:

[1, 1, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5]

一般来说,检查这个答案:Weighted random numbers


在 TR1 和 C++0x 中,<random> header 包含用于生成此类数字等的 discrete_distribution class

您可能还想查看 GSL,它包含比标准 <random> 库更多的 random distributions(和随机数生成器)。 (但请注意,GSL 使用 GPLv3。)

【讨论】:

  • 也许我应该更好地解释一下我的实际情况。我真正需要的是 1-50,000ish 之间的随机数。创建一个冗长的列表似乎没有必要,也不需要。很抱歉造成混乱。
  • 这就是为什么好的工程需要一组适当的要求。你问了简单的案例,你得到了(正确的)简单的答案。
【解决方案2】:

最好的方法可能是只采用正常的无偏随机生成器,然后根据其值所在的区间返回。

只是一个 if 语句,它给出 1 表示 0:0.2、2 表示 0.2:0.3、3 表示 0.3:0.7、4 表示 0.7:0.95 和 5 表示 0.95:1。最好将区间的下限或上限设为包含,另一个不包含。

int biasedRandom(){
double i = randomNumber();
if(i<= 0.2){return 1;}
else if(i <= 0.3){return 2;}
else if(i <= 0.7){return 3;}
else if(i <= 0.95){return 4;}
else{return 5;}
}

类似的东西。

【讨论】:

  • 如果你有很多间隔要检查,你应该做的是想出一个累积分布数组(不知道怎么称呼它)并每次都对其进行二进制搜索找到生成的数字。
  • 似乎这个生成器总是会返回 5。除非 randomNumber() 返回值 0-1,否则需要双精度/浮点数和其他一些数学运算。
  • 是的,你是对的,它每次都会给出 5,并不是要让 i 成为 int,这是一个错误。感谢您指出。
【解决方案3】:

Boost 随机数库提供了为生成器指定不同形状分布的能力。这是一个很棒的图书馆 - 请参阅 http://www.boost.org/doc/libs/1_42_0/libs/random/index.html

【讨论】:

  • 是的,不要重新发明轮子!
  • 有一天我要开始使用 Boost。总有一天。
  • 它只是缺少适当的文档......如果知道引擎和分发所需的概念是什么,以便能够在不对库进行逆向工程的情况下自己制作:(
  • 库很好,但我认为可以肯定地说他是一个相当新的程序员,在这种情况下,它们更像是一个障碍,因为你首先需要经验才能习惯逻辑。
  • @AaronM 编写自己的随机数生成器充满了问题 - 使用现有的、经过测试的生成器是更好的选择。
【解决方案4】:

这次聚会迟到了。这是 C++0x 的答案:

#include <iostream>
#include <random>
#include <iterator>

int main()
{
    // Set up distribution
    double interval[] = {1,   2,   3,   4,   5,   6};
    double weights[] = {  .2,   .1,  .4,  .25, .05};
    std::piecewise_constant_distribution<> dist(std::begin(interval),
                                                std::end(interval),
                                                std::begin(weights));
    // Choose generator
    std::mt19937 gen;  // seed as wanted
    // Demonstrate by pouring into avg[rand-1]
    const unsigned N = 1000000;
    double avg[sizeof(weights) / sizeof(weights[0])] = {0};
    for (unsigned i = 0; i < N; ++i)
        avg[static_cast<unsigned>(dist(gen)) - 1]++;
    // Comute averages
    for (double* i = std::begin(avg); i < std::end(avg); ++i)
        *i /= N;
    // Display
    for (unsigned i = 1; i <= sizeof(avg)/sizeof(avg[0]); ++i)
        std::cout << "avg[" << i << "] = " << avg[i-1] << '\n';
}

对我来说输出:

avg[1] = 0.199779
avg[2] = 0.100002
avg[3] = 0.400111
avg[4] = 0.250257
avg[5] = 0.049851

【讨论】:

    【解决方案5】:

    您所描述的是从特定概率分布中提取的随机数生成器的实现。例如,从高斯分布中抽取数字应该抽取随机数,使得特定抽取的概率 x 成正比
    (来源:wikimedia.org
    .

    一般而言,该方法是从均匀随机分布中提取,然后在该提取位置选择所需分布的累积分布函数 (CDF) 的值。在正态高斯的情况下,从均匀分布中抽取一个随机数 x(这是标准随机数生成器应该给出的),然后选择 作为随机的高斯分布值。对于您的情况,您描述的 CDF 是一个分段连续阶梯函数,可以使用您已经收到的许多(正确)答案中的任何一个来实现。

    当然,这都是琐事。你应该做的是使用一个已经为你处理这个的库。统计数据和随机数生成并非易事,无需重新发明轮子。请参阅 Neil 的回答(并查看 Boost random number 库)。

    【讨论】:

      【解决方案6】:

      您为什么不直接使用返回 0.0 到 1.0 之间数字的常规随机数生成器,然后用另一个根据您的要求返回数字的函数包装它?

      喜欢

      double biased (double seed) {
      if (seed >= 0.0 && seed <0.2) return 1;
      else if  ...
      }
      

      【讨论】:

      • 我不会使用seed 作为随机生成数字的标识符,这很混乱......
      • 为什么不呢? C rand 函数也需要一个种子,在大多数情况下是系统时间。所以有偏见的是一个基于种子生成随机数的函数。
      【解决方案7】:

      在[0,1]、if 0&lt; x&lt;0.2 return 1if 0.2&lt;x &lt;0.3 return 2等中抛出一个随机实数x。

      有关一般问题,请参阅here

      【讨论】:

        【解决方案8】:

        肯尼根据您的特定频率分布给出了适当的答案。

        更一般的答案适用于数据的 CDF(累积分布函数),并使用统一的随机数在分布中选取一个值。

        【讨论】:

          【解决方案9】:

          我正在做同样的事情,我发现了这个: http://eli.thegreenplace.net/2010/01/22/weighted-random-generation-in-python/

          对于你所说的目的来说似乎足够好。

          【讨论】:

            【解决方案10】:
            #include <boost/random/discrete_distribution.hpp>
            #include <boost/random/mersenne_twister.hpp>
            #include <boost/random/variate_generator.hpp>
            
            #include <iostream>
            
            int main()
            {
            
              unsigned int seed = 42;
              boost::mt19937 generator(seed);
            
              // return 0 with probability 10%
              //        1                  40%
              //        2                  50%
              boost::random::discrete_distribution<int> custom_dist{1,4,5};
            
              boost::variate_generator<boost::mt19937&,
              boost::random::discrete_distribution<int> > rndn(generator, custom_dist);
            
              for (unsigned int i = 0; i<10000; i++) {
                std::cout << rndn() << std::endl;
              }
            
              return 0;
            
            }
            

            这是一个结果图:

            【讨论】:

              【解决方案11】:

              我一直在为 TypeScript 寻找类似的东西,但只为 C 找到了这个问题。

              所以这是我想出的 TypeScript 中的有偏随机数生成器,以防万一有人在 TypeScript 中需要这样的东西。我相信您可以以某种方式将其翻译成 C。

              export async function weightedRandomItem<T>(list: { weight: number; item: T }[]): Promise<T> {
                  const weightSum = sumBy(list, (item) => item.weight)
                  const randomIndex = await randomIntegerBetween(1, weightSum)
              
                  let currentIndex = 1
                  for (const listItem of list) {
                      if (randomIndex >= currentIndex && randomIndex < currentIndex + listItem.weight) {
                          return listItem.item
                      }
                      currentIndex += listItem.weight
                  }
                  throw new Error("No item selected. Impossible.")
              }
              

              randomIntegerBetween(minInclusive: number, maxInclusive: number) 从您选择的 RNG 中返回指定范围(包括最小值和最大值)的随机整数。

              sumBy() 在这种情况下是lodash function,它应该是不言自明的。

              作为输入,您可以传递如下内容:

              [{
                  weight: 10,
                  item: 1,
              },
              {
                  weight: 50,
                  item: 2,
              },
              {
                  weight: 30,
                  item: 3,
              },
              {
                  weight: 10,
                  item: 4,
              }]
              

              那么,结果很可能是2

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2023-03-12
                • 2013-07-29
                • 2019-02-28
                • 2018-12-31
                • 2011-01-23
                相关资源
                最近更新 更多