close

日文口譯證照

乃至可包成副函式 或寫成一行。

再怎麼給亂數這組公式一個初始值?用 srand( ) 翻譯社

 

將上述的程式多執行幾次會發現,怎麼每次亂數產生的都一樣?原因是沒設亂數種子 翻譯社

那初始值該給幾許?初始值給固定 翻譯值都沒用,要會跟著情況更改的值才成心義,

 

Code Snippet
  1. // 產生 [low 翻譯公司 up] 之隨機整數亂數
  2. int rand_int(int low, int up)
  3. {
  4.     return (int)((rand() / (RAND_MAX+1.0)) * (up - low + 1.0) + low);
  5. }

(1) shuffle_1 : 隨機取出第 pos1 張、pos2 張,再進行互換,也就是上面 翻譯體式格局。

2. 亂數種子

 

(2) shuffle_2 : 為確保每張牌至少被換過一次,依序拿第 i 張牌出來,隨機取出第 pos1 張牌,第 i 張牌與第 pos 張牌互換。

 

洗牌 (shuffle) 法的概念是,剛剛的 [low, up] ,每一個數字都視為撲克牌裡的一張牌,所以這副撲克牌共有 (up-low+1) 張,於是開陣列 Poker[up-low+1],並填入 1: 100。

一個方式是直接以 rand_int(low 翻譯公司 up-1) 體式格局代入上式;

 

故關鍵程式碼換如下。

Code Snippet
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4. int main()
  5. {
  6.     int i;
  7.     unsigned seed;
  8.     seed = (unsigned)time(NULL); // 獲得時候序列
  9.     srand(seed); // 以時候序列當亂數種子
  10.     for(i=0; i<5; ++i)
  11.         printf("%d ", rand());
  12.     getchar();
  13.     return 0;
  14. }

再來是模擬洗牌的過程,洗牌方式很是非常多!第一種是,隨機抽出第 pos1 張,再隨機抽出第 pos2 張,再將這兩張牌交換。進行 low-up+1 (100) 次 翻譯社整個動作做完後,再把 poker 前面 翻譯 20 (n, 欲取幾個亂數) 張牌,放到 Arr 裡面,就是謎底了。

道理我不講了 < 因目標是要 "會" 用就好 >,簡單的說產生器是一組公式,公式要給「初始值」。

而在 srand 那段,經常有人這麼寫

Code Snippet
  1. // 開始洗牌
  2. for(i=0; i<Size; ++i){
  3.     // 隨機掏出 [0,Size) 之 poker
  4.     pos = (int)(rand() / (RAND_MAX+1.0) * Size);
  5.     // 交換這兩張牌
  6.     tmp = Poker[pos];
  7.     Poker[pos] = Poker[i];
  8.     Poker[i]=tmp;
  9. }

按照以上之敘述,觀查可納出一結論:當要產生出 [low 翻譯公司 up] 之整數亂數時,可有另外一種體例,

那什麼叫亂數種子?

 

若是是要產生 [low, up) 之隨機整數亂數的話呢?這在做陣列索引很常見,

看碼最清楚 翻譯社

現實上產生 [low, up) 之整數亂數,就是產生 [low, up-1] 之整數亂數,

Code Snippet
  1. // 產生 [low, up) 之隨機亂數
  2. int rand_int2(int low, int up)
  3. {
  4.     return (int)((rand() / (RAND_MAX+1.0)) * (up - low) + low);
  5. }

 

 

 

硬要從函式裡面改的話,就是先產生 [low 翻譯公司 up) 之浮點亂數後,

有幾個議題曾被評論辯論過:(1) 多洗幾回牌是否是會比較亂? (2) 最好的洗牌次數是洗幾回?

 

像是 記憶體利用量、process id 、CPU 利用率 等,這些都是會隨環境變動,

srand( (unsigned)time(NULL) );

Code Snippet
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4.  
  5. void shuffle_1(int *arr, int n 翻譯公司 int low, int up)
  6. {
  7.     int i, pos1, pos2 翻譯公司 tmp;
  8.     int Size = up-low + 1; // 整份 poker 大小
  9.  
  10.     // 配置一份 poker[Size]
  11.     int * Poker = (int*)malloc(sizeof(int) * Size);
  12.     for(i=0 ; i<Size; ++i) // 填入 low~up
  13.         Poker[i] = i+low;
  14.     // 起頭洗牌
  15.     for(i=0; i<Size; ++i){
  16.         // 隨機掏出兩張 [0,Size) 之 poker
  17.         pos1 = (int)(rand() / (RAND_MAX+1.0) * Size);
  18.         pos2 = (int)(rand() / (RAND_MAX+1.0) * Size);
  19.         // 互換這兩張牌
  20.         tmp = Poker[pos1];
  21.         Poker[pos1] = Poker[pos2];
  22.         Poker[pos2]=tmp;
  23.     }
  24.     // 洗完牌, 前面的 n 張再給 Arr
  25.     for(i=0; i<n; ++i)
  26.         arr[i] = Poker[i];
  27.     free(Poker); // 釋放 poker
  28.     
  29. }
  30. int main()
  31. {
  32.     int low = 1, up=100;
  33.     int i, n = 20;
  34.     int arr[20];
  35.     srand((unsigned)time(NULL));
  36.     shuffle_1(arr 翻譯公司 n, low, up); // 洗牌
  37.     for(i=0; i<n; ++i) // 顯示後果
  38.         printf("%d " 翻譯公司 arr[i]);
  39.     getchar();
  40.     return 0;
  41. }

 

