封裝不同類模板的隨機數生成器

最近準備刷題,打算簡單封裝下隨機數生成器,方便產生測試數據。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.pswp.cn/news/256009.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/256009.shtml
英文地址,請注明出處:http://en.pswp.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;除此之外還有以下要求&…