随机产生不同概率的整数

3
我在想是否有一种方法可以在A和B之间生成随机数,并且如果一个数符合特定要求,则它比A和B之间的所有其他数字更有可能出现,例如:较小的数字更有可能出现,因此如果A = 1且B = 10,则1将是最可能出现的数字,而10则是最不可能出现的数字。非常感谢您的帮助 :)(对于糟糕的英语/语法/问题表示抱歉)

您正在寻找Zipf分布吗?https://dev59.com/0Gkw5IYBdhLWcg3wUI1W - HazemGomaa
4个回答

2
C++11(您现在应该绝对使用)将<random>头添加到C++标准库中。此头文件为C++提供了更高质量的随机数生成器。使用srand()rand()从来都不是一个好主意,因为没有保证其质量,但现在这已经变得不可原谅了。
在您的示例中,听起来您想要的可能被称为“离散三角形分布”:概率质量函数看起来像一个三角形。在C++中实现它最简单(但也许不是最有效)的方法是使用<random>中包含的离散分布。
auto discrete_triangular_distribution(int max) {
    std::vector<int> weights(max);
    std::iota(weights.begin(), weights.end(), 0);
    std::discrete_distribution<> dist(weights.begin(), weights.end());
    return dist;
}

int main() {
    std::random_device rd;
    std::mt19937 gen(rd());
    auto&& dist = discrete_triangular_distribution(10);
    std::map<int, int> counts;
    for (int i = 0; i < 10000; i++)
        ++counts[dist(gen)];
    for (auto count: counts)
        std::cout << count.first << " generated ";
        std::cout << count.second << " times.\n";
}

对我来说,这会产生以下输出:

1 generated 233 times.
2 generated 425 times.
3 generated 677 times.
4 generated 854 times.
5 generated 1130 times.
6 generated 1334 times.
7 generated 1565 times.
8 generated 1804 times.
9 generated 1978 times.

对于比这更复杂的事情,最好使用现有的分布之一(据我所知,所有常用的统计分布都已包含在内),或者编写自己的分布。编写自己的分布并不难:它只需要是一个对象,具有一个函数调用运算符,该运算符接受一个随机比特生成器,并使用这些比特产生(在本例中)随机数。但是您可以创建一个生成随机字符串或任何任意随机对象的分布,也许是为了测试目的。


从问题中看来,输出应该在范围 [1, 10] 内,并且较小的数字应该更有可能出现。 - Ami Tavory
这只是一个例子。 - Miles Rout
非常感谢您的帮助!我刚刚将:++counts [dist(gen)]更改为++counts [abs(10-dist(gen))],以使较小的数字更有可能出现 :) - AdminBenni
@MilesRout 嘿,我一直在使用你的随机分布来完成我的项目,但是我遇到了一个问题,每当输入0-2之间的数字时,程序就会崩溃,有没有简单的方法可以避免这种情况? - AdminBenni

2
您的问题没有指定要使用哪个分布。其中一个选项是使用(负)指数分布。该分布由参数λ参数化。对于每个λ值,最大结果是无界的(需要处理以仅返回在指定范围内的结果)。

enter image description here

从维基百科(作者:Skbkekas,CC BY 3.0):
因此,理论上任何λ都可以工作;然而,累积分布函数的属性不同。

enter image description here

这段文字的意思是:从维基百科上获得,由Skbkekas撰写,采用CC BY 3.0许可协议。其中表达了选择某个值的最佳方式是按照1 / (to - from + 1)的顺序进行选择。下面的类与标准库分布函数类似,内部通过循环生成数字,直到得到[from, to]范围内的结果。
#include <iostream>
#include <iomanip>
#include <string>
#include <map>
#include <random>

class bounded_discrete_exponential_dist {
public: 
    explicit bounded_discrete_exponential_dist(std::size_t from, std::size_t to) : 
        m_from{from}, m_to{to}, m_d{0.5 / (to - from + 1)} {}
    explicit bounded_discrete_exponential_dist(std::size_t from, std::size_t to, double factor) : 
        m_from{from}, m_to{to}, m_d{factor} {}

    template<class Gen>
    std::size_t operator()(Gen &gen) {
        while(true) {
            const auto r = m_from + static_cast<std::size_t>(m_d(gen));
            if(r <= m_to)   
                return r;
        }
    }

private:
    std::size_t m_from, m_to;
    std::exponential_distribution<> m_d;
};

这是一个使用它的例子:
int main()
{
    std::random_device rd;
    std::mt19937 gen(rd());

    bounded_discrete_exponential_dist d{1, 10};

    std::vector<std::size_t> hist(10, 0);
    for(std::size_t i = 0; i < 99999; ++i)
        ++hist[d(gen) - 1];

    for(auto h: hist)
        std::cout << std::string(static_cast<std::size_t>(80 * h / 99999.), '+') << std::endl;
}

当运行时,它会输出像这样的直方图:
$ ./a.out
++++++++++
+++++++++
+++++++++
++++++++
+++++++
+++++++
+++++++
+++++++
++++++
++++++

非常好的答案,但我认为@Miles的答案更适合我的需求。 - AdminBenni
1
@AdminBenni 没问题,祝一切顺利。 - Ami Tavory

0

你的基本随机数生成器应该能够在0到1-epsilon范围内产生高质量、均匀分布的随机数。然后,你可以进行转换以得到所需的分布。最简单的转换当然是 (int) ( p * N ),通常需要整数范围为0 到N -1。

但还有许多其他的转换方法可供尝试。例如,取平方根来偏向于1.0,然后取 1-p 来设置向0的偏差。或者你可以查找泊松分布,这可能是你想要的。你也可以使用半高斯分布(统计钟形曲线,将零条目切断,并且可能还有分布的极端尾巴超出范围)。

没有正确答案。尝试各种各样的方法,绘制出大约一万个值,并选择结果你喜欢的一个。


-1
你可以创建一个值的数组,其中更可能的值具有更多的索引,然后选择一个随机索引。
例如:
int random[55];
int result;

    int index = 0;
    for (int i = 1 ; i <= 10 ; ++i)
       for (int j = i ; j <= 10 ; ++j)
          random[index++] = i;

result = random[rand() % 55];

此外,您可以尝试获取两次随机数,第一次选择最大数,然后选择您的随机数:

int max= rand() % 10 + 1; // This is your max value
int random = rand() %  max + 1; // This is you result

无论哪种方式,都会使1比2更有可能,2比3更有可能...9比10更有可能。

1
是的,每个人都想要正确的解决方案。 - Miles Rout

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接