kowala's home

kowala's home
這裡是我的學習筆記,陸續增加中。
http://kowala21.blogspot.com

2011-10-26

Dev C++ 亂數產生器-究竟版

嘿嘿!此究竟非彼究竟,這只是限於使用time來產生的最亂,當然,這還不真亂,眼尖者必能覺察。

先看一下代碼

//亂數產生器
int getRandom(int min,int max){
    int r;
    clock_t seed = clock();
    srand(seed);
    r= min + rand()%(max-min+1);
    return r;
}

這是取時間來當種子,使用的值是毫秒,下圖是模擬大樂透開獎,49選6... 主要是要產生49個不重複的亂數,但是現在電腦都算太快,亂數結果數值會一樣...


這時就要想個辦法減速一下,在前一個主題,遞迴函數-費式數列(Fibonacci),就派上用場了,我們可以利用遞迴函數算很慢的特性,來當減速器,隨便找個質數填進去,f(31)

#include <cstdlib>
#include <iostream>
#include <iomanip.h>

using namespace std;

//亂數產生器
int getRandom(int min,int max){
    int r;
    clock_t seed = clock();
    srand(seed);
    r= min + rand()%(max-min+1);
    return r;
}
//費式數列(Fibonacci)
double f(double n){   
    if(n<=2)return 1;   
    return f(n-1)+f(n-2);
}
int main(int argc, char *argv[]){
    int i;
    system("cls");
   
    cout << "亂數測試..." << endl;
    for(int i=1;i<=49;i++){
        f(31);    //減速器
        cout << setw(3) << setprecision(3) << getRandom(1,49) << " ";       
    }
    cout << endl;

    system("PAUSE");
    return EXIT_SUCCESS;
}

看來有點亂了,減速成功!

接著是消去重複數,這需要用個陣列來幫忙。

int main(int argc, char *argv[]){
    int i,n,v,lo[49];
    system("cls");
   
    cout << "亂數測試..." << endl;
    for(int i=0;i<49;i++){
        f(37);
        v=getRandom(1,49);
        n=0;
        while(n<i){           
            if(lo[n++]==v){
                f(23);
                v=getRandom(1,49);
                n=0;
            }
        }
        lo[i]=v;       
        cout << setw(3) << setprecision(3) << lo[i] << " ";       
    }
    cout << endl;

    system("PAUSE");
    return EXIT_SUCCESS;
}

這還是不夠亂,生氣,最後一招,改良種子,取 UTC + 毫秒  來當種子

clock_t seed = time(0)+clock();

結果不錯,每次都不一樣,這樣就夠亂了。



//亂數產生器-究竟版
int getRandom(int min,int max){
    int r;
    clock_t seed = time(0)+clock();
    srand(seed);
    r= min + rand()%(max-min+1);
    return r;
}

其實,這還是不夠亂,上圖每個數字都差3-4之間,而且都是規律遞增,所謂亂數,就是無規律,閉著眼亂點,點中就算,這樣才夠亂,我深深懷疑,電腦或是數學式能否產生出真正的亂數,要真亂的話,我只想到一個,自己拿骰子擲,然後記錄點數。

下表是我找的亂數表,懶得自己打字,所以從網路copy來的, 應該是比這個時間產生的要亂些,可以充當種子。

http://www.stat.nuk.edu.tw/prost/content2/statics_2.htm

29280 39655 18902 92531 90374 07109 26627 59587 84340 98351
20123 82082 55477 22059 43168 12903 13436 25523 21090 73449
66405 35287 33248 67657 07702 01474 66068 01125 59258 30138
97299 83419 13069 17826 76984 48906 10567 17829 00723 46700
83923 92076 98880 33942 46841 58731 36513 16681 88722 61984
11258 92175 94894 97606 11134 51941 43733 00514 06694 27706

http://wiki.mbalib.com/zh-tw/%E9%9A%8F%E6%9C%BA%E5%8F%B7%E7%A0%81%E8%A1%A8%E6%B3%95

