您的位置:首页 > 编程语言 > C语言/C++

同余幂的原理和C++实现,附赠一个10进制数转换为任意进制的数组的算法。

2009-11-12 12:26 771 查看
  18世纪末,高斯这个大牛定义了所谓同余的概念,这个东西在离散数学里几乎到处都是,作用也多的没话说,特别是现在很多加密算法都有用到。而这个同余幂也是基于同余中的一个小知识,主要还是因为能够比较方便的计算非常大的整数的求幂再求模,所以比较不小心就会用到。所以今天有空就专门写了一个函数的形式,方便以后随时取用。同时,因为要进行快速同余幂的计算必须要使用对10进制数字的二进制展开,我也就顺便写了一个能够把10进制数据按任意基数展开的函数当然其实返回的是个vector而不是数组。  

这里先从同余简单开始介绍下:

  a≡b(mod m) iff(当且仅当)a mod m = b mod m。a≡b(mod m) 就读作:a模m同余b。这里,mod表示取模运算,比如3 mod 2就是3/2的余数1了。

  距离来说,比如17≡5(mod 6) ,17模6同余于5. 因为17/6余5,而5/6余5。

  

  同余关系和加法乘法的运算还是同构的,其实就是a + c b + d (mod m),和 a*c b*d(mod m);

下面说同余幂:

  我们经常会需要快速的求出bn mod m,其中 b n m都是比较大的整数,比如b=12345 n=5567,这样去直接计算显然是不可行的,所以我们把n进行二进制展开(就是转换为二进制)比如这个n就变成了1010110111111,这样每次只需要求b mod m,b2 mod m,... b2^(k-1) mod m,然后把对应位置上的二进制是1的项乘起来。在每次乘完以后求除m的余数即可。

int ModularExponentiation(int base, int exp_in_bin, int modular);
vector<short> baseBexpansion(int num, short b);

int main(){
//测试,2413的16进制展开应该是96D
//因为没有修改显示,所以会显示成9613
vector<short> a = baseBexpansion(2413, 16);
//用迭代器显示结果
for(vector<short>::iterator it = a.begin(); it != a.end(); it++){
cout<< *it;
}

cout<<endl;
//测试数据,981^937 mod 2537的结果应该是704
int x = ModularExponentiation(981, 937,2537);
cout <<x <<endl;
}

/**
* 计算 base^exp mod modular这个同余幂的值。
* base:底数
* exp_in_bin:指数
* modular:模
* return: 同余幂
*/
int ModularExponentiation(int base, int exp, int modular){
//把exp进行二进制展开
vector<short> n = baseBexpansion(exp, 2);
int x = 1; //x = base^exp mod modular;

int power = base % modular;
for(int i = n.size() - 1; i > -1 ; i --){
if( n[i] ) { //if n[i] == i
//从二进制展开后的最右端开始求
x = (x * power)% modular;
}
//求b^(2^(k-1)) mod m的值
power = (power * power) % modular;
}

return x;
}

/**
* 计算数字num的b进制展开形式的数组
* num:将被展开的数字
* b:数字展开的基
* return:数字展开后的向量,按照从左往右的顺序存储,如13的二进制展开为1101,存储的顺序也是{1,1,0,1}
*/
vector<short> baseBexpansion(int num, short b){
int q = num, i = 0, temp = 0;
vector<short> a;
while(q != 0){
a.push_back(q % b);
q /= b;
}

//反转a
int size = a.size();
for(; i < size/2; i ++){
temp = a[i];
a[i] = a[size-1-i];
a[size-1-i] = temp;
}
return a;
}


实际上,因为这个算法里面,因为把求b的n次幂换成了求某个数的2次幂形式,渐而大大的降低了计算的复杂度,比如对于12345678987654321这样一个指数,它的二进制形式是:101011110111000101010001100010100100011111010010110001,所以最大要计算的数也不过是9007199254740992*9007199254740992= 81129638414606681695789005144064(实际上,这个计算的数决定于modular的值,当modular没有这么大的时候,显然不可能要算这么大的数字), 而原来要对一个b计算12345678987654321次幂,即时b是2,在超过5位数的次幂之后几乎就已经无法用我们c++的double类型计算了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: