【速记】离散分布的实现算法

news2024/12/29 16:19:58

离散分布与 zipf 分布

下面的一段代码,能根据数值描述来生成对应概率的离散值:

#include <iostream>
#include <iomanip>
#include <map>
#include <random>

using namespace std;

int main()
{
    std::random_device rd;
    std::mt19937 gen(rd());
    std::discrete_distribution<> d({10, 5, 5, 10});
    std::map<int, int> map;
    for(int n=0; n<6000; ++n) {
        int randval = d(gen);
        ++map[randval];
    }
    for(int n=0; n<4; ++n) {
        std::cout << n << " generated " << std::setw(4) << map[n] << std::endl;
    }
}

执行结果:

[xiaochu.yh ~/tools/cpp] $g++ rand.cpp -std=c++17
[xiaochu.yh ~/tools/cpp] $./a.out
0 generated 2013
1 generated  923
2 generated  980
3 generated 2084
[xiaochu.yh ~/tools/cpp] $./a.out
0 generated 1985
1 generated  954
2 generated 1032
3 generated 2029

可以看到,d(gen) 可以生成0、1、2、3四个值,并且这几个值的概率大约为 0.5, 0.25, 0.25, 0.5,它正是 {10, 5, 5, 10} 的比例。

基于 discrete_distribution 类,我们通过控制它的参数,可以实现 zipf 分布:

#include <iostream>
#include <iomanip>
#include <map>
#include <random>

using namespace std;

void test_zipf()
{
    int alpha = 1;
    std::vector<double> v;
    v.reserve(26);
    for (int i = 1; i <= 26; ++i) {
      v.push_back(1.0 / pow(i, alpha));
    }
    std::random_device rd;
    std::mt19937 gen(rd());
    std::discrete_distribution<> d(v.begin(), v.end());
    std::map<int, int> map;
    for(int n=0; n<100000; ++n) {
        ++map[d(gen)];
    }
    for(int n=0; n<26; ++n) {
        std::cout << n << " generated " << std::setw(4) << map[n] << std::endl;
    }
}

输出结果如下:

[xiaochu.yh ~/tools/cpp] $g++ rand.cpp -std=c++17
[xiaochu.yh ~/tools/cpp] $./a.out
0 generated 25811
1 generated 12968
2 generated 8804
3 generated 6563
4 generated 5220
5 generated 4300
6 generated 3751
7 generated 3233
8 generated 2853
9 generated 2627
10 generated 2312
11 generated 2157
12 generated 1970
13 generated 1820
14 generated 1695
15 generated 1658
16 generated 1543
17 generated 1464
18 generated 1304
19 generated 1315
20 generated 1297
21 generated 1187
22 generated 1140
23 generated 1017
24 generated 1034
25 generated  957

用 Excel 画出一个图:
在这里插入图片描述

我们将 alpha 调整为 2,再画出一个图如下,可以看到曲线更加陡峭。在这里插入图片描述
把两个曲线放在一起对比下,可见 alpha 越大,倾斜越严重。
在这里插入图片描述

离散分布实现原理

核心逻辑分为2步

  • 第1步是生成一个 normalized accumulative sum 数组
  • 第2步是根据随机值在这个数组里找到一个最近的值,返回这个值的下标。

std::discrete_distribution<> d(v.begin(), v.end());

这段逻辑会完成第1步,gcc 中的实现如下:

template<typename _IntType>
    void
    discrete_distribution<_IntType>::param_type::_M_initialize()
{
    if (_M_prob.size() < 2)
	{
	  _M_prob.clear();
	  return;
	}

      const double __sum = std::accumulate(_M_prob.begin(),
					   _M_prob.end(), 0.0);
      // Now normalize the probabilites.
      __detail::__transform(_M_prob.begin(), _M_prob.end(), _M_prob.begin(),
			  std::bind2nd(std::divides<double>(), __sum));
      // Accumulate partial sums.
      _M_cp.reserve(_M_prob.size());
      std::partial_sum(_M_prob.begin(), _M_prob.end(),
		       std::back_inserter(_M_cp));
      // Make sure the last cumulative probability is one.
      _M_cp[_M_cp.size() - 1] = 1.0;
    }

这段逻辑是把从 v.begin() 到 v.end() 的值做归一化,然后求一个累加和数组。对于 {10, 5, 5, 10} 来说,求得的归一化数组就是 {0.33, 0.50, 0.66, 1}。

有了这个数组后,只需要随机生成一个 0~1之间的浮点数 f,用 f 去这个数组里找大于等于它的值,就得到一个下标。例如 f = 0.53,就找到 0.66,得到下标 2,最终输出 2。只要随机数是均匀的,那么很容易知道 f 落到 0~0.33 的概率,大于落到 0.33~0.50的概率,等等,就得到我们需要的分布。

