随机数序列

来源:互联网 发布:java macbook pro 编辑:程序博客网 时间:2024/06/01 10:27

  • 高效伪随机数序列生成方案
    • 前言
    • RSA算法简介
    • RSA加密解密的基本原理
    • RSA安全性
    • RSA算法生成随机序列的难点
    • RSA算法应用于随机数序列生成的基本原理
    • 具体C代码实现
  • 总结

高效伪随机数序列生成方案

  • 解决(不重复的生成上亿伪随机数),并且不需要记录和比对生成过的随机数

前言

  • 前段时间开发过一个快速端口扫描的模块,需求是要对大批量主机进行端口扫描。端口扫描技术已经是被无数人玩过的了,快速端口扫描主要的技术就是syn扫描,比较成熟。但在大批量扫描的时候,为了降低对目标安全设备的警报(常见的防火墙会通过连接并发数,端口的连接顺序,连接间隔时间来做警报),一般会选择随机的端口扫描。不过因为syn的特性,所以几个目标随机扫描基本没什么意义(发包太快),但是如果是大批量次的扫描,随机就会有意义。如有10个B段要扫描,每个B段的发包数量就是255*255*65535=4261413375(非常大,一般不会傻傻的扫描那么大),每次都随机的从10个段中选择一个段,然后从这个段中随机的选择一个目标,因为扫描的基数大,所以对每个目标重复扫描的间隔时间就会相对比较长,从而减少安全设备的警告
  • 正常情况,大多数人第一反应想到的是洗牌算法,或每次保存生成的随机数,第二次对生成的随机数进行去重查找,如果已经生成过,就继续生成,直到不重复。这些随机数序列生成算法在小范围内是非常高效的,但要生成上亿的随机数序列,并且每次生成的随机数都不重复,使用这些算法就会非常消耗内存和时间,基本不可接受。前期的做法是通过对一个大范围的段拆分为无数个小范围的段,然后一次对10或20个小范围的段进行随机扫描,这样对内存和时间的消耗勉强可以接受,但拆分的过程也是很消耗时间和空间的,整体效率和效果不高
  • 注意:因为IP段其实就是一段连续的无符号整数(在主机字节序的情况下),如一个B段(119.41.0.0-119.41.255.255)要扫描全端口(65535),他的扫描范围就是1-4261413375,只要从1-4261413375范围中随机选择一个数,就可以通过这个数计算出对应的IP与端口,所以只要有1-4261413375范围的随机数序列,就可以高效的进行随机扫描(前提是算法时空效率高)
  • 通过一段时间的查找,终于找到自己满意的算法:http://www.cnblogs.com/Geometry/archive/2011/01/25/1944582.html,这个算法的主要思路是通过加密与随机的相通性来实现。如将一个数加密为另一个数,这与随机算法达到的效果很相似,那么如果将1-4261413375中的每一个数都进行加密,就可以得到一个随机的序列。另外随机数的变化是通过初始种子,加密的变化是通过密钥,那么看起来通过改造一个加密算法来实现随机效果,是可以行的。
  • http://www.cnblogs.com/Geometry/archive/2011/01/25/1944582.html中是通过RSA算法来实现伪随机数序列生成,并且此加密算法的特性已经保证每次产生的随机数都是不重复的(有重复还咋解密),最主要的是,在生成1-N范围内的随机数时,生成N次,即可全覆盖1-N(N太大就需要使用大数运算),整体效果还是非常好。

RSA算法简介

  • 详细的RSA算法说明请参考:http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html
  • RSA是非对称加密算法,加密与解密使用不同的密钥,广泛用于混合加密,身份校验,数字签名等(如OperSSL)。公钥加密,私钥解密。私钥签名,公钥验证。

RSA加密解密的基本原理

  1. 选取随机的初始大素数:(p,q),注意:p与q不相等
  2. 计算模数N:(N = p * q)
  3. 计算欧拉φ(n):(φ(n) = (p - 1) * (q - 1))
  4. 计算公钥指数e:(1 < e < φ(n) 且 gcd(e,φ(n)) = 1),即e与φ(n)互质,一般选择的e都是比较小的数,主要是为了提升计算速度,毕竟大数运算是很消耗时间的
  5. 计算私钥指数d:(e * d) % φ(n) = 1,即d是e的模反元素
  6. 组合密钥:(n,e)组成公钥,(n,d)组成私钥
  7. 加密:假设A是明文,C是密文,(C = A^e % N),即A的e次幂模N等于C。注意:如果e过大,很容易产生计算漏出,另外使用模幂运算,可有效减少计算漏洞并加速计算
  8. 解密:(A = C^d % N),即C的d次幂模N等于A

