前言
ns-3是離散事件仿真平台,它由內核部分和常用模塊兩個部分組成。它的內核是用C++實現的。可以在src/core目錄下查看,也可以在ns3的在線doxygen文檔中查閱。
內核包含很多部分,實現了很多底層API供用戶使用。因為仿真中經常需要模擬現實環境中的不確定行為,因而隨機數機制是ns-3中非常重要的部分。了解隨機數機制對仿真模擬真實隨機情況非常重要,本文將詳細講解關於ns-3隨機數的內容。
本文之后的部分將按照以下章節展開:
1.隨機數生成器的背景
2.ns-3中的偽隨機數生成器(PRNG,Pseudo Random Number Generator)
3.ns-3中常用的隨機變量()
在第1點中將結合ns-3討論常見的隨機數生成器;在第2點中首先簡單講述ns-3中PRNG的特性,然后講述ns-3中PNRG的常見使用方法;在第3點中介紹ns-3中常用的隨機變量,包括滿足均勻分布、正態分布、等差分布等的隨機變量
一、背景:隨機數生成器RNG
在了解ns-3中隨機數機制之前,如果你之前對隨機數生成器(RNG,Random Number Generator)理解的不是很清楚,非常有必要了解什么是隨機數生成器。
計算機是按照事先確定的算法和程序進行確定的運算,都是確定性的計算。那么計算機到底是怎么得到隨機數的?作為人類,我們大可提筆隨便在紙上寫一大串數字,也許就算是隨機數了,但是計算機可沒有這本事,它必須有一個科學穩定的隨機數來源,才能得到隨機數,這個來源,我們稱為隨機數生成器。
作為編程愛好者,應該會發現,每一門編程語言必然會有自己的隨機數生成函數,常用的比如:C語言stdlib庫中的rand()函數,java中Random類中的nextInt () 方法,Python中random模塊的randint()方法等等。作為各種編程語言的“官方標配”,這小小的隨機函數作用那也是大大的,不光而這看似簡單的東西背后學問還真不少。
常見的計算機隨機數生成器有三種:
參考文章 : 解密隨機數生成器
一是使用物理方法,稱為真隨機數生成器(True Random Number Generator),生成的算是真正意義上的隨機數,無法預測且無周期性;
與真隨機數對應的是偽隨機數生成器(Pseudo Random Number Generator),它是由算法計算得來的,但這種方法生成的隨機數是可預測、有周期的,並不能算真的隨機數,因此得名偽隨機數;
還有第三種方法,叫隨機數表法,就是用真隨機數生成器事先生成好大量隨機數,存到數據庫中,使用時再從庫中調用。記得高中數學書第三冊后附有一個叫隨機數表的東西,使用時直接查閱就行,這種方法簡單,但缺點是內存占用大,因此不常采用。
在編程當中常常使用的就是偽隨機數生成器。
偽隨機數算法中重要術語:
PRNG中常常需要給定一個種子,映射出一個不確定的隨機數。需要划重點的是:不同的種子映射的數值可以認為是沒有規律的,可以認為是隨機的。在這樣的映射條件下,才有我們所說的隨機數生成器,否則如果是確定的一一映射,完全無法體現隨機這樣一個概念。
每一個偽隨機數生成器(PRNG)都會提供一個相當長的隨機數序列,這個序列的長度稱為循環周期或者循環長度,RNG在完成一次循環后將自動進行下一次重復。這個序列可以被分隔為幾個相互獨立的數據流。一個RNG的隨機數據流是這個RNG序列的連續子集。例如,一個RNG隊列長度為N,同時RNG提供2個數據流,第一個數據流使用序列的前半部分,第二個數據流使用序列的后半部分,這兩個數據流是相互獨立的。同樣,每一個數據流還可以被分為更小的子流。
一個RNG最希望做到的就是提供一個非常長的循環序列,並有效的分配給每一個數據流。
偽隨機數生成器有很多種算法,包括同余法、梅森旋轉算法等多種多樣的方法,我們此處重點介紹一下prerre L’Ecuyer提出的MRG32k3a生成器。它提供1.8*1 019相互獨立的數據流,每個數據流又包含2.3*1 015個子數據流,並且每一個子數據流用甘油7.6*1 022的長度,因此整個生成器的循環周期為3.1*1 057。正因為RNG擁有這么長的序列,才保證了RNG產生的隨機數具有很高的可信度。
MRG32k3生成器需要一個隨機數種子,
敲黑板!敲黑板!敲黑板!
1.計算機一般都是確定性的計算和運行,只有借助隨機數生成器才可以產生隨機數
2.三種隨機數生成器中,偽隨機數生成器PRNG在編程中使用最頻繁
3.偽隨機數中,由種子映射到隨機數時候可以認為映射是沒有規律、是隨機的
4.偽隨機數生成器的循環周期足夠長能夠保證RNG的隨機數具有更高的可信度
二、詳解:ns-3中PRNG的使用
在ns-3中,仿真程序通常使用固定的種子,也就是產生固定的隨機數,也就是固定的數值,所以仿真也就不存在不確定性,也即確定性仿真。
如果需要模擬不確定的情況,則需要改變PRNG的種子或者運行標識。
ns-3的隨機數通過調用類ns3::RandomVariableStream來提供(在3.14版本及之前有ns3::Random Variable提供)。ns3::RandomVariableStream是對底層的隨機數生成器進行封裝之后向用戶提供的使用接口。
PRNG的種子和運行標識分別存儲在類GlobalValue的g_rngSeed和g_rngRun中,改變PRNG的種子或者運行標識通過ns3::RngSeedManager::SetSeed()和ns3::RngSeedmanager::SetRun()進行,下面對兩種改變方式分別做介紹。
2.1 通過ns3::RngSeedManager::SetSeed()改變種子實現隨機性
#include "ns3/simulator.h" #include "ns3/nstime.h" #include "ns3/command-line.h" #include "ns3/rng-seed-manager.h" #include "ns3/random-variable-stream.h" #include <iostream> using namespace ns3; int main(int argc, char *argv[]) { CommandLine cmd; cmd.Parse(argc,argv); RngSeedManager::SetSeed(1); Ptr<UniformRandomVariable> uv = CreateObject<UniformRandomVariable>(); std::cout<<uv->GetValue()<<std::endl; return 0; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
上述代碼中,首先通過RngSeedManager::SetSeed(1)設置種子為1,然后創建了一個類型為UniformRandomVariable的智能指針Ptr,其中UniformRandomVariable是ns3::RandomVariableStream的子類,所以通過uv調用GetValue()函數獲得對應的值。
運行結果如下:
對於這個例子,在不改變SetSeed()函數參數的情況下連續運行兩次,得到的隨機數是一樣的,如上圖中所示,結果都是0.816532。將SetSeed()函數的參數修改為2,在運行一下,運行結果如下圖,發現改變種子之后,得到的隨機數結果是不一樣的,seed=2的時候的結果變成0.633064。
2.2 通過ns3::RngSeedmanager::SetRun()改變運行標識實現隨機性
在不改變種子的條件下,也可以通過改變 運行標識 來實現隨機性,改變運行運行標識的方法也有4種:
①通過函數RngSeedManager::SetRun(4)改變運行標識
以下的實驗中,分別使用RngSeedManager::SetRun(3)和RngSeedManager::SetRun(4)運行得到結果。
②通過修改環境變量NS_GLOBAL_VALUE的值來修改運行標識
這種方法不需要對代碼做修改,只需要在運行時候,在命令窗口進行設置。
NS_GLOBAL_VALUE="RngRun=3"./waf --run program-name
- 1
③通過命令行傳遞參數來修改運行標識
這種方法也不需要對代碼做修改,只需要在運行時候,在命令窗口進行設置。
./waf --run "program-name --RngRun=3"
- 1
④這種方式不使用waf,而是直接使用build
./build/optimized/scratch/program-name --RngRun=3
- 1
這種方法不太適用,此處不做演示。
**針對上述4種方式,第1種與改變方式的方式基本上是一致的,只需要把代碼行RngSeedManager::SetSeed()用代碼行RngSeedManager::SetRun()替換就可以,每次進行獨立運行時改變參數就行。第二種方式比較繁瑣,每次都要修改環境變量,所以建議還是使用第三種方法。
**需要注意的是:提倡使用改變運行標識的方法實現隨機性。因為在改變種子的情況下進行重復試驗,ns-3無法保證每個種子對應的RNG序列不會重復。而修改運行標識是使用RNG序列的不同的子序列,同一個RNG序列的子序列是不會重復的。
三、詳解:ns-3中的隨機變量類型
ns-3中一共有5種常用的隨機變量,這5種都是
1.滿足均勻分布的隨機數
2.滿足正態分布的隨機數
3.滿足指數分布的隨機數
4.滿足等差序列的隨機數
5.返回一個固定的數值
以上5種分別對應了RandomVariableStream類的5個子類,下面一一進行解釋。
1.UniformRandomVariable:給定最大值和最小值,按均勻分布的方式返回一個隨機數
UniformRandomVariable是從RandomVariableStream繼承的派生類,也是最基礎和最常用的類。它有兩個屬性:
屬性名稱 | 含義 | 類型 |
---|---|---|
Max | 均勻分布區間的上界 | ns3::DoubleValue |
Min | 均勻分布區間的下界 | ns3::DoubleValue |
以下是一個實例:
2.NormalRandomVariable:根據正態分布返回隨機數
需要設置的屬性值包括:
屬性名稱 | 含義 | 類型 | 初始值 |
---|---|---|---|
Mean | 返回值的均值 | ns3::DoubleValue | 0 |
Valance | 返回值的方差 | ns3::DoubleValue | 1 |
Bound | 返回值的上界 | ns3::DoubleValue | 1*10^307 |
3.ExponentialRandomVariable:根據指數概率分布返回隨機數
需要設置的屬性值包括:
屬性名稱 | 含義 | 類型 | 初始值 |
---|---|---|---|
Mean | 返回值的均值 | ns3::DoubleValue | 1 |
Bound | 返回值的上界 | ns3::DoubleValue | 0 |
4.SequentialRandomVariable:返回一串等差序列
如果超過了給定的最大上限,則重新從給定的最小值開始循環。序列隨機變量的常用屬性如下:
屬性名稱 | 含義 | 類型 | 初始值 |
---|---|---|---|
Min | 序列的最小值 | ns3::DoubleValue | 0 |
Max | 序列最大值的上限 | ns3::DoubleValue | 0 |
Increment | 序列的差值 | ns3::PointerValue | 1 |
consecutive | 序列中每個數重復的次數 | ns3::StringeValue | 1 |
5.ConstantRandomVariable:返回一個固定的數
其默認值是0,使用方法和均勻分布的例子一樣。
RngSeedManager::SetSeed(3); double dValue=10.0 //把默認初始值改為10 Ptr<ConstantRandomVariable> uv = CreateObject<ConstantRandomVariable>(); uv->SetAttribute("Constant",DoubleValue(dValue));