最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

C/C++編程筆記:C語言 rand() 隨機函數(shù),深入解析程序隨機數(shù)!

2020-07-02 22:53 作者:C語言編程__Plus  | 我要投稿

各種編程語言返回的隨機數(shù)(確切地說是偽隨機數(shù))實際上都是根據(jù)遞推公式計算的一組數(shù)值,當序列足夠長,這組數(shù)值近似滿足均勻分布。

C的標準函數(shù)庫提供一隨機數(shù)生成器rand(定義在stdlib.h),能返回0~RAND_MAX之間均勻分布的偽隨機整數(shù)(RAND_MAX至少為32767,一般都默認為32767)。

用rand()隨機生成在[x,y]內的整數(shù)

int k;

k=x+rand()%(y-x+1),k即為所求范圍內隨機生成的數(shù),rand()%a的結果最大為a-1.

rand(?)%20的意思的生成20以內的隨機數(shù)。

例如:

#include

#include

void main()

{

for(int i=0;i<10;i++)

printf("%d\n",rand());

}

如果我們是第一次運行,而且對其不太清楚,那么它生成的基本上算是0-RAND_MAX之間的等概率隨機數(shù)列了。但是如果你第二次運行的時候會發(fā)現(xiàn)輸出結果仍和第一次一樣。

原來rand()生成偽隨機數(shù)時需要一個種子(計算偽隨機序列的初始數(shù)值)才行,如果種子相同就會得到相同的序列結果(這就是函數(shù)的好處T-T)。這個“優(yōu)點”被有的軟件利用于加密和解密。加密時,可以用某個種子數(shù)生成一個偽隨機序列并對數(shù)據(jù)進行處理;解密時,再利用種子數(shù)生成一個偽隨機序列并對加密數(shù)據(jù)進行還原。這樣,對于不知道種子數(shù)的人要想解密就需要多費些事了。當然,這種完全相同的序列對于你來說是非常糟糕的。要解決這個問題,需要在每次產(chǎn)生隨機序列前,先指定不同的種子,這樣計算出來的隨機序列就不會完全相同了。


srand()用來設置rand()產(chǎn)生隨機數(shù)時的隨機數(shù)種子。在調用rand()函數(shù)產(chǎn)生隨機數(shù)前,必須先利用srand()設好隨機數(shù)種子seed,如果未設隨機數(shù)種子,rand()在調用時會自動設隨機數(shù)種子為1(有人說默認是0,困惑中)。上面的兩個例子就是因為沒有設置隨機數(shù)種子,每次隨機數(shù)種子都自動設成相同值1,進而導致rand()所產(chǎn)生的隨機數(shù)值都一樣。(可能有人知道C語言中的隨機函數(shù)random,可是random函數(shù)并不是ANSIC標準,所以說,random函數(shù)不能在gcc,vc等編譯器下編譯通過。我們可以自己編一個^0^)我們需要使程序每一次使用的種子都不一樣,現(xiàn)在主要問題是種子srand的選擇是不是接近隨機(不存在完全隨機),你也可以人為指定種子數(shù)。