RSA安全性

  • RSA的安全性主要体现在大数N分解难度,通过上面可看出e与d是密钥的关键,在知道了e与N的情况下,要计算出d就需要对N分解来得出p和q,目前公布的对N分解最高的二进制位数是768位。

RSA算法生成随机序列的难点

  • 利用RSA来做随机数序列生成的难点也是分解N,但这个N往往是非常小的(相对于加密的安全性来说),因为我们不需要考虑安全性,只需要考虑随机数序列的范围,所以也不是特别麻烦。另外我们只能得到一个随机数范围的参数如1-4261413375,此时Na=4261413375(假设Na为用户输入的随机数序列范围参数),需要对Na分解来得到p,q,然后重新计算出RSA的N。

RSA算法应用于随机数序列生成的基本原理

  1. **用户输入随机数序列范围:**Na,如Na = 4261413375,即生成1-4261413375范围的随机数序列
  2. 分解Na:由于Na是一个用户输入的自然数,而分解质因数是针对合数,另外分解后的p与q是质数并且不相等,并且大多数据情况分解出来的因数会是多个,所以需要循环累加分解,直到找到合适的p和q。通过测试对亿大小的数分解还是很快速的
  3. 得出p和q后,其它步骤与RSA算法加密流程一样。注意:随机数生成只需要使用到RSA加密部分,所以只需要计算出e即可
  4. 注意:如果在同一范围内,想要每次生成的随机数序列都不一样,可以改变e来实现

具体C++代码实现

  • 这里没有使用大数运算,在使用的时候需要注意。经过测试在1亿的范围内生成随机数序列是没问题的,如果有需要提高范围的,加入大数运算也不难,主要是在模幂运算的时候使用大数即可
  • 注意:如果n过小,就不能使用此算法生成随机序列,如n<=6的时候,因为无法计算有效的e,所以无法生成无序的随机数序列
  • 如果代码有什么问题或有更好的方法请告知
  • radom_integer_iterator.h 文件
#ifndef RADOM_INTEGER_ITERATOR_H_H#define RADOM_INTEGER_ITERATOR_H_H// 这是一个伪随机序列数迭代生成器,用于解决快速迭代1-n范围内的随机序列(这个n最好在1亿内,否则需要做大数运算),并且保证每次生成的随机数不重复,在迭代n次后,保证序列能覆盖1-n的所有数// 此随机序列迭代生成算法使用的是rsa加密算法(加密的特性使他天生具有生成随机数的能力),优点是速度快,内存占用极低// 此随机数适用场景多,比如用于随机扫描一个B段IP端口时,此随机迭代器就能非常完美的解决// 由于没有使用大数库,请注意n的范围,防止漏出// 只需要利用rsa的加密部分就可以实现,所以这只里需要计算出p,q,e和新的n就可以,不需要再计算d来解密#include <vector>using std::vector;#include <stdlib.h>#include <time.h>// 范围限制宏,即限制用户输入的n,最大不可以超过宏指定的数#define KOOK_MAX_RADOM_INTEGER_N 100000000namespace kook {    // 保存pq值    typedef struct _RSAPQ {        unsigned long long rsap;        unsigned long long rsaq;        _RSAPQ() :rsap(0), rsaq(0) {};    }rsapq;    // 随机序列迭代对象    // 当n<=6的时候,将不能产生出随机序列,因为无法生成有效的e    class radom_integer_iterator {    public:        radom_integer_iterator() : n(0), rn(0), re(0), i(1) {}        // 通过unsigned long来限制用户输入过大        radom_integer_iterator(unsigned long n);        // 分解质因数,把一个合数分解成若干个质因数的乘积的形式,即求质因数的过程叫做分解质因数(分解质因数只针对合数)        // 合数指自然数中除了能被1和本身整除外,还能被其他数(0除外)整除的数        static vector<unsigned long long> decompose_prime_factor(unsigned long long x);        // 判断一个数是否是质数        static bool is_prime_number(unsigned long long x);        // 计算 a * b % n        static unsigned long long mul_mod(unsigned long long a, unsigned long long b, unsigned long long n);        // 模幂运算是RSA的核心算法,直接决定RSA算法的性能,通常都是先将幂模运算转化为乘模运算        // 返回值 x = base^pow mod n        static unsigned long long pow_mod(unsigned long long base, unsigned long long pow, unsigned long long n);        // 辗转相除法来得出两整数的最大公约数        // 1. x%y得余数c        // 2. 若c=0,则y即为两数的最大公约数        // 3. 若c≠0,则x=y,y=c,再回去执行1        static unsigned long long calculate_common_divisor(unsigned long long x, unsigned long long y);        // 产生一个大于2小于n范围内的随机数,因为1<e>n        static int randint(int n);        // 重置n        bool reset_n(unsigned long n);        // 重置e,当范围n不变,但想改变随机序列的时候,就可以通过重置e来实现。注意:重置e,那么迭代i也将初始化        bool reset_e();        // 迭代随机序列数        // 如果返回结果为0,表示迭代结束,如果再继续迭代,将重复上一次的随机序列        unsigned long next();    private:        // 初始化随机参数,如n,p,q,e        bool init();        // 计算pq        rsapq findpq(vector<unsigned long long>& prime, unsigned long long x);        // 通过qp来得出e        unsigned long long finde(rsapq &pq);        // 用户指定的随机序列范围n        unsigned long long n;        // 新计算,用于随机算法的n,加密(c=i^re%rn)        unsigned long long rn;        // 用于随机算法的e        unsigned long long re;        // 保存pq值        rsapq rpq;        // 用于随机序列迭代的i,初始值为1        unsigned long long i;    };}#endif  
  • radom_integer_iterator.cpp 文件