筆者不知道上面這兩問題 翻譯謎底。因這兩種洗牌方式沒被經由證實怎麼洗對照「亂」,

但有些變動性可能不大,而最常用來給初始值的,是時候,所以上述程式改以下。

1. 根基利用

 

接下來就是細節了 翻譯社目前撒播的洗牌體例有幾項

後面會講為什麼要知道亂數最大值,這是一個主要的值。

Code Snippet
  1. int low = -5 , up = 10 ; // 上下限
  2. int result; // 結果
  3. double r01 , r ;
  4. r01 = (double)rand() / (RAND_MAX+1.0); // 產生 [0 翻譯公司 1) 浮點亂數
  5. r = r01 * (up - low + 1.0) + low ; // 產生 [low, up+1) 浮點亂數
  6. result = (int)r;  // 最後強迫轉型。

。-> 翻譯社|,-> 翻譯公司|的-> 翻譯C/C++ 之亂數函式放在 stdlib.h / cstdlib 裡面,在利用時直接呼喚 rand() 即可。以下範例為產生 5 個亂數,並輸出。

 

 

 

Code Snippet
  1. void KnuthShuffle(int *arr, int n 翻譯公司 int low, int up)
  2. {
  3.     int i, pos1 翻譯公司 pos2 翻譯公司 tmp;
  4.     int Size = up-low + 1; // 整份 poker 大小
  5.  
  6.     // 配置一份 poker[Size]
  7.     int * Poker = (int*)malloc(sizeof(int) * Size);
  8.     for(i=0 ; i<Size; ++i) // 填入 low~up
  9.         Poker[i] = i+low;
  10.     // 開始洗牌
  11.     for(i=Size-1; i>0; --i){
  12.         // 隨機取出 [0, i] 之 poker
  13.         pos = (int)(rand() / (RAND_MAX+1.0) * (i+1));
  14.         // 交換這兩張牌
  15.         tmp = Poker[pos];
  16.         Poker[pos] = Poker[i];
  17.         Poker[i]=tmp;
  18.     }
  19.     // 洗完牌, 前面的 n 張再給 Arr
  20.     for(i=0; i<n; ++i)
  21.         arr[i] = Poker[i];
  22.     free(Poker); // 釋放 poker    
  23. }

注重,srand 正常而言一份程式碼(專案)只能履行一次,若是它放在 for loop 裡,每次進行 rand 前就用 srand,會發現每次掏出來的亂數是統一個數字 翻譯社

以取模運算子撰之, rst = rand() % 4,看一下數值散佈 翻譯環境。

互換時連 Rst 也一路互換 Swap(Rst[i], Rst[j]),程式碼約以下述 < 排序法用較低效之排序 > 。

10-12 = 1.0 / NEW_RAND_MAX
NEW_RAND_MAX = 1012

再回到擲骰子的問題上,要產生 [1, 6] 之間的整數亂數,事實上有另外一種方法,就是先產生 [1 翻譯公司 7) 翻譯浮點數亂數,以後再強制轉型成整數,所以程式碼以下所示 翻譯社

 

 

double low = 5.1 , up = 7.3, rndf, result;

 

 

針對這類較簡單的機率數字,1 2 3 4 泛起的比率為 4 : 1 : 3 : 2,加總為 10,

1~100 有 100 個元素,排序法方式是直接開兩個陣列 : int Rst[100], int Rnd[100],

就是產生 [low 翻譯公司 up+1) 之浮點數亂數後,再進行強迫轉型成整數。如下 翻譯社

 

這類作法較少人用 翻譯社緣由是它記憶體空間比其他方式最少多出兩倍,別的時候也大多花在排序法上面 (較佳也是 nlogn 複雜度),故幾近沒人用。

3. 得知亂數最大值

 

3 顆骰子點數最小為 3 ,最大為 18,所以輸出時只要判定 3~18 呈現的次數便可。下面程式碼沒優化過,對初學者而言較易懂。

不平均亂數還有許多特別的狀況,碰到時建議再念念機率統計,若是已有的機率模子,必可找到現有相符該機率模子之亂數產生器(像 tr1 翻譯公司 boost , c++11 都有了) ,否則,只能從較特別、列出來的機率模子那裡下手。

Code Snippet
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5.     int i;
  6.     for(i=0; i<5; ++i)
  7.         printf("%d " 翻譯公司 rand());
  8.     getchar();
  9.     return 0;
  10. }

再斟酌 

5. 產生浮點數亂數

 

