1. 程式人生 > >C庫:rand和srand的實現原理以及C庫中原始碼

C庫:rand和srand的實現原理以及C庫中原始碼

一、rand和srand的使用示例

博主前言:對rand和srand函式使用熟練的人,這步可直接跳過不看。

1.程式碼示例1和執行結果

//只有rand,沒有srand生成隨機種子
#include<stdio.h>
#include<unistd.h>

void test()
{
    int i=0;
    for(;i<10;i++)
    {
        printf("%d\n",rand()%10);//列印09
        sleep(1);
    }
}

int main()
{
    test();
    return 0;
}

這裡寫圖片描述

2.程式碼示例2和執行結果

//有srand生成隨機種子
#include<stdio.h>
#include<unistd.h>

void test()
{
    int i=0;
    for(;i<10;i++)
    {
        printf("%d\n",rand()%10);//列印09
        sleep(1);
    }
}

int main()
{
    srand(time(NULL));
    test();
    return 0;
}

這裡寫圖片描述

二、rand函式使用的“問題”

根據上面兩個程式碼的執行結果不難看出,不加srand函式的rand函式產生的隨機數是偽隨機數


偽隨機數是指randh函式生成的一組序列看似隨機,實際上每次呼叫rand函式後生成的序列都是相同的

三、由“問題”到原理

①在rand函式的內部,是通過一個公式計算出一個值作為隨機值,下次再呼叫rand的時候,再把這個隨機值作為引數傳給這個公式計算出一個新的隨機值,周而復始。
②在C庫中,是通過一個靜態全域性變數來作為“種子”,而這個“種子”的值是通過srand函式改變的,如果不寫srand函式,這個“種子”值預設賦值為1。這就解釋了“為何不寫srand函式,rand函式就會生成偽隨機數”,因為程式只要重新開始執行,“種子”值就會被預設賦值為1,那麼通過公式算出來的序列肯定就一直相同了。

四、模擬實現rand和srand

通過上面對原理的分析,我們不難寫出模擬程式碼,如下:

#include<stdio.h>
#include<unistd.h>

static unsigned long next=1;//靜態全域性變數,作為種子

void my_srand(unsigned long seed)//通過傳不同的引數更改種子值,一般傳time(NULL)
{
    next=seed;
}

int my_rand(void)//將srand更改過的種子值通過公式計算出結果作為隨機值
{
    next = next * 1103515245 + 12345;
    return((unsigned)(next/65536) % 32768);
}

int main()
{
    my_srand(time(NULL));
    int i=0;
    for(;i<10;i++)
    {
        printf("%d\n",my_rand()%10);
        sleep(1);
    }
    return 0;
}

執行結果如下:
這裡寫圖片描述
補充:srand傳參傳time(NULL)的話會有個小缺陷,就是如果同一秒內呼叫兩次或以上rand函式,生成的隨機數序列仍然是相同的。

五、C庫原始碼

#include <cruntime.h>
#include <mtdll.h>
#include <stddef.h>
#include <stdlib.h>

void __cdecl srand (
        unsigned int seed
        )
{
        _getptd()->_holdrand = (unsigned long)seed;
}

int __cdecl rand (
        void
        )
{
        _ptiddata ptd = _getptd();

        return( ((ptd->_holdrand = ptd->_holdrand * 214013L
            + 2531011L) >> 16) & 0x7fff );
}

六、個人的疑惑和答案

這是我當初犯的一個低階錯誤,如果各位沒有過跟我一樣的疑惑,那你們還是比我強的。
疑惑:雖然說沒有srand函式,rand生成的隨機數是偽隨機數,但是第二次呼叫rand會把第一次生成的隨機值傳給公式計算出結果作為新的隨機值。那麼我的程式如下:
int main()
{
printf("%d\n",rand()%10);
return 0;
}

為什麼我多次執行的結果都是同一個值?不應該是把第一次生成的隨機值傳參給公式重新生成一個新的嗎?
答案:“多次執行”,這句話就表明這是重啟程式了,那麼每次執行,“種子”就恢復成初始值1了,所以實際上每次都是把1傳給公式計算,可不就永遠都是同一個數嘛。想要驗證rand產生的是一個序列,得在同一個程式中迴圈列印輸出,而不是“多次執行”。

相關推薦

Crandsrand實現原理以及C原始碼

一、rand和srand的使用示例 博主前言:對rand和srand函式使用熟練的人,這步可直接跳過不看。 1.程式碼示例1和執行結果 //只有rand,沒有srand生成隨機種子 #in

C語言的rand()srand()產生偽隨機數的方法總結

rand()會返回一隨機數值,範圍在0至RAND_MAX 間。在呼叫此函式產生隨機數前,必須先利用srand()設好隨機數種子,如果未設隨機數種子,rand()在呼叫時會自動設隨機數種子為1。srand()用來設定rand()產生隨機數時的隨機數種子。引數seed必須是個整數

Struts2(Interceptor篇)攔截器的實現原理以及程式碼示例

目錄 Interceptor 簡介 理解 Interceptor 概念 理解 Interceptor 原理 建立 Interceptor 監聽器 在pom.xml加入相關依賴 自定義 Interceptor 自定義一個實現了Interceptor介面的類,或者繼承抽象

C/C++雜記虛擬函式的實現的基本原理 虛擬函式表