#include "radom_integer_iterator.h"namespace kook {    radom_integer_iterator::radom_integer_iterator(unsigned long n) {        if (reset_n(n)) {            return;        }        // 如果重置失败就初始化        n = 0;        rn = 0;        re = 0;        i = 1;    }    vector<unsigned long long> radom_integer_iterator::decompose_prime_factor(unsigned long long x) {        vector<unsigned long long> prime_vector;        unsigned long long i = 0;        // 0和1不属于质数也不属于合数        if (0 == x || 1 == x) {            return prime_vector;        }        // 2和3本身就是质数(也不属于合数,最小的合数是4),无法再分解        if (2 == x || 3 == x) {            prime_vector.push_back(x);            return prime_vector;        }        // 分解质因数        for (i = 2; x != 1; i++) {            if (x%i == 0) {                x /= i;                prime_vector.push_back(i);                i--;    //i--和i++使得i的值不变,即能把N含有的所有的当前质因数除尽            }        }        return prime_vector;    }    bool radom_integer_iterator::is_prime_number(unsigned long long x) {        // 2和3本身就是质数        if (x <= 3) {            return (x > 1);        }        // 质数大于等于2 不能被它本身和1以外的数整除,这里通过整除2和3来快速判断(因为x在这里不可能等于2和3)        if (x % 2 == 0 || x % 3 == 0) {            return false;        }        // 循环判断        for (unsigned long long i = 5; i * i <= x; i += 6)        {            if (x % i == 0 || x % (i + 2) == 0) {                return false;            }        }        return true;    }    unsigned long long radom_integer_iterator::mul_mod(unsigned long long a, unsigned long long b, unsigned long long n) {        return a * b % n;    }    unsigned long long radom_integer_iterator::pow_mod(unsigned long long base, unsigned long long pow, unsigned long long n) {        unsigned long long a = base, b = pow, c = 1;        while (b)        {            while (!(b & 1))            {                b >>= 1; // a = a*a%n;                a = mul_mod(a, a, n);            } b--; // c = a*c%n;            c = mul_mod(a, c, n);        }        return c;    }    unsigned long long radom_integer_iterator::calculate_common_divisor(unsigned long long x, unsigned long long y) {        unsigned long long temp;        while (y != 0) {            temp = y;            y = x % y;            x = temp;        }        return x;    }    int radom_integer_iterator::randint(int n) {        n -= 1;        if (n < 2) return 0;        if (n == 2) return 2;        // 以时间截加n的地址为随机数种子        // 注意:在过快的生成随机数时,会出现大量重复        srand((unsigned int)time(0) + (unsigned int)&n + clock());        return (rand() % (n - 2)) + 2;    }    rsapq radom_integer_iterator::findpq(vector<unsigned long long>& prime, unsigned long long x) {        size_t psize = prime.size();        rsapq pq;        // 如果分解质数数量少于2就表示错误的n        if (psize < 2) {            return pq;        }        // 如果分解的质数数量等于2,并不相等,表示p和q成立        if (psize == 2) {            if (prime.at(0) != prime.at(1)) {                pq.rsap = prime.at(0);                pq.rsaq = prime.at(1);            }            return pq;        }        // 如果分解的质数数量大于2,就通过最笨的方法,遍历查找        // 之前p只取分解的最后一个质数,一般是最大的质数,但如果质数过大,在后期会造成生成的n过大        // (比如用户要生成1-100000000的随机序列,在分解质数后,新生成的n大于用户的范围,如果取最大质数,新生成的n会大于用户范围太多,会造成过多的空循环,影响效率)        // 所以这里取第一个,最小的质数        pq.rsap = prime.at(0);        // 通过除法来得出其它分解质数之积        pq.rsaq = x / pq.rsap;        // 遍历查找质数        while (!is_prime_number(++pq.rsaq)) {}        return pq;    }    unsigned long long radom_integer_iterator::finde(rsapq &pq) {        unsigned long long on = (pq.rsap - 1) * (pq.rsaq - 1);        // e必须在1-on之间,并且e与on互质        // 为了防止e过大,模幂效率过低,这里将e设置得尽量小,范围在1-65537        // 随机生成e的目的是为了每次生成同样范围的随机序列时,序列数的顺序不同,达到最大随机效果        int e = 0;        if (on > 65537) {            e = randint(65538);        }        else {            e = randint((int)on);        }        // 如果on过小,随机数为0,此时e为1,这样将不会产生随机序列        // 循环查找与on互质的e,这里不做e是否大于on的判断,因为最坏情况是e=on-1,因为两个连续的自然数一定是互质数        while (calculate_common_divisor((unsigned long long)e, on) != 1)        {            e++;        }        return (unsigned long long)e;    }    bool radom_integer_iterator::init() {        // 初始化随机算法参数,这里使用了一些低效的循环遍历,会比较慢,但他属于一次性运算        rn = n;        i = 1;        // 如果n<=6就不进行随机序列        if (n <= 6) {            re = 0;            rpq.rsap = 0;            rpq.rsaq = 0;            return true;        }        // 循环分解,直到找到合适的合数        vector<unsigned long long> test;        while (true)        {            test = decompose_prime_factor(rn);            rpq = findpq(test, rn);            if (rpq.rsap == 0 || rpq.rsaq == 0) {                rn++;                continue;            }            break;        }        // 重置rn        rn = rpq.rsap * rpq.rsaq;        // 计算e        re = finde(rpq);        return true;    }    bool radom_integer_iterator::reset_n(unsigned long n) {        if (n == 0 || n > KOOK_MAX_RADOM_INTEGER_N) {            return false;        }        this->n = (unsigned long long)n;        return init();    }    bool radom_integer_iterator::reset_e() {        if (n == 0 || rn == 0) {            return false;        }        // 初始序列计数        i = 1;        // 不对<=6的范围生成随机序列,但返回true        if (n <= 6) {            re = 0;            return true;        }        // 重新生成e        re = finde(rpq);        return true;    }    unsigned long radom_integer_iterator::next() {        if (n == 0 || rn == 0) {            return 0;        }        // 返回下一个随机序列数        unsigned long long c = 0;        // 当n==rn的时候,正常生成的随机序列范围是1-(n-1),因为当i=n的时候,c=0,所以这里直接判断        if (n == rn) {            if (i == rn) {                i++;                return (unsigned long)n;            }            else if (i > rn){                i = 1;                return 0;            }        }        else {            if (i >= rn) {                i = 1;                return 0;            }        }        // 当n<6的时候不生成随机序列        if (rn <= 6) {            return (unsigned long)i++;        }        // 循环生成随机序列,这里使用循环,是因为rn大于n的时候,要过滤超出n的数        while (true)        {            c = pow_mod(i++, re, rn);            // 如果c==0表示i>=rn,此时表示序列生成完毕。因为如果n==rn,这里将不可能出现i>=rn,如果这里出现i>=rn,那么肯定rn>n            if (c == 0) {                i = 1;                break;            }            if (c <= n) {                break;            }        }        return (unsigned long)c;    }}
  • test.cpp 对生成1亿范围内随机数序列测试