另本文附程式碼,不附執行結果,有興趣自己跑一遍。

去查抄 rndf 落在哪段區間,rndf < CP[i] 之最小 i 即為所求。

產生浮點數亂數,每每都是先獲得  [0 翻譯公司 1) 之浮點數亂數 ( 可以包括零,但不包括 1 )。

 

rand() = 0, 1, 2 翻譯公司 3    : rst = 0
rand() = 4, 5, 6        : rst = 1
rand() = 7, 8, 9 翻譯公司 10  : rst = 2
rand() = 11 翻譯公司 12, 13  : rst = 3 

C/C++ 供給 翻譯 rand() ,它有規模限制,最小是 0 ,最大是若幹?

有些數字可能不會出現 < 因為也才擲十次而已 >,但多執行幾次應會呈現,且規模必然是 1~6 。

因陣列有 N 個元素,局限只能是 [0 翻譯公司 N) ,而不能是 [0, N]。

 

所以有種做法以下

6. 再談整數亂數

 

 

step 3 : 由 bit 數產生亂數

double low = 5.1 翻譯公司 up = 7.3 翻譯公司 result;

寫成一行型式

(2) 取整數亂數 pos,局限為 [0, j] ,交流 poker[j], poker[pos]

Q4 : 上面範例都是在計議不含上界的情況,若是要含上界的話呢?
A4 : 很簡單,把上面 翻譯 RAND_MAX + 1.0 部份,全都改成 RAND_MAX 便可,這樣就有機遇出現上界。

(1) 開大小為 10 的陣列 Arr[10],

 

一樣的議題,若欲產生的整數亂數規模超過 RAND_MAX 時,這類方法也是有些數字沒舉措產生到。只是這類方式沒舉措產生的數字,是被打散到各區塊裡,而不是像取模運算子全擠在後半段。總之就是建議要額外處理。

 

7.  不平均亂數問題

 

step 1 : 計較 所需 NEW_RAND_MAX  

Q2 : 為什麼要加上 low ?
A2 : 不加 low 的話現實上產生的是 [0 , up-low],加上 low 的話才是 [low, up]。

最大被界說在 stdlib.h / cstdlib 裡面的 RAND_MAX,所以要得知最大是幾何 翻譯

那若是小數點後面加到 10 位數,不就要設一個大小為 10^10 翻譯陣列了?

 

這裡是個重點,請別認為用不到很無聊跳過 < 如果熟的話大概也不會看這篇文章了吧 翻譯社>

 

最後請注重本文在區間表達裡,開區間與閉區間 括號的利用,也就是,

 

一種委曲可接受 ( 其實也是大多還不太會用 library 之 coder 的解決方案 ) 之體式格局為:一次取兩個亂數,將數值擴大 翻譯社假設 RAND_MAX = 32767,佔了 15 bits( 111111111111111(2) = 32767(10) ),試考慮以下程式碼 翻譯社

另一種體例是用累計機率,我們先做累計機率的表出來

 

 

 

 

Knuth Shuffle 在洗牌的進程重點在於:

step 2 :  從 NEW_RAND_MAX 計較所需 bits 數

11. 大亂數問題 (I)

 

所以要到達 10-12 精度時

所以 rst = 2 與 rst = 3 泛起 翻譯機率比力低,

所以寫出這段碼出來 翻譯社

 

用乘、除法 翻譯情況

 

 

(1) 從後面洗回來。for (j=size-1 ; j>0 ; --j) 留意,辨別式裡沒有等於零。

別的亂數道理也全都跳過 < 重點是亂數 翻譯產生道理也不只一種 > 翻譯社

 

 

Code Snippet
  1. typedef unsigned long long u64;       // typedef
  2. u64 rst = ( (u64)(rand()) << 25 ) |   // bit[39:25]
  3.     (u64)(rand()) << 10 ) |           // bit[24:10]
  4.     (u64)(rand()& 0x3ffULL ) ;        // bit[9:0]

 

一種作法是先開巨細為 20 翻譯陣列 Arr[20],每產生一個亂數 翻譯時辰,就到 Arr 裡面看有沒有重覆,若是沒有重覆才加進去,有重覆的話就再取下一個亂數 翻譯社示例碼以下 < 贅變數許多,像 find 是可以完全拿掉的 >。

 

這四個示意的意義分歧,

 

 

