封装不同类模板的随机数生成器

最近准备刷题,打算简单封装下随机数生成器,方便产生测试数据。C++11的STL提供了很多分布类型,我比较常用的是均匀分布,均匀分布的值有两种类型,一类是整数,另一类是浮点数,STL根据值的类型定义了两个函数 std::uniform_int_distributionstd::uniform_real_distribution 。为了方便使用,我期望在使用的时候通过函数模板的实参推导出要生成的数值类型,而不是显式指定要生成的数值类型。

判断模板实参类型

上面这个需求很简单,最开始想到的方式是对模板实参推断的类型进行判断,根据判断结果做不同的处理。

#include <random>
#include <type_traits>
#include <functional>
#include <limits>std::default_random_engine e;template<typename T_>
void Randoms(size_t counts, T_ min, T_ max) {std::function<T_()> distribute_;if (std::is_integral<T_>::value) {// lambdadistribute_ = [=]() {// 类模板特化 默认result_type int;std::uniform_int_distribution<> u(min, max);return u(e);};} else {// 类模板特化 默认result_type double;std::uniform_real_distribution<> u(min, max);// std::bind// 成员函数返回值默认类型为double;using type_ = double (std::uniform_real_distribution<>::*)(std::default_random_engine &);distribute_ = std::bind(static_cast<type_>(&std::uniform_real_distribution<>::operator()),&u,e);}for (int i = 0; i < counts; ++i) {std::cout << distribute_() << " ";}std::cout << std::endl;
}int main(int argc, char **argv) {Randoms(5,std::numeric_limits<std::size_t>::min(),std::numeric_limits<std::size_t>::max());Randoms(5, -0.5, 0.5);return 0;
}

从这个测试实例的结果来看,产生的数值远远超过了默认的 int 类型所能表示的数值,然后去看了看具体实现。下面代码是MinGW中GCC的实现版本,从实现中可以看出,均匀分布的类模板重载了函数调用运算符,接收两个参数,一个是均匀分布的随机数生成器,第二个是参数类型。首先定义了三个类型别名:

  • 生成器的结果类型 _Gresult_type
  • 无符号的结果类型 __utype,由于类模板推断的结果类型是 int_utype 的实际类型就是 unsigned int
  • 生成器的结果类型和无符号的结果类型的共同类型 __uctype, 该类型只有当 _Gresult_type__utype 可以相互转换才存在,这个过程实际就是类型转换。

随后使用 __uctype类型别名又定义了下面几个关键的变量,简单来看,生成器相关的属性值(如最大、最小值等)与随机数引擎有关,只要随机数引擎不变,生成器的属性值应该就不会变(至于属性值到底变不变以及是否与随机数种子有关等,待以后有时间再看看那部分源码怎么写的,目前就按不变来理解)。

  • __urngmin 表示生成器的最小值,不会改变;
  • __urngmax 表示生成器的最大值,不会改变;
  • __urngrange 表示生成器的数值范围,不会改变;
  • __urange 表示参数类型的数值范围,在放大操作时会随着递归进行改变;
  • __ret 表示返回结果的一部分,加上参数范围的最小值即为最终的随机数结果。

当生成器的范围超过了参数类型的范围,那么生成器生成的数值则可能超过参数类型的范围,这时候就需要进行缩小操作。缩小操作首先计算出缩小因子,然后根据缩小因子计算出缩小后符合参数类型的生成器的最大值,只要生成器生成的值超过了允许的最大值则继续生成下一个,直至生成符合要求的数值,最后将生成器的数值按比例缩小。

当生成器的范围低于参数类型的范围,那么就无法生成超过生成器范围,低于参数类型范围的数值,这时候就需要进行放大操作。放大操作是一个循环递归,递归终止的条件则是生成器的范围大于等于参数类型的范围,当生成器生成的值超过了参数类型的范围,说明生成的数值不正确,需要继续重新生成。根据随机数计算公式可以看到,high 的区间是 [0,urange / (urngrange + 1)]low 的区间是 [0, urngrange],这两个区间左右两侧都是闭区间,那么根据 (urngrange + 1) * high + low 计算的区间则是[0,urange + low],因此生成的数值可能比 urange 大,这个可能性被第一个循环条件处理了。从循环的条件看还有一个判断条件,这个条件还没太理解,初步猜测与数值溢出有一定关系。如果 __tmp 已经处于最大值,此时再加上一个非0的随机数,那么则可能超过 __ret 本身所能表示的范围,导致溢出。

template <typename _IntType>
template <typename _UniformRandomNumberGenerator>
typename uniform_int_distribution<_IntType>::result_type
uniform_int_distribution<_IntType>::
operator()(_UniformRandomNumberGenerator &__urng,const param_type &__param)
{typedef typename _UniformRandomNumberGenerator::result_type_Gresult_type;typedef typename std::make_unsigned<result_type>::type __utype;typedef typename std::common_type<_Gresult_type, __utype>::type__uctype;const __uctype __urngmin = __urng.min();const __uctype __urngmax = __urng.max();const __uctype __urngrange = __urngmax - __urngmin;const __uctype __urange = __uctype(__param.b()) - __uctype(__param.a());__uctype __ret;if (__urngrange > __urange){// downscalingconst __uctype __uerange = __urange + 1; // __urange can be zeroconst __uctype __scaling = __urngrange / __uerange;const __uctype __past = __uerange * __scaling;do__ret = __uctype(__urng()) - __urngmin;while (__ret >= __past);__ret /= __scaling;}else if (__urngrange < __urange){// upscaling/*Note that every value in [0, urange]can be written uniquely as(urngrange + 1) * high + lowwherehigh in [0, urange / (urngrange + 1)]andlow in [0, urngrange].*/__uctype __tmp; // wraparound controldo{const __uctype __uerngrange = __urngrange + 1;__tmp = (__uerngrange * operator()(__urng, param_type(0, __urange / __uerngrange)));__ret = __tmp + (__uctype(__urng()) - __urngmin);} while (__ret > __urange || __ret < __tmp);}else__ret = __uctype(__urng()) - __urngmin;return __ret + __param.a();
}

std::enable_if 模板元方法

std::enable_if 的定义如下:

template< bool B, class T = void >
struct enable_if;

std::enable_if 实现的功能是根据类模板参数 B 来决定是否定义类型 T 。它是一种元函数,利用 SFINAE 根据类型特征有条件地从重载解析中删除函数,并为不同的类型特征提供单独的函数重载和特化 std :: enable_if 可用作附加函数参数(不适用于运算符重载),返回类型(不适用于构造函数和析构函数)或用作类模板或函数模板参数。参考 https://en.cppreference.com/w/cpp/types/enable_if

SFINAE“Substitution Failure Is Not An Error” 的简写,表示替换失败不是错误,这个规则在函数模板的重载解析中经常被使用,当特化发生替换失败时,这个特化会被从函数集中删除掉,而不是导致一个编译错误。

根据这个原则,只要通过返回值进行区分,就能实现这两个函数的封装了,非常简单明了。

#include <random>
#include <type_traits>
#include <functional>
#include <limits>
#include <algorithm>class Randoms {
public:template<typename T_>std::vector<T_> operator()(std::size_t counts, T_ min, T_ max) {std::vector<T_> vec;vec.reserve(counts);for (int i = 0; i < counts; ++i) {vec.push_back(generate_(min, max));}return std::move(vec);}private:std::default_random_engine e;template<typename T_>std::enable_if_t<std::is_integral<T_>::value, T_>generate_(T_ min, T_ max) {std::uniform_int_distribution<T_> u(min, max);return u(e);}template<typename T_>std::enable_if_t<std::is_floating_point<T_>::value, T_>generate_(T_ min, T_ max) {std::uniform_real_distribution<T_> u_real(min, max);return u_real(e);}
};int main(int argc, char **argv) {auto int_result = Randoms()(5,std::numeric_limits<std::size_t>::min(),std::numeric_limits<std::size_t>::max());auto real_result = Randoms()(5, -0.5, 0.5);for (const auto &ci : int_result) {std::cout << ci << " ";}std::cout << std::endl;std::for_each(real_result.cbegin(),real_result.cend(),[](const auto &cr) { std::cout << cr << " "; });std::cout << std::endl;return 0;
}

本文博客地址

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

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

相关文章

Log4j详细设置说明

1. 动态的改变记录级别和策略&#xff0c;即修改log4j.properties,不需要重启Web应用&#xff0c;这需要在web.xml中设置一下。2. 把log文件定在 /WEB-INF/logs/ 而不需要写绝对路径。3. 可以把log4j.properties和其他properties一起放在/WEB-INF/ &#xff0c;而不是Class-Pat…

【机器学习】——卷积神经网络(Keras)修改学习率(定值+自适应)

目录 1、直接获取现有模型的学习率 2、打印显示学习率 3、调整学习率 1&#xff09;1.LearningRateScheduler 2&#xff09;ReduceLROnPlateau 利用tensorflow的Keras模块我们可以建立我们自己定义的卷积神经网络模型&#xff0c;但是一般不会触碰到学习率这个问题&#x…

使用Docker构建你的第一个服务

1. 感受一下Docker的便捷 项目源码 https://github.com/MoonShining/dockernize-grape-helloworld clone下来以后运行,cd到项目目录下&#xff0c;运行 docker build -t api-sample . docker run -p 8080:8080 api-sample:latest 复制代码就可以访问localhost:8080看到效果了. …

MyEclipse连接MySQL

在官网http://www.mysql.com/downloads/下载数据库连接驱动 本文中使用驱动版本为mysql-connector-java-5.1.40 一、创建一个java测试项目MySQLConnectorsTest 在项目下穿件一个lib文件夹用来存放MySQL驱动包。 右键驱动包build path进行add添加操作&#xff0c;打开Referenced…

在Windows系统中配置Google AddressSanitizer

Google AddressSanitizer简介 AddressSanitizer (ASan) 是 C 和 C 的内存错误检测软件&#xff0c;它可以检测&#xff1a; 释放指针后继续使用堆缓冲区溢出栈缓冲区溢出全局缓冲区溢出返回后继续使用在范围之外继续使用初始化顺序的bug内存泄漏 在 Windows 系统中&#xff…

【剑指offer】——求出一个正整数的质数因子(Python)

目录 一、题目描述 二、思路 1、短除法 2、平方根法 一、题目描述 功能:输入一个正整数&#xff0c;按照从小到大的顺序输出它的所有质因子&#xff08;重复的也要列举&#xff09;&#xff08;如180的质因子为2 2 3 3 5 &#xff09; 最后一个数后面也要有空格 输入描述…

Python字符串笔录

python字符串操作实方法&#xff0c;包括了几乎所有常用的python字符串操作&#xff0c;如字符串的替换、删除、截取、复制、连接、比较、查找、分割等 1、去空格及特殊符号 >>> s 123 >>> s.strip() # 去除所有空格123 >>> s1 123 >>&…

第九周进度条

第六周学习进度条 星期一 星期二 星期三 星期四 星期五 星期六 星期日 所花时间 &#xff08;包括上课&#xff09; 无 上午8:00-9:50 无 无 下午4:00-5:00 下午3&#xff1a;00-5&#xff1a;30 代码量&#xff08;行&#xff09; 0行 260行 0行 0行 0行 0…

C++ STL实现的优先队列( priority_queue )

本文参考的源码版本&#xff1a;gcc version 8.1.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project)。 priority_queue 本质是容器适配器&#xff0c;它对内部容器的元素有自己的管理方式&#xff0c;而 priority_queue 实际维护的是一个二叉堆。STL中 priority_queue 的…

【Python函数】——sort,sorted

1、sorted和sort的常规使用 2、关于自定义比较函数 3、试验 from functools import cmp_to_key ll [(2,3,10),(1,2,3),(5,6,7),(2,5,10),(2,4,10)]# 根据一个维度进行排序&#xff0c;这里根据第一维排序 ll1 sorted(ll,key lambda x:x[0]) print(根据一个维度进行排序&a…

生成相关矩阵

U是X&#xff08;差异矩阵&#xff09;各列向量取方向后形成的矩阵&#xff0c;CU^T * U 即相关矩阵&#xff0c;即各列向量两两的夹角&#xff0c;&#xff08;夹角越小说明关联度越高&#xff09; clc avg_e66;avg_m66;avg_s76; x1[61 63 78 65 63] -avg_e; x2[53 73 61 84 5…

Java关于Properties用法的总结(一)

最近项目中有一个这样的需求&#xff0c;要做一个定时任务功能&#xff0c;定时备份数据库的操表&#xff0c;将表数据写入txt文件。因为文件的读写路径可能需要随时改动&#xff0c;所以写死或者写成静态变量都不方便&#xff0c;就考虑使用配置文件&#xff0c;这里总结些配置…

【tensorflow】——tensorboard可视化计算图以及参数曲线图loss图

参考文献&#xff1a; https://zhuanlan.zhihu.com/p/71328244 目录 1.可视化计算图 2.可视化参数 3. 远程tensorboard 4、报错 真是出来混迟早是要还的&#xff0c;之前一直拒绝学习Tensorboard&#xff0c;因为实在是有替代方案&#xff0c;直到发现到了不得不用的地步…

Lab01:Xv6 and Unix utilities

实验测试方法 实验的测试方法主要有2个&#xff1a; 进入到Xv6系统中&#xff0c;执行相应的命令使用实验提供的评分测试 对于单个实验&#xff0c;可以使用 make GRADEFLAGSapplication grade其中application为要测试的实验应用&#xff0c;例如sleep实验对应的评分测试命令…

jQuery学习- 位置选择器

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>位置选择器</title><script src"js/jquery.js"></script><script type"text/javascript">$(function(){//获取第一个li$(&quo…

数据类型之元组

存多个值&#xff0c;对比列表来说&#xff0c;元组不可变&#xff08;是可以当做字典的key的&#xff09;&#xff0c;主要是用来读 与列表类型比&#xff0c;只不过[]换成()age(11,22,33,44,55) #本质agetuple((11,22,33,44,55)) print(type(age)) age[0]12 t(1,2,[a,b]) pri…

cocos2d-x3.6 连连看连通画线

我的博客&#xff1a;http://blog.csdn.net/dawn_moon 网上看到非常多人写的连连看&#xff0c;都没有画连线的实现。事实上要话连线挺简单的。cocos2d-x 提供了一个非常方便的绘图形的类。DrawNode。这个类封装了非常多画线条&#xff0c;多边形的方法。非常方便&#xff0c;非…

阿里云大数据计算服务MaxCompute(上篇)

关于阿里云大数据计算服务MaxCompute的详细内容&#xff1a; 阿里云大数据计算服务MaxCompute使用教程 &#xff08;MaxCompute&#xff08;原ODPS&#xff09;是一项大数据计算服务&#xff0c;它能提供快速、完全托管的PB级数据仓库解决方案&#xff0c;使您可以经济并高效的…

Vue3、TypeScript 实现图片数量及大小随宽度自适应调整

前言 过了这么久&#xff0c;想起自己还有个博客&#xff0c;更点内容吧&#xff01; 来&#xff0c;上需求&#xff01; 最近在做个前端界面&#xff0c;要求在一行中展示一些图片&#xff0c;展示的图片数量随着窗口宽度大小进行变化&#xff0c;除此之外还有以下要求&…