#include "radom_integer_iterator.h"#include <iostream>using std::cout;using std::endl;#include <algorithm>#include <functional>#ifdef WIN32#include <windows.h>#else#include <unistd.h>#include <stdio.h>#endifint main() {    while (true)    {        vector<unsigned long> test;        vector<unsigned long>::iterator pt;        unsigned long c, i;        unsigned long testc;#ifdef WIN32        Sleep(2000);#else        usleep(2000 * 1000);#endif        kook::radom_integer_iterator rii;        int nnn = rii.randint(100000000);        nnn += 90000000;        cout << "ssssssss: " << nnn << endl;        // 设定随机数序列范围        rii.reset_n(nnn);        while (true)        {            // 每次生成一个随机数,如果返回0表示一个循环生成完毕            testc = rii.next();            if (testc == 0) {                break;            }            test.push_back(testc);        }        // 对结果排序,方便检查是否有重复或漏掉        sort(test.begin(), test.end());        // 检查重复        for (pt = test.begin(); pt != test.end();) {            c = *pt;            pt++;            if (pt == test.end()) {                break;            }            i = *pt;            if (i - c == 1) {                continue;            }            else if (i - c == 0) {                // 重复了                cout << "aaaaaaaa: " << i << endl;                continue;            }            else {                // 漏网了                cout << "bbbbbbbb: " << c << " : " << i << endl;            }        }    }    return 0;}  
  • test50.cpp 生成1-50范围的随机数序列演示