下面是 lib 中对应的实现:

d(gen)

  template<typename _IntType>
    template<typename _UniformRandomNumberGenerator>
      typename discrete_distribution<_IntType>::result_type
      discrete_distribution<_IntType>::
      operator()(_UniformRandomNumberGenerator& __urng,
		 const param_type& __param)
{
	if (__param._M_cp.empty())
	  return result_type(0);

	__detail::_Adaptor<_UniformRandomNumberGenerator, double>
	  __aurng(__urng);

	const double __p = __aurng();
	auto __pos = std::lower_bound(__param._M_cp.begin(),
				      __param._M_cp.end(), __p);

	return __pos - __param._M_cp.begin();
}

我们只要用符合 zipf 分布的概率值来初始化 discrete_distribution 就能很方便地实现一个 zipf 分布函数。

从上面的原理可知,zipf 分布的实现复杂度如下:

  • 空间复杂度:与需要生成的不同元素个数 N 相等。仅仅初始化数据结构时付出该代价,和生成多少个随机数无关。
  • 时间复杂度:与 std::lower_bound 的实现复杂度相关,一般为 O(logN)。每次生成一个随机数都要付出该代价。

所以,一般系统中提供 zipf 函数时都会限制 N 的取值范围,否则可能导致系统被打爆。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/182897.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

「计算机组成原理」计算机系统概述

文章目录一、计算机发展历程1.1 什么是计算机系统1.2 硬件的发展1.2.1 硬件发展1.2.2 摩尔定律1.3 软件的发展1.4 目前的发展趋势二、计算机系统的多级层次结构2.1 编程语言的三个等级2.2 计算机系统层次结构三、计算机硬件的基本组成3.1 冯诺依曼结构3.2 现代计算机结构四、计…

Codeforces Round #847 (Div. 3) 的 C. Premutation(找规律题)

题面&#xff1a;中文大意&#xff1a;如果一个n个数字的序列恰好包含了1到n的所有整数&#xff0c;那么这个序列就被称为置换。例如&#xff0c;序列[3&#xff0c;1&#xff0c;4&#xff0c;2]。1]和[2&#xff0c;1]是互换&#xff0c;但是[1&#xff0c;2&#xff0c;1]&a…

Java设计模式-备忘录模式Memento

介绍 备忘录模式&#xff08;Memento Pattern&#xff09;在不破坏封装性的前提下&#xff0c;捕获一个对象的内部状态&#xff0c;并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。可以这里理解备忘录模式&#xff1a;现实生活中的备忘录是用来记录某…

window系统path环境变量删除了怎么办?

前言 纪念我今天装JDK配置环境时&#xff0c;误删了path环境变量&#xff0c;后总结的知识点&#xff0c;希望对大家有所帮助&#xff0c;期待大家的评论&#xff01; 目录 前言 方案一&#xff1a;从注册表里找 方案二&#xff1a;实在没办法&#xff0c;从网上复制 方案三…

7-1输入/输出系统-概念外设接口

文章目录一.I/O系统基本概念&#xff08;一&#xff09;输入/输出系统&#xff08;二&#xff09;I/O控制方式二.外部设备1.显示存储器VRAM2.字符显示器3.外储存器三.I/O接口1.I/O接口的功能2.I/O接口的基本结构3.I/O接口的工作原理4.I/O接口的类型5.I/O端口及其编址&#xff0…

网络编程(TCP+UDP)(3)

1)咱们之前所说的网络分层就是因为说如果说使用一个协议太复杂了&#xff0c;那么我们就需要把这个协议分层&#xff0c;每一个协议都会简单一些&#xff0c;灵活替换也更方便&#xff1b; 2)咱们现在需要实现一个网络计算器&#xff1b; 1)上图是模拟一个计算器服务器和客户端…

第01天-Java数据结构和算法

001_几个经典的算法面试题(1)暴力匹配不推荐KMP算法分治算法002_几个经典的算法面试题(2)回溯算法图的深度优先遍历算法(DFS) 贪心算法优化003_内容介绍和授课方式数据结构和算法的重要性课程亮点和授课方式004_数据结构和算法的关系005_编程中实际遇到的几个问题Java代码小结…

机器学习实战(第二版)读书笔记(3)——膨胀卷积,WaveNet

一、基础知识 对于一个卷积层&#xff0c;如果希望增加输出单元的感受野&#xff0c;一般可以通过三种方式实现&#xff1a; 增加卷积核的大小增加层数&#xff08;比如两层3 3 的卷积可以近似一层5 5 卷积的效果&#xff09;在卷积之前进行池化操作 其中第1&#xff0c;2种…

推荐5个很牛的开源项目

