随机数序列
来源:互联网 发布: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加密解密的基本原理
- 选取随机的初始大素数:(p,q),注意:p与q不相等
- 计算模数N:(N = p * q)
- 计算欧拉φ(n):(φ(n) = (p - 1) * (q - 1))
- 计算公钥指数e:(1 < e < φ(n) 且 gcd(e,φ(n)) = 1),即e与φ(n)互质,一般选择的e都是比较小的数,主要是为了提升计算速度,毕竟大数运算是很消耗时间的
- 计算私钥指数d:(e * d) % φ(n) = 1,即d是e的模反元素
- 组合密钥:(n,e)组成公钥,(n,d)组成私钥
- 加密:假设A是明文,C是密文,(C = A^e % N),即A的e次幂模N等于C。注意:如果e过大,很容易产生计算漏出,另外使用模幂运算,可有效减少计算漏洞并加速计算
- 解密:(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算法应用于随机数序列生成的基本原理
- **用户输入随机数序列范围:**Na,如Na = 4261413375,即生成1-4261413375范围的随机数序列
- 分解Na:由于Na是一个用户输入的自然数,而分解质因数是针对合数,另外分解后的p与q是质数并且不相等,并且大多数据情况分解出来的因数会是多个,所以需要循环累加分解,直到找到合适的p和q。通过测试对亿大小的数分解还是很快速的
- 得出p和q后,其它步骤与RSA算法加密流程一样。注意:随机数生成只需要使用到RSA加密部分,所以只需要计算出e即可
- 注意:如果在同一范围内,想要每次生成的随机数序列都不一样,可以改变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
总结
- 不得不说,数学家级的程序员就是叼,解决什么问题都可以那么优雅,很佩服他们
阅读全文
0 0
- 随机数序列
- 洗牌!!!随机数序列~!
- python 随机数,随机序列
- 求助:LCG随机数序列分析
- 用伪随机数生成器Random生成随机数序列
- 用伪随机数生成器Random生成随机数序列
- JavaScript生成指定范围的随机数和随机数序列
- 产生不相同的随机数序列
- 1~33之间的整数随机数序列
- 不重复随机数序列的生成
- 生成不重复的随机数序列
- 输出随机数1到511*511序列
- 随机数生成方法及序列随机排序
- 随机数、序列数生成、数组排序
- 数组生成序列不重复随机数
- 以日期时间加四位随机数作序列
- Random指定生成的随机数序列
- C++在一定范围内生成不重复随机数序列
- mysql正则表达式
- 文章标题
- python print输出延时,让其立刻输出
- amijor nonmontone linesearch
- Redis Scard 命令
- 随机数序列
- Problem G 小鑫爱运动
- C# Task WaitAll和WaitAny
- C++赋值运算符重载
- Tomcat企业级web应用服务器配置与实战
- python程序打包成可执行文件
- 算法题:直方图和0-1矩阵中最大矩形
- Java 学习之过错(一)
- 遍历多个字典组成的列表,将字典按目标key的值等或不相等分类,放进新的列表,实现字典key:value的索引