摘要:返回非負隨機數大于或等于零且小于的位帶符號整數。返回一個指定范圍內的隨機數返回的隨機數的下界隨機數可取該下界值。其中參數是下限,參數是上限,生成的隨機數。如,結果相當于從序列中獲取一個隨機數。
你真的懂隨機數?
Author : Jasper Yang
School : Bupt
Q:為什么要寫這篇文章?
A:因為我發現在最近的科學計算中,常常遇到隨機數,所有的隨機數都是基于0,1隨機,而這個0,1隨機怎么實現呢?下面我會娓娓道來~
這篇文章不同于網路上的雜散的技術文,我是針對 random 這么一個論題展開調研最后將所有相關的知識進行整理敘述,希望每個人看完都可以得到小小的提升~
& 什么是隨機數隨機數:數學上產生的都是偽隨機數,真正的隨機數使用物理方法產生的
隨機數種子:隨機數的產生是由算術規則產生的,在c++中,srand(seed)的隨機數種子不同,rand()的隨機數值就不同,倘若每次的隨機數種子一樣,則rand()的值就一樣。所以要產生隨機數,則srand(seed)的隨機數種子必須也要隨機的。在 python 中就是 random.seed()來設置種子。
下面我講的隨機數不僅僅講隨機數生成的原理,也會講在python中以及在c++中怎么去實現,當然,大部分資料也都是網上找的,我只是做了一個整理匯總,并用自己的語言加以敘述。
& 隨機數的原理這里我看了一篇博客,由于這篇博客是那個博主轉的,但是該博主并沒有表明是從哪里轉來的,我就不po出鏈接了,大家往下看~
有位朋友問那博主關于一段程序的錯誤。
</>復制代碼
C/C++ code
for (int i =0;i< n;++i)
{
srand((unsigned)time( NULL ));
int r = rand()%100;
cout << r << ",";
}
這里很明顯他是想輸出一串小于100的隨機的數列.可是運行結果輸出的卻是類似 97,97,97,97,....97,30,30,30,30,30,30,30,30,30,30,30,30,....,27,27,27,27,27,27,....的序列.很明顯這樣完全看不出有任何的隨機性.這是由于他對C的rand函數不理解導致的錯誤用法.而這兩天逛C#區我也同樣看到了幾個類似的錯誤用法(C和C#的rand從大體的原理上差不多).想想自己初學的時候類似的錯誤犯得也不少.所以自己下去查了寫資料總結了在隨機數使用上的一些錯誤的用法.希望能對初學者有所幫助。
現在各種語言中的隨機數產生函數所產生的"隨機數",實際上被稱之為"偽隨機數".可以將
整個隨機數函數看做這樣一個表達式:
$$A = R(s)$$
其中R是隨機函數,s是種子.A是一個數列.即對于任意一個種子s,經過R的計算后,總有一個確定的數列A與之對應.而當在C#里調用var rnd = new Random (s)或在C里調用srand(s)實質上所做工作之一就是設定這個種子.而rnd.Next();或rand()只不過是在A上取下一個元素而已.當然實際的實現不可能事先計算一個數列A,所以rand()相當于由s計算出下一個數字s",然后將s"作為新的種子賦值給s,最后將s"作為結果返回。
往細了講,就是這樣。
如果約定:$a_1=f(seed),a_{n+1}=f(an)$
那你可以行到一個序列:$a_1,a_2,a_3...a_n$,那么要制作一個偽隨機函數rand,只需要讓它每調用一次就返回序列的下一個元素就行。
下面是兩種常見的錯誤做法
</>復制代碼
C# code
for (int i=0;i
這樣,每次使用Random,都去申請了一個變量rnd,然后才用這個變量去找隨機數(rnd.Next())。這樣其實就是在隨機數的序列中總是在找第一個。這樣下來,第一個數肯定是固定的,就不存在什么隨機數了。
第二種情況更加常見。
</>復制代碼
C# code
for (int i=0;i
之前的第一種情況使用了一個固定的常數s來做種子,這里選用了系統時間做種子,想要達到隨機的效果,但是得到的結果往往就會是和博主那位朋友一樣的結果97,97,97,97,....97,30,30,30,30,30,30,30,30,30,30,30,30,....,27,27,27,27,27,27,.... 。
這是因為Windows系統時鐘的更新頻率大概在10ms左右.而這個for循環的執行顯然要快
得多.于是在一段執行時間內Environment.TickCount (Random的默認種子)或是C的time函數返回的都是同一個值.從而導致rnd.Next在一段時間內返回一個常數。
所以正確的做法應該是把種子移出循環之外。
</>復制代碼
C# code
var rnd = new Random ();//用系統時間作為種子
for (int i=0;i
各種庫中是怎么實現隨機數呢?
在 Linux 下實現的方式類似如下
</>復制代碼
static unsigned long next = 1;
/* RAND_MAX assumed to be 32767 */
int myrand(void) {
next = next * 1103515245 + 12345;
return((unsigned)(next/65536) % 32768);
}
void mysrand(unsigned seed) {
next = seed;
}
myrand、mysrand分別對應rand和srand,但實際的rand實現會復雜一些。
下面是這位博主實現的方式,其實挺簡單的,我們每個人都可以實現一種自己想要的隨機數方式加到自己的私有庫中~
</>復制代碼
*
* Copyright (c) 2008 Microsoft::Tsorgy.Utils, Reserved.
*
* Filename: @(#)Random.cs
* Create by: TsOrgY
* Email: tsorgy@gmail.com
* Date: 2008/12/27 15:01:40
*
* Classname: Random
* Description: 一種能夠產生滿足某些隨機性統計要求的數字序列的設備.
*
*/
using System;
using System.Runtime.InteropServices;
namespace Tsorgy.Utils {
///
/// 表示偽隨機數生成器,一種能夠產生滿足某些隨機性統計要求的數字序列的設備.
///
[Serializable]
[ComVisible(true)]
public class Random {
private int inext;
private int inextp;
private const int MBIG = 0x7fffffff;
private const int MSEED = 0x9a4ec86;
private const int MZ = 0;
private int[] SeedArray;
///
/// 使用與時間相關的默認種子值,初始化 Random 類的新實例.
///
public Random()
: this(Environment.TickCount) {
}
///
/// 使用指定的種子值初始化 System.Random 類的新實例.
///
/// 用來計算偽隨機數序列起始值的數字。如果指定的是負數,則使用其絕對值。
/// Seed 為 System.Int32.MinValue,在計算其絕對值時會導致溢出。
public Random(int Seed) {
this.SeedArray = new int[0x38];
int num2 = 0x9a4ec86 - Math.Abs(Seed);
this.SeedArray[0x37] = num2;
int num3 = 1;
for (int i = 1; i < 0x37; i++) {
int index = (0x15 * i) % 0x37;
this.SeedArray[index] = num3;
num3 = num2 - num3;
if (num3 < 0) {
num3 += 0x7fffffff;
}
num2 = this.SeedArray[index];
}
for (int j = 1; j < 5; j++) {
for (int k = 1; k < 0x38; k++) {
this.SeedArray[k] -= this.SeedArray[1 + ((k + 30) % 0x37)];
if (this.SeedArray[k] < 0) {
this.SeedArray[k] += 0x7fffffff;
}
}
}
this.inext = 0;
this.inextp = 0x15;
Seed = 1;
}
private double GetSampleForLargeRange() {
int num = this.InternalSample();
if ((((this.InternalSample() % 2) == 0) ? 1 : 0) != 0) {
num = -num;
}
double num2 = num;
num2 += 2147483646.0;
return (num2 / 4294967293);
}
private int InternalSample() {
int inext = this.inext;
int inextp = this.inextp;
if (++inext >= 0x38) {
inext = 1;
}
if (++inextp >= 0x38) {
inextp = 1;
}
int num = this.SeedArray[inext] - this.SeedArray[inextp];
if (num < 0) {
num += 0x7fffffff;
}
this.SeedArray[inext] = num;
this.inext = inext;
this.inextp = inextp;
return num;
}
///
/// 返回非負隨機數.
///
/// 大于或等于零且小于 System.Int32.MaxValue 的 32 位帶符號整數。
public virtual int Next() {
return this.InternalSample();
}
///
/// 返回一個小于所指定最大值的非負隨機數.
///
/// 要生成的隨機數的上界(隨機數不能取該上界值)。maxValue 必須大于或等于零。
/// 大于或等于零且小于 maxValue 的 32 位帶符號整數,即:返回的值范圍包括零但不包括 maxValue。
/// maxValue 小于零。
public virtual int Next(int maxValue) {
if (maxValue < 0) {
throw new ArgumentOutOfRangeException("maxValue", string.Format(""{0}" must be greater than zero.", maxValue));
}
return (int) (this.Sample() * maxValue);
}
///
/// 返回一個指定范圍內的隨機數.
///
/// 返回的隨機數的下界(隨機數可取該下界值)。
/// 返回的隨機數的上界(隨機數不能取該上界值)。maxValue 必須大于或等于 minValue。
/// 一個大于或等于 minValue 且小于 maxValue 的 32 位帶符號整數,即:返回的值范圍包括 minValue 但不包括 maxValue。如果minValue 等于 maxValue,則返回 minValue。
/// minValue 大于 maxValue。
public virtual int Next(int minValue, int maxValue) {
if (minValue > maxValue) {
throw new ArgumentOutOfRangeException("minValue", string.Format(""{0}" cannot be greater than {1}.", minValue, maxValue));
}
long num = maxValue - minValue;
if (num <= 0x7fffffffL) {
return (((int) (this.Sample() * num)) + minValue);
}
return (((int) ((long) (this.GetSampleForLargeRange() * num))) + minValue);
}
///
/// 用隨機數填充指定字節數組的元素.
///
/// 包含隨機數的字節數組。
/// buffer 為 null。
public virtual void NextBytes(byte[] buffer) {
if (buffer == null) {
throw new ArgumentNullException("buffer");
}
for (int i = 0; i < buffer.Length; i++) {
buffer[i] = (byte) (this.InternalSample() % 0x100);
}
}
///
/// 返回一個介于 0.0 和 1.0 之間的隨機數.
///
/// 大于或等于 0.0 而小于 1.0 的雙精度浮點數字。
public virtual double NextDouble() {
return this.Sample();
}
///
/// 返回一個介于 0.0 和 1.0 之間的隨機數.
///
/// 大于或等于 0.0 而小于 1.0 的雙精度浮點數字。
protected virtual double Sample() {
return (this.InternalSample() * 4.6566128752457969E-10);
}
}
}
這里我要另外提到一個大家聽到了很多次的東西 ------------> 線性同余法
這也是實現隨機數的一種方式
線性同余方法(LCG)
它的遞歸公式:
$$N_{j+1} = (A * N_j +B) (mod M)$$
其中A,B,M是產生器設定的常數。
LCG的周期最大為M,但大部分情況都會少于M。要令LCG達到最大周期,應符合以下條件:
B,M互質
M的所有質因子的積能整除A-1
若M是4的倍數,A-1也是
A,B,$N_0$都比M小
A,B是正整數
最后生成的就是一個 <$N_i$> 序列,這個序列應該滿足下面的幾個條件。
這個函數應該是一個完整周期的產生函數。也就是說,這個函數應該在重復之前產生出0 到m之間的所有數
產生的序列應該看起來是隨機的
這個函數應該用32bit 算術高效實現
實現
</>復制代碼
#include
#include
static unsigned long rand_seed;
void mysrand (unsigned long int);
void myrand ();
int
main (void)
{
int i;
mysrand (time (NULL));
for (i = 0; i < 100; i++)
{
myrand ();
}
return 0;
}
void
mysrand (unsigned long seed)
{
rand_seed = seed;
}
void
myrand ()
{
rand_seed = (rand_seed * 16807L) % ((1 << 31) - 1);
printf ("%ld ", rand_seed);
}
可以看到,這個實現和上面提到的 linux 的實現很像,其實就是一樣的。
& 隨機數使用
因為最近用的c++和python特別的多(我覺得這兩個語言是程序員們最需要掌握的兩種語言,別的都是補充 ~:)),所以下面我就只講這兩種語言的實現方式。
c++
實例程序
</>復制代碼
#include "stdafx.h"
#include
#include
int _tmain(int argc, _TCHAR* argv[])
{
// 初始化隨機數種子
// time函數返回從1970年1月1日零時零分零秒到目前為止所經過的時間,單位為秒
srand((int)time(NULL));
int j;
for (int i = 0; i < 10; i++) {
j = (rand() * 10) / RAND_MAX + 1; // 生成1~10之間的隨機數
printf("j = %d
", j);
}
unsigned start = (rand() * 1000)/ RAND_MAX + 15550; // 生成15550~16549之間的隨機數
printf("start = %d
", start);
start &= ~1; // 把start變為偶數,如果是奇數,則start變為start - 1的偶數
printf("start = %d
", start);
getchar();
return 0;
}
c++ 其實就是 srand 和 rand 兩個函數。
上面的都只是生成的整數,如果需要浮點數什么的就需要自己再加以處理,而在python中提供了比較多的函數。
python
這塊的內容是 Capricorn的實驗室的整理。其實這塊內容直接去官網的doc翻譯就可以了,但是我有點懶,不太想去看了,就用了這篇博文的內容~
random.random
random.random()用于生成一個0到1的隨機符點數: 0 <= n < 1.0
random.uniform
random.uniform的函數原型為:random.uniform(a, b),用于生成一個指定范圍內的隨機符點數,兩個參數其中一個是上限,一個是下限。如果a>b,則生成的隨機數n: a <= n <= b。如果 $a
random.randint
random.randint()的函數原型為:random.randint(a, b),用于生成一個指定范圍內的整數。其中參數a是下限,參數b是上限,生成的隨機數n: a <= n <= b。
random.randrange
random.randrange 的函數原型為:random.randrange([start], stop[, step]),從指定范圍內,按指定基數遞增的集合中 獲取一個隨機數。如:random.randrange(10, 100, 2),結果相當于從[10, 12, 14, 16, ... 96, 98]序列中獲取一個隨機數。random.randrange(10, 100, 2)在結果上與 random.choice(range(10, 100, 2) 等效。
random.choice
random.choice從序列中獲取一個隨機元素。其函數原型為:random.choice(sequence)。參數sequence表示一個有序類型。這里要說明 一下:sequence在python不是一種特定的類型,而是泛指一系列的類型。list, tuple, 字符串都屬于sequence。有關sequence可以查看python手冊數據模型這一章。下面是使用choice的一些例子:
</>復制代碼
print random.choice("學習Python")
print random.choice(["JGood", "is", "a", "handsome", "boy"])
print random.choice(("Tuple", "List", "Dict"))
random.shuffle
random.shuffle的函數原型為:random.shuffle(x[, random]),用于將一個列表中的元素打亂
random.sample
random.sample的函數原型為:random.sample(sequence, k),從指定序列中隨機獲取指定長度的片斷。sample函數不會修改原有序列。
OK,告一段落了~,朋友們,有沒有覺得進步了一點點呢~
</>復制代碼
paper done 2017/05/13
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/40631.html
摘要:是你學習從入門到專家必備的學習路線和優質學習資源。的數學基礎最主要是高等數學線性代數概率論與數理統計三門課程,這三門課程是本科必修的。其作為機器學習的入門和進階資料非常適合。書籍介紹深度學習通常又被稱為花書,深度學習領域最經典的暢銷書。 showImg(https://segmentfault.com/img/remote/1460000019011569); 【導讀】本文由知名開源平...
摘要:正文總所周知,和根本就是兩個東西,每次因為這個兼容性的問題都會把自己搞瘋。提供了模塊,把下一個新版本的特性導入到當前版本,于是我們就可以在當前版本中測試一些新版本的特性。傳送門不多,才個。 寫在前面 我是在學習cs231n的assignment3的課程,發現里面的代碼大量頻繁出現了這個庫,那我就很奇怪了,為什么有個future這個奇怪名字的庫會出現呢?到底這個庫又有什么用?下面就讓我為...
摘要:由設計,作為編程語言的繼承者,于年首次發布。表達式表達式是編程語言中的語法實體,可以對其進行評估以確定其值。它是編程語言解釋和計算以產生值的常量變量函數和運算符的組合。它在年年年和年被評為年度編程語言,是唯一四次獲得該獎項的語言。 ...
閱讀 1765·2021-11-18 13:20
閱讀 1157·2021-10-11 10:59
閱讀 2993·2021-08-24 10:01
閱讀 3503·2019-08-29 14:21
閱讀 3356·2019-08-29 14:15
閱讀 3521·2019-08-26 12:23
閱讀 3347·2019-08-26 11:46
閱讀 3353·2019-08-26 11:35
极致性价比!云服务器续费无忧!
Tesla A100/A800、Tesla V100S等多种GPU云主机特惠2折起,不限台数,续费同价。
NVIDIA RTX 40系,高性价比推理显卡,满足AI应用场景需要。
乌兰察布+上海青浦,满足东推西训AI场景需要