大家伙们年过了哈&#xff0c;该收拾收拾心情上班了。 不知道大家有没有这种感觉&#xff0c;年纪越大&#xff0c;越觉得年过得快。感觉好像才刚开始&#xff0c;马上初五了&#xff0c;初六送完穷鬼&#xff0c;初七送自己出来上班了&#xff08;没有哭&#xff09;。 不过…

Python编写的词频统计工具的使用说明

一、工具下载 https://download.csdn.net/download/huangbangqing12/87400984 二、工具使用方式 目录文件如下所示&#xff1a; 请先在word.txt文件里放入目标长尾词&#xff0c;一行一个&#xff1a; 文件-另存为&#xff1a; 选择utf-8编码并直接保存替换原文件&#xff1…

二叉树的层次遍历

文章目录二叉树的层次遍历二叉树的层次遍历107. 二叉树的层序遍历 II199. 二叉树的右视图637.二叉树的层平均值429. N 叉树的层序遍历515.在每个树行中找最大值116. 填充每个节点的下一个右侧节点指针填充每个节点的下一个右侧节点指针II104.二叉树的最大深度二叉树的最小深度二…

ESPnet

文章目录关于 ESPnet安装配置运行 yesno关于 ESPnet github&#xff1a; https://github.com/espnet/espnet ESPnet is an end-to-end speech processing toolkit covering end-to-end speech recognition, text-to-speech, speech translation, speech enhancement, speaker …

机器自动翻译古文拼音 - 十大宋词 - 声声慢 寻寻觅觅 李清照

声声慢寻寻觅觅 宋李清照 寻寻觅觅&#xff0c;冷冷清清&#xff0c;凄凄惨惨戚戚。 乍暖还寒时候&#xff0c;最难将息。 三杯两盏淡酒&#xff0c;怎敌他、晚来风急。 雁过也&#xff0c;最伤心&#xff0c;却是旧时相识。 满地黄花堆积&#xff0c;憔悴损&#xff0c;如今…

Web 应用程序——我的心理备忘单

介绍本文是“持续交付&#xff1a;HTML 到 Kubernetes”的一部分。虽然我迫不及待地想深入了解分布式系统的细节&#xff0c;但我发现自己处于一个不愉快的境地&#xff1a;我认为最好从前端开始写。那是因为网络应用程序是当今的标准。在多个云中部署的 ArgoCD-Kubernetes 集群…

商业智能BI,大数据时代的新趋势

根据IDC预测&#xff0c;2025年时中国产生的数据量预计将达48.6ZB&#xff0c;在全球中的比例为27.8%。在未来&#xff0c;数据会是构建现代社会的基本要素&#xff0c;也是社会的基本建设。这也不禁让我想起了最近新公布的《关于构建数据基础制度更好发挥数据要素作用的意见》…

高并发下如何保证接口的幂等性?

一、什么是幂等&#xff1f; 看一下维基百科怎么说的&#xff1a; 幂等性&#xff1a;多次调用方法或者接口不会改变业务状态&#xff0c;可以保证重复调用的结果和单次调用的结果一致。 二、使用幂等的场景 1、前端重复提交 用户注册&#xff0c;用户创建商品等操作&#…

使用这个工具,本地调试UI再也不用怕了

前言&#xff1a;在我们日常使用中&#xff0c;很多场景都会用到UI自动化&#xff0c;通用的都是PythonSelenium的方式。今天介绍一种&#xff0c;不用通过代码&#xff0c;直接通过页面可视化配置的方式&#xff0c;就可以完成我们想要的自动化场景。话不多说&#xff0c;正片…

ElasticSearch - 结果处理

目录 结果处理-排序 结果处理-分页 结果处理-高亮 结果处理-排序 elasticsearch默认是根据相关度算分(_score)来排序&#xff0c;但是也支持自定义方式对搜索结果排序可以排序字段类型有&#xff1a;keyword类型、数值类型、地理坐标类型、日期类型等普通字段排序keyword、数…

pytorch图像分类全流程(五)--图像分类算法精度评估指标

本次我们来学习图像分类算法精度的各种评估指标&#xff1a;precision、recall、accuracy、f1-score、AP、AUC。 首先我们来学一个很重要的概念&#xff0c;混淆矩阵&#xff1a; 1.精确率(Precision)&#xff1a; 指的是所有被判定为正类&#xff08;TPFP&#xff09;中&…

8-Arm PEG-Succinamide Acid,8-Arm PEG-SAA,八臂-聚乙二醇-丁二酸酰胺供应

英文名称&#xff1a;8-Arm PEG-SAA&#xff0c;8-Arm PEG-Succinamide Acid 中文名称&#xff1a;八臂-聚乙二醇-丁二酸酰胺 8-臂PEG-SAA是一种多臂PEG衍生物&#xff0c;在连接到一个六甘油核心的八个臂的每个末端具有羧基。PEG和丁二酰胺酸COOH基团之间存在C3酰胺键。PEG酸…