模指数运算&素性测试

模指数运算与素性测试是密码学中经常要用到的学法,掌握一些基本的实现原理有助于后续的学习。

模指数运算

法1

计算ae mod m , 底数a , 指数e , 模数m

常应用于RSA密码

例:计算290 mod 13

可用pow(base , exponent) , 但结果容易溢出


法2

循环指数次,每次都乘底数再%m , 边乘边模

复杂度O(e)


法3

分 - 治 - 归并

  • 分:290 = 250*240
  • 治:250 mod 13 = 3 , 240 mod 13 = 4
  • 归并: 290 mod 13 = 3*4 mod 13 = 12

上例只分了一次,其实还可以往下分的,那么就需要制定一个分拆的规则

以二进制方式分拆指数:e = 1010100...(e对应的二进制) = Dn-1+Dn-2+....+D0

而aDn-1+Dn-2+....+D0 =aDn-1 * aDn-2 * ..... * aD0

举个栗子: 计算5117 mod 19

  • 分拆指数:117 = 1 + 4 + 16 + 32 + 64
  • 分治计算:51 mod 19 = 5
  • 52 mod 19 = 6
  • 54 mod 19 = 17
  • 58 mod 19 = 4
  • 516 mod 19 = 16
  • 532 mod 19 = 9
  • 564 mod 19 = 5
  • 归并:分拆指数对应的结果相乘再mod19(边乘边模
  • = 5 * 17 * 16 * 9 * 5 mod 19 = 1

复杂度O(log2e)

代码实现

size_t mod_exp(size_t a, size_t e, size_t m)
{
	// TODO: return the answer of a^e mod m";
	size_t res = 1;

	//遍历e的二进制
    while(e != 0){
        if(e%2 == 1){
            res = (res*a)%m;
        }
        a = (a*a)%m;
        e >> 1;
        
    }
	return res;
}

素性测试

给定一个数n 判断其是否为素数。

数学应用:所有合数都可由素数的乘积表示,且这些素因子按大小排列后是唯一的。

检测方法

法1

根据定义 , 遍历2 - n看是否有数整除n


法2

遍历2 - n1/2即可

因为如果存在j > n1/2 是 n 的因子的话,那么一定会有i 也是n的因子且 i < n1/2

所以只需要跑2 - 根n就行了


法3

找出2 - n1/2中的素数,遍历这些素数是否整除n即可

因为任意合数都可以表示为素数的乘积,判断合数就重复判断了。

基于法3我们可以写出大致检测的步骤:

  • 确定待筛集合{2,3,4.........,n1/2}
  • 基于{2,3,4.........,n1/2}的Eratosthenos筛选
  • 筛选后的元素与n整除判定

Eratosthenos筛选法:

产生2 - N范围内的所有素数,不适用于产生某个特定范围内的全部素数。

大致过程:

  • 取第一个素数2,删掉{2...,N}中除2以为所有的2的倍数
  • 认定2的下一个数(未被删掉)为素数(这里为3),在后面的整数中删掉该数的倍数
  • 重复第二步。直到后面没有数可以删了。

举个栗子:判断2543是否为素数

  1. 25431/2 ≈ 50 , 确定待筛集合{2,3,4,5...50}
  2. 使用Eratosthenos筛选法在该集合中筛选出所有素数:2,3,5,7,11,13,17....,47
  3. 整除判定,2543整除这15个素数,结果都不是整数,那么2543一定为素数

代码实现:

bool prime_test(unsigned int a)
{
	//TODO: return true when "a" is a prime | False when "a" is not a prime";
	size_t sqr = (size_t)sqrt((double)a);
	
	//0、1、2比较特殊 需要特判下
	if (a == 1 || a ==0)	return false;
	if (a == 2)				return true;

	//取2 - sqr个空间
	//并初始化为0
	int* num;
	num = new int[sqr + 10];
	memset(num, 0, sqr + 10);


	//遍历2 - sqr个空间
	//若发现没有筛过则假定其为素数
	//再遍历其后的数并将能整除该数的数标记为1
	for (int i = 2; i <= sqr; i++)
	{
		if (num[i] == 0)
		{
			for (int j = i * i; j <= sqr; j++)
			{
				if (j % i == 0) num[j] = 1;
			}

		}
	}

	//标记为0的数为筛出的素数
	bool flag = true;
	for (int i = 2; i <= sqr; i++)
	{
		if (num[i] == 0 && a % i == 0)
		{
			flag = false; 
			break;
		}
	}
    
	delete []num;
	return flag;
}