一個打亂列表獲得僞隨機序列的生成器

一個打亂列表獲得僞隨機序列的生成器

  • 偶得一代碼,再晚也要記錄下來。最近看一本叫遊戲編程的書,裏面講到了一種用僞隨機的方式實現的洗牌算法。
算法思想是這樣的:
算法利用質數和二次方程的數學特性,該算法需要一個質數,他應該大於要遍歷的集合元素個數。如果集合中有10個元素,該指數爲11,當然這個算法不會生成質數;它只是將一組選定的質數放在一個查詢表中。如果需要比較大的質數可以另行生成。其工作原理如下:用3個大於0的隨機數計算出一個跳躍值(skip)。這些隨機數作爲二次方程的係數,而閾值(x)被設置爲集合的元素個數;
跳躍值 = 隨機數A x (元素個數 x 元素個數)+(隨機數B x 元素個數)+隨機數C
skip = RandomA * (members * members)+(RandomB * members)+RandomC
有了跳躍值(skip)後,就可以使用下面的代碼,以僞隨機順序遍歷一次整個集合;
nextMember += skip
nextMember %= prime
skip的值要比集合元素個數大得多,因此選擇的值好像是隨機跳躍一樣

直接上代碼吧

//PrimeSearch.h
#pragma once
class PrimeSearch
{
    static int prime_array[];

    int skip;
    int currentPosition;
    int maxElements;
    int *currentPrime;
    int searches;

public:
    PrimeSearch(int elements);
    int GetNext(bool restart = false);
    bool Done() { return (searches == *currentPrime); }
    void Restart() { currentPosition = 0; searches = 0; }
};
//PrimeSearch.cpp
#include "PrimeSearch.h"
#include <stdlib.h>
#include <assert.h>

//#define GCC_ASSERT(a,b) assert(a);
/////////////////////////////////////////////////////////////////////////////
// DEBUG info
/////////////////////////////////////////////////////////////////////////////

#define max(a,b)            (((a) > (b)) ? (a) : (b))

int PrimeSearch::prime_array[] =
{
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
    53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 
    109, 113, 127, 131, 137, 139, 149,  151, 157, 163, 167, 
    173, 179, 181, 191, 193, 197, 199,  211, 223, 227, 229, 
    233, 239, 241,  251, 257, 263, 269, 271, 277, 281, 283, 
    293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 
    367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 
    433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 
    499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 
    577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 
    643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 
    719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 
    797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 
    863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 
    947, 953, 967, 971, 977, 983, 991, 997,

    // after 1000, for space efficiency reasons, 
    // we choose to include fewer prime numbers the bigger the number get.

    1009, 1051, 1103, 1151, 1201, 1259, 1301, 1361, 1409, 1451,
    1511, 1553, 1601, 1657, 1709, 1753, 1801, 1861, 1901, 1951,
    2003, 2053, 2111, 2113, 2153, 2203, 2251,
    2309, 2351, 2411, 2459, 2503, 2551, 2557,
    2609, 2657, 2707, 2753, 2767, 2801, 2851, 2903, 2953,
    3001, 3061, 3109, 3163, 3203, 3251, 3301, 3359, 3407, 3457,
    3511, 3557, 3607, 3659, 3701, 3761, 3803, 3851, 3907, 3967,
    4001, 4051, 4111, 4153, 4201, 4253, 4327, 4357, 4409, 4451,
    4507, 4561, 4603, 4651, 4703, 4751, 4801, 4861, 4903, 4951,

    // begin to skip even more primes

    5003, 5101, 5209, 5303, 5407, 5501, 5623, 5701, 5801, 5903,
    6007, 6101, 6211, 6301, 6421, 6521, 6607, 6701, 6803, 6907,
    7001, 7103, 7207, 7307, 7411, 7507, 7603, 7703, 7817, 7901,
    8009, 8101, 8209, 8311, 8419, 8501, 8609, 8707, 8803, 8923,
    9001, 9103, 9203, 9311, 9403, 9511, 9601, 9719, 9803, 9901,

    // and even more
    10007, 10501, 11003, 11503, 12007, 12503, 13001, 13513, 14009, 14503,
    15013, 15511, 16033, 16519, 17011, 17509, 18013, 18503, 19001, 19501,
    20011, 20507, 21001, 21503, 22003, 22501, 23003, 23509, 24001, 24509

    // if you need more - go get them yourself!!!!
    // Create a bigger array of prime numbers by using this web site: 
    // http://www.rsok.com/~jrm/printprimes.html
};


PrimeSearch::PrimeSearch(int elements)
{
    // "Can't do a PrimeSearch if you have 0 elements to search through, buddy-boy"
    assert(elements>0);

    maxElements = elements;

    int a = (rand() % 13) + 1;
    int b = (rand() % 7) + 1;
    int c = (rand() % 5) + 1;

    skip = (a * maxElements * maxElements) + (b * maxElements) + c;
    skip &= ~0xc0000000;       // 防止跳躍距離過大

    Restart();

    currentPrime = prime_array;
    int s = sizeof(prime_array) / sizeof(prime_array[0]);

    // if this GCC_ASSERT gets hit you didn't have enough prime numbers to deal with this number of 
    // elements. Go back to the web site.
    assert(prime_array[s - 1]>maxElements);

    while (*currentPrime < maxElements)
    {
        currentPrime++;
    }

    int test = skip % *currentPrime;
    if (!test)
        skip++;
}

int PrimeSearch::GetNext(bool restart)
{
    if (restart)
        Restart();

    if (Done())
        return -1;

    bool done = false;

    int nextMember = currentPosition;

    while (!done)
    {
        nextMember = nextMember + skip;
        nextMember %= *currentPrime;
        searches++;

        if (nextMember < maxElements)
        {
            currentPosition = nextMember;
            done = true;
        }
    }
    return currentPosition;
}
//main.cpp
#include <iostream>
#include "PrimeSearch.h"

int main(int argc, char *argv)
{
    int n = 10;
    PrimeSearch pSearch = PrimeSearch(n);
    while (true)
    {
        int getn = pSearch.GetNext();
        if (-1 == getn)
        {
            break;
        }
        std::cout << getn << " ";
    }
    std::cout << std::endl;
    return 0;
}

執行結果如下
執行結果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章