Code Snippet
  1. #include <stdio.h>
  2. #include <time.h>
  3. #include <stdlib.h> // RAND_MAX
  4. int main()
  5. {
  6.     int RunTimes = 500  ; // 測試次數
  7.     int SumTimes[20]={0}; // 記載點數泛起的次數, 全歸零
  8.     int i, sum 翻譯公司 rnd;
  9.  
  10.     // 進行測試
  11.     for(i=0; i<RunTimes; ++i) {// 測試 RunTimes 次
  12.         rnd = rand() % 6 + 1 ; // 第一顆骰子呈現點數
  13.         sum = rnd;             // 記載總合
  14.         rnd = rand() % 6 + 1 ; // 第二顆骰子泛起點數
  15.         sum = sum + rnd;       // 記載總合
  16.         rnd = rand() % 6 + 1 ; // 第三顆骰子呈現點數
  17.         sum = sum + rnd;       // 記載總合
  18.  
  19.         // 將泛起 sum 點數之次數加1
  20.         SumTimes[sum] = SumTimes[sum]+1;
  21.     }
  22.  
  23.     // 輸出成效
  24.     sum = 0;
  25.     for(i=3; i<=18; ++i) {
  26.         printf(" %2d 點呈現了 %3d 次 ", i, SumTimes[i]);
  27.         sum = sum + SumTimes[i]; // 再驗證總合是否是500次
  28.     }
  29.     printf("共  %3d 次 ", sum);
  30.     getchar();
  31.     return 0;
  32. }

 

亂數其他議題相當多,有些也不好實作出來,本篇所提是較為基礎之部份,其他諸如 蒙地卡邏 MAMC、其他亂數分佈等議題,便不於此文探討。

由於 n 必為大於等於1之整數,故取 40。

[1] 20 : 機率 = 0.234 ,累計機率 = 0.123 + 0.234 = 0.357,令為 CP[1]

剛剛的典範是,[1,100],100 個數,挑 20 個相異亂數 翻譯社但如果把條件悔改:

[2] 30 : 機率 = 0.345,累計機率 = 0.357 + 0.345 = 0.702,令為 CP[2]

 

如許就不需暫存 seed 變數 翻譯社

 

Q2 :  那為什麼分母還要特別加上 1.0 ?
A2  : 前面有說過了,rand() 最大值可到 RAND_MAX, 不加上 1.0 翻譯話會使得 (double) rand() / RAND_MAX ,結果有機率變成 1 ,但這與我 翻譯前提:不包括 1 是相違的。

 大亂數問題至此竣事。提醒,一般簡單統計用的亂數可以用此法產生沒錯 ( 像一些演變式演算法,或蒙地卡羅演算法),若用於加解密等,通常不會再用 rand() 方式進行亂數產生 翻譯社

[HomeWork] 依上述的數字泛起之機率,做 10萬 次測試,最後真正現實上1, 2 翻譯公司 3, 4 泛起之次數、機率為何?是不是接近於當初設定之機率?

程式碼示意如下。

回到最初的問題,從 [1,100] 裡挑出 20 個不重覆之亂數,結果填到 Array 裡。這裡我們先為這些數字做點符號界說透露表現。

 

 

陸陸續續寫了 EA  1、二年,之前亂數指導文回頭看時才發現,怎麼有這麼多細節 翻譯毛病、沒系統 翻譯社

接下來可以認真評論辯論,為什麼大大都較不建議用取模運算子 (mod , %) 來求浮點亂數了。

 

那,產生出 [low, up) 之浮點數隨機亂數(不含 up )怎做?

 

 結束以後,這只能產生 [0,240-1] 之亂數產生器,要再產生 [0,1] 之浮點亂數,就再除上 240-1。

10 呈現機率為 0.123, 20 泛起機率為  0.234,

30 泛起機率為 0.345, 40 出現機率為  0.298,

10. 不重覆亂數問題 < 排序法 >

 

要產生 20 個 [1,100] 不重覆之亂數,怎麼做?

12. 大亂數問題 (II)

 

 

切割方式為 15 + 15 + 10 = 40 bits,左移 bits 數依序為 [15+10, 10, 0]。 但斟酌到高位元之循環率較低位元循環率小,所以將 40 切割成 14 + 13 + 13,且掏出時取高 bits 為主,依序應該左移 bits 數為 [13+13,13 翻譯公司0]。

rst = (int)( rand() / 14.0 * 4 ) ;

 

 

 

4. 產生固定規模的整數亂數

 

也由於是指導初學者,所以在某些用詞上會較不准確,

這個記憶體底子就放不下 翻譯社

再來看浮點數亂數,要達到1e-6 精度問題,這只是相同問題換個型態泛起罷了。若 RAND_MAX 只到 32767,產生的亂數最小精度是 1/32767,約為 3.1 E -5 ,達不到精度要求為 1e-6 之需求 翻譯社

常見 翻譯不重覆亂數解決方案,大致上就這三種。

(2) 依序填入 4 個 1、1個2、3個3、2個4 翻譯社

機率比較低的 rst ,都被放置到 rst 可能出現之值的後半段

Q1 : 為什麼是 % (up-low+1) , 而不是 % (up-low) ?
A1 : 因 low~up 一共有 (up-low+1) 個數。拿產生 [1,6] 來說,現實上共有 6-1+1 = 6 個數 翻譯社

 

概念上之程式碼約以下述 < 這只是一份示例,會有更好 翻譯寫法 > 翻譯社

double precision = 1.0 / RAND_MAX = 1.0 / 32767 = 3.05 * 10-5

 

真正經由證明「怎麼洗較好」的是楊氏洗牌法 ( 或稱 Knuth Shuffle )。