#include "radom_integer_iterator.h"#include <iostream>using std::cout;using std::endl;int main() {    kook::radom_integer_iterator rii;    unsigned long testc = 0;    // 设定随机数序列范围    rii.reset_n(50);    while (true)    {        // 每次生成一个随机数,如果返回0表示一个循环生成完毕        testc = rii.next();        if (testc == 0) {            break;        }        cout << testc << " ";    }    return 0;} // 生成结果如下 1 14 19 22 35 34 25 18 13 26 21 12 9 2 27 20 17 8 3 16 11 4 7 10 15 28 29 30 43 48 6 5 47 42 50 41 38 31 49 46 37 32 45 40 33 24 23 36 39 44
  • teste.cpp 在不改变随机数序列生成范围的时候,通过重新计算e来使用每次生成的随机数序列不一样
#include "radom_integer_iterator.h"#include <iostream>using std::cout;using std::endl;int main() {    kook::radom_integer_iterator rii;    unsigned long testc = 0;    int i = 0, n = 3;    cout << endl << i << endl;    // 设定随机数序列范围    rii.reset_n(50);    while (i < n)    {        // 每次生成一个随机数,如果返回0表示一个循环生成完毕        testc = rii.next();        if (testc == 0) {            i++;            // 在不重新计算p,q,n的情况下,通过重新计算e来实现每次生成序列不一样            rii.reset_e();            if (i < n) {                cout << endl << endl << i << endl;            }        }        if (testc != 0) {            cout << testc << " ";        }    }    return 0;} // 测试重复3次的结果01 8 27 6 9 42 48 33 14 46 18 11 36 41 32 15 39 34 45 20 23 2 21 28 29 30 37 35 38 13 24 19 4 43 26 17 22 47 40 7 12 3 44 25 10 5 16 49 31 5011 48 21 42 33 22 49 44 35 18 31 12 5 32 24 17 11 43 38 7 45 8 39 28 29 30 19 50 13 4 20 15 6 47 2 41 34 3 26 46 27 40 23 14 9 36 25 16 37 1021 26 47 38 4 45 2 5 50 15 46 35 10 19 41 14 37 24 27 42 25 36 49 40 3 28 29 30 18 9 22 33 16 31 34 21 44 17 6 39 48 23 12 43 8 13 7 20 11 32

总结

  • 不得不说,数学家级的程序员就是叼,解决什么问题都可以那么优雅,很佩服他们