Windows9x/NT的游戲FreeCell就允許用戶指定種子數(shù),這樣用戶如果一次游戲沒有成功,下次還可以以同樣的發(fā)牌結果再玩一次。但我們還是喜歡系統(tǒng)自動生成……最簡單的方法就是利用系統(tǒng)時間了(幾乎所有的人都這么做),因為時間的數(shù)值隨時間變化而變化,運行兩次,一般不會出現(xiàn)前一次和后一次相同的局面,time(NULL)會返回一個表示當前系統(tǒng)時間的整數(shù)(它在time.h中,據(jù)說time()函數(shù)求出來的是自1970年1月1日到現(xiàn)在的秒數(shù),有的說是unix元年,不知道這兩個是不是一天……:(,另外有的嫌麻煩會寫作time(0))。我們這么來寫:

#include

#include

#include

void main()

{

srand((int)time(0));

for(int x=0;x<10;x++)

printf("%d\n",(rand());

}

?據(jù)說如果軟件一直開兩天,種子會有1/(60*60*24)個可能會重復,雖說這對于一般人已經(jīng)絕對足夠了,但縱然人不舒服,于是我在網(wǎng)上開到有這么寫的:

#include

#include

#include

#include

void main(void)

{

int i;

unsigned int seedVal;

struct timeb timeBuf;

ftime(&timeBuf);

seedVal=((((unsigned int)timeBuf.time&0xFFFF)+

(unsigned int)timeBuf.millitm)^

(unsigned int)timeBuf.millitm);

srand((unsigned int)seedVal);

for(i=0;i<10;++i)

printf("%6d\n",rand());

}

“上面的程序先是調用_ftime()來檢查當前時間,并把它的值存入結構成員timeBuf.time中,當前時間的值從1970年1月1日開始以秒計算。在調用了_ftime()之后,在結構timeBuf的成員millitm中還存入了當前那一秒已經(jīng)度過的毫秒數(shù),但在DOS中這個數(shù)字實際上是以百分之一秒來計算的。然后,把毫秒數(shù)和秒數(shù)相加,再和毫秒數(shù)進行異或運算。當然也可以對這兩個結構成員進行更多的計算,以控制seedVal的取值范圍,并進一步加強它的隨機性,但上例用的邏輯運算就已經(jīng)足夠了?!?/p>

看下面代碼:

void main()

{

for(int i=0;i<100000;i++)

{

srand( (unsigned)time( NULL ) );

printf("%d\n",rand());

}

}

為什么總是生成一個數(shù)???因為此程序產(chǎn)生一個隨機數(shù)之前,都調用一次srand,而由于計算機運行很快,所以每用time得到的時間都是一樣的(time的時間精度較低,只有55ms)。這樣相當于使用同一個種子產(chǎn)生隨機序列,所以產(chǎn)生的隨機數(shù)總是相同的。把srand放在循環(huán)外看看:

srand( (unsigned)time( NULL ) );

for(int i=0;i<100000;i++)

問題解決了:)

既然生成的是

0-RAND_MAX之間均勻分布的隨機整數(shù)(我們姑且把以上方法生成的是理想的隨機數(shù)吧),那么要生成0-x之間(包括0不包括x)的隨機數(shù)就把rand()改成?

rand()/(double)RAND_MAX*x ,要生成x-y之間(包括x不包括y)的就是?

rand()/(double)RAND_MAX*(y-x)+x?? 了。但是如果要生0-10之間的整數(shù)很多人會這么寫:

#include

#include

#include

void main()

{

srand((int)time(0));

for(int x=0;x<10;x++)

printf("%d\n",(rand()%10);

}

x-y的就是 rand()%(y-x)+x

???懂一點概率的就知道這樣寫,會使得到的數(shù)列分布不均的,但是大家確實都喜歡這么寫……因為在x的值比較小,RAND_MAX相對較大,而生成的數(shù)列有不算太大,又是用來解決精確度要求不高的問題(如一些游戲目標,傳說游戲中踩地雷式的遇敵就是用rand()實現(xiàn)的.

當主角在地圖上走的時候動不動冒出三兩小兵挑釁兼找死.它的實現(xiàn)方式是設主角所立位置為0,主角每走一步,變量加1,當變量==隨機取得的數(shù)時,小兵出現(xiàn)),這樣寫足夠了……


下面再討論生成m個小于n的不重復隨機數(shù)的算法

本人認為算法結構很重要,但語句結構也很重要,所以我喜歡一個算法給出多個語句結構……

最容易想到的傻瓜算法,逐個產(chǎn)生這些隨機數(shù),每產(chǎn)生一個,都跟前面的隨機數(shù)比較,如果重復,就重新產(chǎn)生:

算法一(1)

srand((unsigned)time(NULL));

for(j = 0; j < m; j++) {

a:a[j] = rand() % n;

for(i=0;i

很早的時候學編程都喜歡用goto語句,因為過去算法是用流程圖表示的,用goto可以直接轉譯,而且循環(huán)語句發(fā)展不完善,僅僅是作為條件分支的一個補充,如果條件分支箭頭向上就是一個循環(huán),而且goto可以實現(xiàn)過去循環(huán)所不能實現(xiàn)的結構,形成很巧妙的循環(huán)交叉。所以過去一般認為有兩種結構,順序和分支,循環(huán)是分支的特殊情況。但是break和continue語句使這些結構實現(xiàn)成為可能,后來發(fā)現(xiàn)goto的使用會造成維護和調試的許多麻煩,所以循環(huán)逐漸代替了goto,使程序更有層次?,F(xiàn)在編程不建議用goto了,如果用goto還會被認為編程修養(yǎng)不好……言歸正題,把它的goto去掉:

算法一(2)

srand((unsigned)time(NULL));

j=0;

while (j

{

a[j] = rand() % n ;

for(i=0;i

if(i

j++

}

?但是后來都建議用for循環(huán),這樣可以使循環(huán)條件跟緊湊:

算法一(3)

srand((unsigned)time(NULL));

for(j=0;j<m;j++)

{a[j]=rand()%n;

?for(i=0;i<j;i++)

?{if(a[i]==a[j])

?? {i=-1;a[j]=rand()%n;}

?}

}

但這是個很笨的方法,且比較次數(shù)呈線性增長,越往后次數(shù)越多……另一個想法是生成一個就把它從中去掉,就可以實現(xiàn)不重復了:

算法二(1)

?for (i=0;i<n;i++)

???? a[i]=i+1;

for(i=0;i<m;i++)

{j=rand()%(n-i);

?b[i]=a[j];

?for (k=j;k<n-i-2;k++)

?a[k]=a[k+1];

?}

b[]是生成的隨機數(shù)列

?這樣做也太麻煩了,生成的直接做個標記不就行了?

算法三(1-1)

i=rand()%m;

a[i]=1;b[0]=i;

for(j=1;j<m;j++)

{for (i=rand()%m;a[i]==1;i=rand()%m);

?b[j]=i;a[i]=1;

}

寫緊湊一些吧,直接:

算法三(1-2)

for(j=0;j<m;j++)

{for (i=rand()%m;a[i]==1;i=rand()%m);

?b[j]=i;a[i]=1;

}

但是我看到了更簡潔的:

int n=某值;

int a[n]={0};

for (i=1;i<=n;i++)

{while(a[x=rand()%n]);

? a[x]=i;

}

這個無循環(huán)體的while保證a[m]是初始化的0狀態(tài)。這生成了n個,我們只取m個,這太浪費了,結合一下:

int n=某值,m=某值;

int a[n]={0},b[m]

for (i=1;i<=n;i++)

{while(a[x=rand()%n]);

? b[i]=x;a[x]=1;

}

但是總叫人不舒服,對這種算法誰有更好的寫法呢?

這種算法越到后面,遇到已使用過的元素的可能性越高,重復次數(shù)就越多,但是當m較小時還是很好的:)

這都不太讓人滿意么?看看下面這個:

算法四(1)

for (i=0;i<n;i++)

? a[i]=i+1;

for (i=n-1;i>0;i--)

{w=rand()%i;

?t=a[i];

?a[i]=a[w];

?a[w]=t;

}

這個算法很不錯,有人會懷疑其隨機性,但個人認為是沒問題的,首先第二行按順序用0到n填滿整個數(shù)組;第三行,是隨機產(chǎn)生從0到n-2個數(shù)組下標,把這個下標的元素值跟n-1下標的元素值交換,一直進行到下標為1的元素。因此它只需要遍歷一次就能產(chǎn)生全部的隨機數(shù)。

但這樣會生成n個小于n的隨機數(shù),我們只要m個,再加兩句:

for(i=0;i<m;i++)

?b[i]=a[i];

b[]是生成的隨機數(shù)列

?如果m和n很接近的話或者相等是最好,但可能不是……那改一下:

算法四(2)

for (i=0;i<n;i++)

? a[i]=i+1;

for (i=0;i<m;i++)

{w=rand()%n;

?t=a[i];

?a[i]=a[w];

?a[w]=t;

}

但是條件反過來了,如果m遠小于n還行,如果接近,其隨機性就存在問題了(為什么?),再改一下:

算法四(3)

for (i=0;i<n;i++)

? a[i]=i+1;

for (i=0;i<m;i++)

{w=rand()%(n-i)+i;

?t=a[i];

?a[i]=a[w];

?a[w]=t;

}

這樣可以萬無一失了……

希望對大家有幫助!

學習C/C++編程知識,提升C/C++編程能力,歡迎關注UP一起來成長!
另外,UP在主頁上傳了一些學習C/C++編程的視頻教程,有興趣或者正在學習的小伙伴一定要去看一看哦!會對你有幫助的~


C/C++編程筆記:C語言 rand() 隨機函數(shù),深入解析程序隨機數(shù)!的評論 (共 條)

分享到微博請遵守國家法律
河南省| 新巴尔虎左旗| 松阳县| 桐城市| 灵山县| 山阳县| 海盐县| 明水县| 临清市| 巴南区| 平潭县| 民权县| 湖南省| 岳阳市| 榆林市| 怀远县| 光泽县| 九江市| 从江县| 子洲县| 三原县| 佛坪县| 台北县| 黄龙县| 临安市| 错那县| 开原市| 左权县| 阿城市| 始兴县| 扬州市| 巴青县| 左贡县| 贡嘎县| 双柏县| 长宁县| 玛曲县| 崇文区| 宝山区| 沛县| 无为县|