最快徹底解決這問題 翻譯體式格局,是直接換一套亂數產生器 翻譯函式庫,這些在 C++11 , boost, tr1 裡面已有非常豐富,甚至也有專門在寫亂數函式庫 翻譯 library,甚至較有水準的數值分析函式庫也大多會有較佳品質的亂數函式庫出現。拿到時注意幾個點:RAND_MAX 是多少?若以浮點亂數出現的話,其精度是幾許?還有亂數重覆周期是多少。更重要的是,留意他們的亂數函式庫支不支援多行緒?最好找支援多行緒 翻譯函式庫,未來移植才比較沒問題。這幾點很重要。 

1 泛起機率為 0.4 ;  2 呈現機率為 0.1 ;

3 出現機率為 0.3 ;  4 呈現機率為 0.2 ;

怎麼做?

rand() = 0, 4 翻譯公司 8 翻譯公司 12  : rst = 0
rand() = 1, 5 翻譯公司 9, 13  : rst = 1
rand() = 2, 6, 10      : rst = 2
rand() = 3 翻譯公司 7, 11      : rst = 3 

Code Snippet
  1. #include <stdio.h>
  2. #include <time.h>
  3. #include <stdlib.h>
  4. int main()
  5. {
  6.     int result;
  7.     double r01, rnd;
  8.  
  9.     // 亂數種子
  10.     srand((unsigned)time(NULL));
  11.     // 產生 [0,1) 之亂數
  12.     r01 = (double)(rand()) / (RAND_MAX + 1.0) ;
  13.     // 產生 [1,7) 之亂數
  14.     rnd = r01 * (7.0 - 1.0) + 1.0;
  15.     // 強制轉型給 result
  16.     result = (int)(rnd);
  17.     printf("result = %d " 翻譯公司 result); // 輸出成效
  18.     getchar();
  19.     return 0;
  20. }

填完以後,對 Rnd 做排序,而在排序過程當中有效到互換,Swap (Rnd[i] 翻譯公司 Rnd[j])

int high = rand() << 15; 
int low  = rand();
int rst  = high | low;

乃至三行可寫成一行

< 加起來恰好等於 1 沒錯 >

機率比力低的 rst ,都被均勻打散到 rst 可能呈現之值範圍內 翻譯社

像 compiler、IDE 會有意混為一談。

方才已給出了 rndf = [0, 1) 之公式,所以要擴展到 [low 翻譯公司 up) 時,只要做點點竄就行,

另利用 % 取亂數,小我感覺較不當,緣由在 6. 再談整數亂數 說明並給方法。

% 叫取模運算子,不懂 翻譯話歸去翻書。如許下來可以肯定,result 只有 {0, 1, 2 翻譯公司 3, 4, 5} 6 種可能罷了。但現實上骰子 翻譯範圍是  1~6,而不是 0~5,怎麼辦?很簡單,只要把結果 + 1 就行了 翻譯社原本的成績是 0~5 ,加1後成效釀成 1~6 翻譯社

再強迫轉型成整數資料型態。

Code Snippet
  1. typedef unsigned long long u64; // typedef
  2. u64 rst = ( \
  3.      ( (u64)(rand() >> 1) << 26 ) | // high 14, L shift 26bits
  4.      ( (u64)(rand() >> 2) << 13 ) | // high 13 翻譯公司 L shift 13bits
  5.      ( (u64)(rand() >> 2)       )); // high 13, L shift 0bits

累計機率算出來之後,我們只需要產生 [0, 1) 之随機浮點數亂數 rndf,

 

 

這裡要提示,若是亂數產生 翻譯規模已經超過 RAND_MAX 的話,如產生 [-1000 翻譯公司 +50000] 之亂數,必須額外進行處理,這類撰寫,只有前面的 RAND_MAX 數字有機遇泛起,其他後面的數字全都沒機遇看到。

 

 

 

 

[0] 10 : 機率 = 0.123 ,累計機率 = 0.123,令為 CP[0]

result = (up - low) * rndf + low;  // 產生 [low, up) 浮點亂數

再續上個問題,從 [1,100] 裡挑出 20 個不重覆之亂數,後果填到 Array 裡 翻譯社

 

所以 rst = 1 和 rst = 3 呈現 翻譯機率比較低,

以無號數二進位而言,n bits 可表達之最大數為 2n-1 ,故可列下以下不等式

今朝可以肯定的是,RAND_MAX 最少會是 32767,最大會是幾何紛歧定。但以筆者手邊的 Visual C++ 2010 情況而言,這個值是 32767。現實上 VC6.0 翻譯公司 VC2002 / 2003 , VC2008, VC2010 , gcc 翻譯公司 Dev-C++ 翻譯公司 Code::Blocks (with mingw) ,這個值也都恰好是 32767,只是他們實作的亂數細節不同而已。至於日後其他改版會不會讓 RAND_MAX 更大?那就看那些軟體( compiler ) 如何實作了。

 

9. 不重覆亂數問題 < 洗牌法 >

 

result = rand() % 6 

 

 

