一个打乱列表获得伪随机序列的生成器

一个打乱列表获得伪随机序列的生成器

  • 偶得一代码,再晚也要记录下来。最近看一本叫游戏编程的书,里面讲到了一种用伪随机的方式实现的洗牌算法。
算法思想是这样的:
算法利用质数和二次方程的数学特性,该算法需要一个质数,他应该大于要遍历的集合元素个数。如果集合中有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 
#include 

//#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 
#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;
}

执行结果如下
一个打乱列表获得伪随机序列的生成器_第1张图片

你可能感兴趣的:(学习笔记)