随机数生成生成器和力扣按权重随机选择 528

随机数生成生成器和力扣按权重随机选择 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值