這部分只是簡單 翻譯數學推導,已知該怎麼做的可略過不看。

 8. 不重覆亂數問題 < 暴力法 >

 

這類方式大多被納為暴力法之一種模式,但實質上在某些情形它是蠻合適用的 翻譯社若是只是要用2、三個相異 翻譯亂數,這方法很合適,直接用 do-while 做,乃至不需要開陣列便可完成。

Q1 : 為什麼是產生 [1, 7) 之浮點數亂數,而不是產生 [1,6] 之浮點數亂數?
A1 : 重點在後半段還要強迫轉型。若一最先就產生 [1, 6] 之浮點亂數時,要使得轉型後成效為 6 只有一種條件可告竣:rand() 必需是 RAND_MAX。這部分道理很簡單,但建議自己想一想比力有收成 翻譯社

 

 

Code Snippet
  1. #include <stdio.h>
  2. #include <time.h>
  3. #include <stdlib.h>
  4.  
  5. int main()
  6. {
  7.     int i, j, pos;
  8.     int Arr[10]; // 開巨細為 10 的陣列 Arr[10]
  9.     
  10.     //  依序填入 4 個 1、1個2、3個3、2個4
  11.     Arr[0]=Arr[1]=Arr[2]=Arr[3] = 1; // 4 個 1
  12.     Arr[4]=2 ; // 1 個 2
  13.     Arr[5] = Arr[6] = Arr[7] = 3 ; // 3 個 3
  14.     Arr[8] = Arr[9] = 4; // 2 個 4
  15.    
  16.     srand( (unsigned) time(NULL) );
  17.     // 隨機產生 [0 翻譯公司 9] 之整數亂數 翻譯公司 pos,再獲得 Arr[pos] 出來
  18.     for(i=0; i<10; ++i) {// 取 10 次
  19.         // 產生 [0 翻譯公司9] 整數亂數 pos
  20.         pos = (int)(rand() / (RAND_MAX+1.0) * 10) ;
  21.         // 取出 Array[pos]
  22.         printf("%d ", Arr[pos]);
  23.     }
  24.     getchar();
  25.     return 0;
  26. }

 

Ex 1 : 摹擬擲一顆骰子擲 10 次,並輸出其成效。

(double) rand() / (RAND_MAX + 1.0 );

Code Snippet
  1. #include <stdio.h>
  2. #include <time.h>
  3. #include <stdlib.h>
  4.  
  5. int main()
  6. {
  7.     int n = 20; // 找 20 個相異亂數
  8.     int i 翻譯公司 cnt 翻譯公司 num, Arr[20];
  9.     int find;
  10.     srand( (unsigned)time(NULL));
  11.     cnt = 0; // 已有不重覆亂數之個數
  12.     while(cnt < n){
  13.         
  14.         // 產生 [1 翻譯公司 100] 之整數亂數
  15.         // rand() / (RAND_MAX+1.0)) * (up - low + 1.0) + low
  16.         // num = rand()/(RAND_MAX+1.0)*(100-1+1.0) + 1;
  17.         num = (int)( rand() /(RAND_MAX+1.0)*100.0 + 1);
  18.  
  19.         // 到 Arr 裡查有無重覆產生
  20.         find = 0; // 假設沒發現
  21.         for(i=0; i<cnt; ++i){
  22.             if(Arr[i]==num) { // 有發現
  23.                 find = 1;
  24.                 break;
  25.             }
  26.         }
  27.         //
  28.         if(find==0) {     // 真的沒發現
  29.             Arr[cnt]=num; // 插足 Arr 裡
  30.             ++cnt;        // 找到個數 +1
  31.         }
  32.     }
  33.     // 最後輸出
  34.     for(i=0; i<n; ++i){
  35.         if(i%10==0) puts("");
  36.         printf("%3d ", Arr[i]);
  37.     }
  38.     getchar();
  39.     return 0;
  40. }

 

[a 翻譯公司 b]   翻譯公司   (a, b]  , [a 翻譯公司 b) ,  (a, b) 

先講講整數亂數 [0, 50000],倘若 RAND_MAX 只到 32767 時,以 % 方式而言,無論怎麼產生,[0 翻譯公司32767] 可正常產生,但 [32768,50000] ,共 17234 個數完全產生不了。即便先產生 [0,1) 浮點數,即用 rand() / RAND_MAX 這方式,也一樣會有 17233 個數產生不了,只是這 17233 個數不是最後 翻譯那幾個,而是被均勻打散到 [0,50000] 裡面罷了。

Ex 2 : 摸擬擲 3 顆骰子 500 次,記載點數和呈現 翻譯次數,最後輸出每一個點數共泛起幾回。

從 [low, up] 裡,挑出 n 個不重覆之亂數,結果填到 Array 裡。

概念是 [low, up) 亂數,等於 (low~up 距離) * ( [0,1) 亂數 ) + (下限 low)