部落格園 首頁 新隨筆 聯絡 訂閱 管理 1. 概述 簡單地說,每一個含有虛擬函式(無論是其本身的,還是繼承而來的)的類都至少有一個與之對應的虛擬函式表,其中存放著該類所有的虛擬函式對應的函式指標。例: 其中: B的虛擬函式表中存放著B::fo

C++介面Graphic Element Template實現

    這篇文章描述的一個圖形元素模板終於通過了冒煙測試。下面將展示模板的XML程式碼、呼叫模板的程式碼以及截圖。    下面的XML描述了一個黑變藍底的長方形裡面居中一個文字。  1 <?xml version="1.0" encoding="utf-8" ?> 2 <irc

乾貨!!c++newdelete工作原理 以及 針對連結串列節點過載operator new operator delete 實現連結串列節點使用記憶體池申請釋放空間

第一部分: new和delete的實現原理 開始談之前我們應該瞭解另一個概念“operator new”和“operator delete”: new操作符呼叫一個函式來完畢必需的記憶體分配,你可以重寫或過載這個函式來改變它的行為。new操

事件觸發機制Poll,SelectEpoll實現原理分析

Poll和Select和Epoll都是事件觸發機制,當等待的事件發生就觸發進行處理,多用於linux實現的伺服器對客戶端連線的處理。 Poll和Select都是這樣的機制:可以阻塞地同時探測一組支援非阻塞的IO裝置,是否有事件發生(如可讀,可寫,有高優先順序的錯誤輸出,出現

C++—randsrand的用法(簡單易懂版)—產生隨機數

每天進步一點點,目標距離縮小點在C++中,可以使用rand()函式產生隨機數。(rand()函式的標頭檔案在<cstdlib>中)如果想產生在一定範圍內的數,可以用取餘的方法獲得。如想獲得0—100的數同樣的道理,如果想獲得100-200之間的數—————————

C/C++函式strstrfind實現子字串查詢

1 子字串查詢實現Demo #include<iostream> #include<string> #include<cstring> using namesp

C++隨機函式rand()srand()的用法

一、rand()   函式名:   rand     功   能:   隨機數發生器    用   法:   int rand(void);     所在標頭檔案: stdlib.h   函式說明 :                 rand()的內部實現是用線性同餘法做

C++隨機函式rand()srand()的用法(函式講解)

一、rand()   函式名:   rand      功   能:   隨機數發生器    用   法:   int rand(void);     所在標頭檔案: stdlib.h   函式說明 :                 rand()的內部實現是用線

【Java並發編程】之六RunnableThread實現多線程的區別(含代碼)

技術分享 runnable 避免 實際應用 details div 一個 預測 enter 轉載請註明出處:http://blog.csdn.net/ns_code/article/details/17161237 Java中實現多線程有兩種方法:繼承Thre

Spring技術內幕Spring AOP的實現原理(三)

dede ide configure ida mini == src min dem 生成SingleTon代理對象在getSingleTonInstance方法中完畢,這種方法時ProxyFactoryBean生成AopProxy對象的入口。代理對象會

前端必備FastStoneCapture Licecap &&& c++ 編譯執行

bsp alt 屏幕 軟件 時間 apt 博客 png 錄像 前端必備:FastStoneCapture 和 Licecap   FastStoneCapture這個軟件非常小,只有2M多,並且其功能很強大,包括截圖,錄制視頻,量尺,取色等等,對於前端工程師絕對是必備神器。

Java並發機制底層實現原理

差距 32處理器 們的 trac 結點 exce jdk cep 定性   Java代碼在編譯後會變成Java字節碼,字節碼被類加載器加載到JVM裏,JVM執行字節碼轉化為匯編指令在CPU上執行。Java中的並發機制依賴於JVM的實現和CPU的指令。      Java語言

3.C#知識點isas

true color 轉換成 lec post test using line ask IS和AS 都是用於類型轉換的操作。 但是這兩個有什麽區別呢? 簡單的來說 is 判斷成立則返回True,反之返回false。as 成立則返回要轉換的對象,不成立則返回Null。 下面掏

Java日誌框架slf4j作用及其實現原理

sof cat 打開 系統 aging .get matching ade you 簡單回顧門面模式 slf4j是門面模式的典型應用,因此在講slf4j前,我們先簡單回顧一下門面模式, 門面模式,其核心為外部與一個子系統的通信必須通過一個統一的外觀對象進行,使得子系統更易於

【TOJ 5247】C++實驗時間日期類

OS 日期類 一行 ID 表示 pac 日期 style pub 描述 用C++實現日期類CDate和時間類CTime,並在次基礎上利用多繼承實現日期時間類CDateTime,使其能輸出樣例信息。 主函數裏的代碼已經給出,請補充完整,提交時請勿包含已經給出的代碼。 int

分布式數據中間件DDM的實現原理

事務 如何 分布式數據庫 customer 查詢效率 分庫 關心 都是 單個 隨著數據量不斷增大,傳統的架構模式難以解決業務量不斷增長所帶來的問題,特別是在業務成線性、甚至指數級上升的情況。此時我們不得不通過水平擴展,把數據庫放到不同服務器上來解決問題,也就是我們說的數據庫

JMM底層實現原理

現代計算機物理上的記憶體模型 物理機遇到的併發問題與虛擬機器中的情況有不少相似之處,物理機對併發的處理方案對於虛擬機器的實現也有相當大的參考意義。 其中一個重要的複雜性來源是絕大多數的運算任務都不可能只靠處理器“計算”就能完成,處理器至少要與記憶體互動,如讀取運算資料、儲存運算結果