随机数生成生成器和力扣按权重随机选择 528
在linux下内核有个专门内核模块进行生成随机数
/dev/random 不是很精准
/dev/urandom 更精准
原理 是内核的一个模块,专门产生的,根据cpu指令等等产生
读取随机数的话也可以读取这两个设备。
例如如下的代码:
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main()
{
int fd1 = open("/dev/urandom", O_RDONLY);
int fd2 = open("/dev/random", O_RDONLY);
unsigned long u1 = 0, u2;
read(fd1, &u1, sizeof(u1));
read(fd2, &u2, sizeof(u2));
printf("u1 = %lu\n", u1);
printf("u2 = %lu\n", u2);
return 0;
}
打印出来就是两个随机数。
std::random_device{}() // 生成真随机数,在linux中这个是通过读取 /dev/urandom 进行获取的, 一般用于做随机数种子
mt19937 // 用于生成伪随机数, 这个和rand()类似都是需要设置随机数种子。
真随机数一般为操作系统生成,根据当前操作系统的中断,键盘鼠标的输入,文件句柄等等一系列随机事件组成。伪随机数一般由算法组成,mt19937实际上就是一个算法的名称。
#include <iostream>
#include <random>
using namespace std;
int main()
{
int i ;
for(i = 0; i < 10; i++)
{
cout << std::random_device{}() <<endl; //真随机数
}
mt19937 Rand;
for(i = 0; i < 10; i++){
cout << Rand() <<endl; //假随机和rand() 一样
}
mt19937 s(std::random_device{}());
for(i = 0 ; i < 10; i++){
cout << s() <<endl; //通过真随机数播种,随后得到随机数质量更高
}
}
如果要生成一个范围的随机数该怎么办呢,如果要生成一个类似于高斯分布形状的随机数该怎么办呢,其实在STL中已经为我们准备好了。
std::uniform_int_distribution 均匀分布器
std::normal_distribution 高斯分布器
均匀分布器可以通过构造函数设置随机值的上下范围
高斯分布器构造函数的两个参数用于设置均值和方差
#include <iostream>
#include <random>
using namespace std;
int main()
{
mt19937 s(std::random_device{}());
uniform_int_distribution<int> Rand(0, 10);
vector<int> arr(11);
for(int i = 0 ; i < 1000000; i++){
arr[Rand(s)]++;
}
for(int i = 0 ; i < arr.size(); i++){
cout << arr[i] << endl;
}
}
上代码是均匀分布,生成一百万次 0到10的随机数并统计每项出现的次数运行结果如下:
运行结果每个区间的值大致是均匀的。
#include <iostream>
#include <random>
using namespace std;
int main()
{
mt19937 s(std::random_device{}());
uniform_int_distribution<int> Rand(0, 10);
std::normal_distribution<double> rands(100, 5);
int size = 1000000; //一百万次
int count_90_110 = 0;
int count_95_105 = 0;
for(int i = 0 ; i < size; i++){
double a = rands(s);
if(a >= 90 && a <= 110){
count_90_110++;
}
if(a >= 95 && a <= 105){
count_95_105++;
}
}
cout << count_90_110 << " 90 - 100" <<endl;
cout << count_95_105 << " 95 - 105" <<endl;
}
打印100万次发现在 一个 方差范围内概率大致为 68%, 两个 方差范围内概率大致为95% 符合正太分布。
正态分布如图所示:
http://c.biancheng.net/view/643.html
https://baike.baidu.com/item/%E6%AD%A3%E6%80%81%E5%88%86%E5%B8%83/829892?fr=aladdin
给定一个正整数数组 w ,其中 w[i] 代表下标 i 的权重(下标从 0 开始),请写一个函数 pickIndex ,它可以随机地获取下标 i,选取下标 i 的概率与 w[i] 成正比。
例如,对于 w = [1, 3],挑选下标 0 的概率为 1 / (1 + 3) = 0.25 (即,25%),而选取下标 1 的概率为 3 / (1 + 3) = 0.75(即,75%)。
也就是说,选取下标 i 的概率为 w[i] / sum(w)
class Solution{
private:
mt19937 gen;
uniform_int_distribution<int> dis;
vector<int> pre;
public:
Solution(vector<int>& w): gen(random_device{}()), dis(1, accumulate(w.begin(), w.end(), 0)) {
partial_sum(w.begin(), w.end(), back_inserter(pre));
}
int pickIndex() {
int x = dis(gen);
return lower_bound(pre.begin(), pre.end(), x) - pre.begin();
}
};
/*
mt19937头文件是<random> 是伪随机数产生器,用于产生高性能的随机数
uniform_int_distribution 头文件在<random>中,是一个随机数分布类,参数为生成随机数的类型,构造函数接受两个值表示区间段
accumulate 头文件在<numeric>中,求特定范围内所有元素的和。
spartial_sum函数的头文件在<numeric>,对(first, last)内的元素逐个求累计和,放在result容器内
back_inserter函数头文件<iterator>,用于在末尾插入元素。
lower_bound头文件在<algorithm>,用于找出范围内不大于num的第一个元素
*/
该题的力扣链接:https://leetcode-cn.com/problems/random-pick-with-weight