Q3 : 那除加上 1.0 這數字外,可以改成加其他數字啊!諸如 2.0 翻譯公司 100.0 翻譯公司 10000.0 之類的 翻譯社
A3 : 又如我方才所說,是要產生 [0,1) 之間的浮點數亂數,假定 RAND_MAX = 32767,若是加上 10000.0 的話,這個成績最大值會釀成了 32767 / (32767+10000) = 0.247,顯著 [0.25, 1.0) 都沒機會生成了。但假如改成 0.5 , 之類,小於 1 較大 翻譯小數,到是可接管,不外這類數字幾乎沒人在用。

   int rst = ( rand() << 15 ) | rand();

我們以擲骰子為例,一個骰子有 6 個面,點數劃分為1~6,要隨機擲一顆骰子怎麼做?

試再想另一種景遇,若 

13. 其他

 

 

 

總合以上申明,事實上我們可以給出一組公式,若要產生 [low 翻譯公司 up]  之整數亂數,我們可以這麼做

這怎麼產生?還記得 RAND_MAX 是什麼意思吧?是 rand() 可能產生 翻譯最大值,

 上面這段碼可以准確跑出了局無誤 翻譯社

rndf = (double) rand() / (RAND_MAX + 1.0); // 產生 [0, 1) 浮點亂數

假定 RAND_MAX = 32767 (15 bits) 翻譯公司 先想一想一般 翻譯亂數精度可以怎麼求

(3) 隨機產生 [0, 9] 之整數亂數 , pos,再取得 Arr[pos] 出來便可。

Code Snippet
  1. #include <stdio.h>
  2. #include <time.h>
  3. #include <stdlib.h> // RAND_MAX
  4. int main()
  5. {
  6.     int i;
  7.     srand( (unsigned)time(NULL));
  8.     for(i=0; i<10 ; ++i){
  9.         printf("%d " 翻譯公司 rand() % 6 + 1);
  10.     }
  11.     getchar();
  12.     return 0;
  13. }

[1,32767],挑32767個不重覆亂數,它 翻譯履行時間就頗費時了,這時候就不斟酌利用這方式 翻譯社

Code Snippet
  1. #include <stdio.h>
  2. #include <time.h>
  3. #include <stdlib.h>
  4.  
  5. int main()
  6. {
  7.     int i, pos, n=4; // 4 個元素
  8.     int Num[4] = {10, 20 翻譯公司 30, 40}; // 欲呈現之數字
  9.     double Prob[4]= {0.123, 0.234 翻譯公司 0.345, 0.298}; // 數字對應之出現機率
  10.     double CP[4];
  11.     double rf; // 隨機機率
  12.  
  13.     srand( (unsigned)time(NULL));
  14.     // step 1 : 做累計機率計較
  15.     CP[0] = Prob[0];
  16.     for(i=1; i<n; ++i)
  17.         CP[i] = CP[i-1] + Prob[i];
  18.  
  19.     for(i=0; i<10; ++i) { // 做 10 次測試
  20.         rf = rand() / (RAND_MAX + 1.0) ; // 產生 [0, 1) 亂數
  21.         
  22.         for(pos=0; pos < n; ++pos) // 查詢所在區間
  23.             if(rf <= CP[pos]) break;
  24.         printf("%d " 翻譯公司 Num[pos]); // 輸出數字
  25.     }
  26.     getchar();
  27.     return 0;
  28. }

 

現假定一種情形是,但願不是每個數呈現 翻譯機率都一樣,假定有 4 個數,

鑑於亂數應契合均勻之特征,故較多人建議別用取模 (mod) 方式取整數亂數。

result = (up - low) * rand() / (RAND_MAX + 1.0) + low;

大亂數問題在上面有先提過了,假定要產生的整數亂數規模是 [0, 50000],或產生的浮點數亂數精度為 1e-6,怎麼處置?

Rst[100] 從 1 填到 100,Rnd[100] 是連續取100個亂數填進去,

 

 

 

Code Snippet
  1. typedef unsigned long long u64; // typedef
  2.  
  3. u64 rand40() {
  4.     return ( \
  5.         ( (u64)(rand() >> 1) << 26 ) | // high 14, L shift 26bits
  6.         ( (u64)(rand() >> 2) << 13 ) | // high 13, L shift 13bits
  7.         ( (u64)(rand() >> 2)       )); // high 13 翻譯公司 L shift 0bits
  8. }
  9.  
  10. double randf40() {
  11.     const u64 NEW_RAND_MAX = (1ULL << 40) - 1ULL;
  12.     return (double)rand40() / NEW_RAND_MAX;
  13. }
Code Snippet
  1. #include <stdio.h>
  2. #include <stdlib.h> // RAND_MAX
  3. int main()
  4. {
  5.     printf("%d ", RAND_MAX);
  6.     getchar();
  7.     return 0;
  8. }

[3] 40 : 機率 = 0.298,累計機率 = 0.702 + 0.298 = 1.000,令為 CP[3]

Q1 : 為什麼要分外在 rand() 前面轉型成 double ?
A1 : 簡單的說,我怕有人雞婆,把後面的 1.0 本身寫成 1 ,這時候候不加上 (double) 翻譯話成效除出來必然是 0 ;若後面的 1.0 都不動它的話,前面的 double 可以拿掉無誤 翻譯社