03 47 43 73 86 36 96 47 36 61 46 98 63 71 62
97 74 24 67 62 42 81 14 57 20 42 53 32 37 32
16 76 02 27 66 56 50 26 71 07 32 90 79 78 53
12 56 85 99 26 96 96 68 27 31 05 03 72 93 15
55 59 56 35 64 38 54 82 46 22 31 62 43 09 90
16 22 77 94 39 49 54 43 54 82 17 37 93 23 78
84 42 17 53 31 57 24 55 06 88 77 04 74 47 67
63 01 63 78 59 16 95 55 67 19 98 10 50 71 75
33 21 12 34 29 78 64 56 07 82 52 42 07 44 28
57 60 86 32 44 09 47 02 96 54 49 17 46 09 62
18 18 07 92 46 44 17 16 58 09 79 83 86 19 62
26 62 38 97 75 84 16 07 44 99 83 11 46 32 24
23 42 40 54 74 82 97 77 77 81 07 45 32 14 08
62 36 28 19 95 50 92 26 11 97 00 56 76 31 38
37 85 94 35 12 83 39 50 08 30 42 34 07 96 88
70 29 17 12 13 40 33 20 38 26 13 89 51 03 74
56 62 18 37 35 96 83 50 87 75 97 12 25 93 47
99 49 57 22 77 88 42 95 45 72 16 64 36 16 00
16 08 15 04 72 33 27 14 34 09 45 59 34 68 49
31 16 93 32 43 50 27 89 87 19 20 15 37 00 49

假定這兩張表是真亂數,但是引用時,仍然會有決定哪個數值,索引值取捨的問題,你索引如何產生?如果還是時間來源的話,那依舊是陷入先前同樣的問題。


再接再厲,引入亂數表充當種子,再把前次結果加入種子,結果看起來似乎很亂了,改善了數字差3-4之間,規律遞增的問題,這樣的結果是可以接受,但是缺點是複雜了點...

 //亂數產生器-引入亂數表充當種子+反饋
int getRandom(int min,int max,int res){   
    int n,v,seed[]={
        29280,39655,18902,92531,90374, 7109,26627,59587,84340,98351,
        20123,82082,55477,22059,43168,12903,13436,25523,21090,73449,
        66405,35287,33248,67657, 7702, 1474,66068, 1125,59258,30138,
        97299,83419,13069,17826,76984,48906,10567,17829,  723,46700,
        83923,92076,98880,33942,46841,58731,36513,16681,88722,61984,
        11258,92175,94894,97606,11134,51941,43733,  514, 6694,27706    };   
    n=(time(0)+clock())%60;
    v=seed[n]+clock()+res;
    srand(v);
    return (min + rand()%(max-min+1));
}

執行結果,似乎很亂,這樣的結果是可以接受了。


完整測試列表

#include <cstdlib>
#include <iostream>
#include <iomanip.h>

using namespace std;

//亂數產生器-引入亂數表充當種子+反饋
int getRandom(int min,int max,int res){   
    int n,v,seed[]={
        29280,39655,18902,92531,90374, 7109,26627,59587,84340,98351,
        20123,82082,55477,22059,43168,12903,13436,25523,21090,73449,
        66405,35287,33248,67657, 7702, 1474,66068, 1125,59258,30138,
        97299,83419,13069,17826,76984,48906,10567,17829,  723,46700,
        83923,92076,98880,33942,46841,58731,36513,16681,88722,61984,
        11258,92175,94894,97606,11134,51941,43733,  514, 6694,27706    };   
    n=(time(0)+clock())%60;
    v=seed[n]+clock()+res;
    srand(v);
    return (min + rand()%(max-min+1));
}
//費式數列(Fibonacci)
double f(double n){   
    if(n<=2)return 1;   
    return f(n-1)+f(n-2);
}
int main(int argc, char *argv[]){
    int i,n,v,lo[49];
    long k=0;
    system("cls");
   
    cout << "亂數測試..." << endl;
    for(int i=0;i<49;i++){
        v=getRandom(1,49,lo[i]);
        f(i%31);  //減速器
        n=0;k=0;
        while(n<i){           
            if(lo[n++]==v){
                 v=getRandom(1,49,lo[i]);           
                f(i%11);  //減速器
                n=0;
            }
            k++;   //計次
        }
        lo[i]=v;       
        cout << setw(3) << setprecision(3) << lo[i] << " ";       
    }
    cout << "\n共計算了 " << k << " 次..." <<endl;

    system("PAUSE");
    return EXIT_SUCCESS;
}

沒有留言:

張貼留言

請提供您的寶貴意見 ;-)