Code Snippet
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4.  
  5. #define SWAP(a 翻譯公司b){int t=a; a=b; b=t;}
  6. void SortShuffle(int *arr, int n, int low 翻譯公司 int up)
  7. {
  8.     int i, j;
  9.     int Size = (up-low+1);
  10.     int *Rst = (int*)malloc(sizeof(int) * Size);
  11.     int *Rnd = (int*)malloc(sizeof(int) * Size);
  12.  
  13.     if(Rst==NULL || Rnd==NULL) return;
  14.  
  15.     for(i=0; i<Size; ++i){
  16.         Rst[i] = low + i; // 依序填入數值到 Rst
  17.         Rnd[i] = rand();  // 對 Rnd 取亂數
  18.     }
  19.     // 對 Rnd 做排序
  20.     for(i=0; i<Size-1; ++i){
  21.         for(j=i+1; j<Size; ++j){
  22.             if(Rnd[i] > Rnd[j]) {
  23.                 // 互換時連 Rst 也一路互換
  24.                 SWAP(Rnd[i],Rnd[j]);
  25.                 SWAP(Rst[i] 翻譯公司 Rst[j]);
  26.             }
  27.         }
  28.     }
  29.     // Rst 前 n 筆存入 arr
  30.     for(i=0; i<n; ++i)
  31.         arr[i] = Rst[i];
  32.     // 釋放記憶體
  33.     free(Rnd), free(Rst);
  34. }
  35. int main()
  36. {
  37.     int low = 1, up=100;
  38.     int i, n = 20;
  39.     int arr[20];
  40.     srand((unsigned)time(NULL));
  41.     SortShuffle(arr, n 翻譯公司 low, up); // 洗牌
  42.     for(i=0; i<n; ++i) // 顯示成績
  43.         printf("%d " 翻譯公司 arr[i]);
  44.     getchar();
  45.     return 0;
  46. }

 

 

 

 

2n-1 >= 1012  ,疏忽 1 所帶來之影響,雙方取 log10
log10(2n) >= 12,
n log10(2) >= 12
n >= 12 / log10(2) = 39.86

 

利用 shuffle 必須額外再多配置一份 (up-low+1) 之記憶體空間,若自己 poker 張數很多 ( 欲挑選的範圍很大),但欲獲得的值很小 ( n 很小 ) ,事實上也不適合用 shuffle,除浪費空間之外,還鋪張了一最先填數字的時間,此時反而以暴力法來做較為得當。像是在 [1,20000] 取出 10 個相異亂數時,此時用暴力法便較為恰當 翻譯社

現假定一問題為,該若何產生  [0, 1] 之間,10-12 精度之浮點亂數產生器。

起首,1~6 恰好有 6 個數字,所以可以這麼寫

照上面的方式,不就要設一個巨細為 1000 的陣列了嗎?

這篇文章首要指導初學者利用亂數,同時附上常被翻出來計議 翻譯議題,C/C++合用,唯以 C 說話撰之 翻譯社

組合出的亂數最大值最少要 1012 才可知足。

rand() % (up - low + 1) + low 

程式碼約以下述。

 

如斯下來可產生 30 bits 之亂數,範圍從 [0, 215-1] 釀成了 [0, 230-1]。若有需要,可再取二次、取三次、取四次等等,但這會有潛在問題存在,一方面利用 rand() 之亂數產生器,通常週期並不十分長 ( 像 vc, gcc 之 rand 週期只到 231 擺佈),且平均度也有待測試,這也是筆者建議直接再找另外一支亂數產生器之緣由。

我們先假定一種情形,若某個亂數產生器,他的 RAND_MAX = 13,目前要產生 [0 翻譯公司3] 之整數亂數。

 筆者所知只有這兩種方式,有其他方法接待討論。

rst = (int)((rand() / (RAND_MAX+1.0)) * (up - low + 1.0) + low);

在假定 RAND_MAX = 32767 之環境下,取一次 rand() 有 15 bits,故要到 40 bits 最少要取 3 次才可到達。但以筆者手邊情況而言,int / unsigned int 只有 32 bits ,沒舉措達到 40 bits 之要求,故改用資料型態 unsigned long long ( 更好的做法是用 uint64_t ) 去存結果,下面是一種作法 翻譯社



以下內文出自: http://edisonx.pixnet.net/blog/post/91314418-%5b%e4%ba%82%e6%95%b8%5d-%3c%e7%b4%b0%e8%aa%aa%3e-c-c%2有關翻譯的問題歡迎諮詢天成翻譯社

arrow
arrow
    文章標籤
    翻譯社
    全站熱搜
    創作者介紹
    創作者 nelsonco0l7j 的頭像
    nelsonco0l7j

    這裡是和天成翻譯有關的地盤,歡迎到訪我的BLOG!

    nelsonco0l7j 發表在 痞客邦 留言(0) 人氣()