这类问题的相同之处在于,数的大小超出了原生数据类型所能表示的范围。如果用Python或者Java,就不必再看下去了。。。。。。
1. 大数的模幂运算
给定x和y,求x的y次幂模k的余数
unsigned int quick_power_mod(unsigned int x, unsigned int y, unsigned int k)
{
unsigned int res = 1;
x %= k;
while(y != 0)
{
if(y & 1)
{
res = (res * x) % k;
}
y >>= 1;
x = (x * x) % k;
}
return res;
}
这种做法的道理如下,指数y的二进制表示为:
y = a 0 ⋅ 2 0 + a 1 ⋅ 2 1 + a 2 ⋅ 2 2 + ⋯ + a n ⋅ 2 n , a i ∈ { 0 , 1 } y = a_{0} \cdot 2^{0} + a_{1} \cdot 2^{1} + a_{2} \cdot 2^{2} + \cdots + a_{n} \cdot 2^{n}, a_{i} \in \{0, 1\} y=a0⋅20+a1⋅21+a2⋅22+⋯+an⋅2n,ai∈{0,1}
那么
x y = x a 0 ⋅ 2 0 + a 1 ⋅ 2 1 + a 2 ⋅ 2 2 + ⋯ + a n ⋅ 2 n = x a 0 ⋅ 2 0 ⋅ x a 1 ⋅ 2 1 ⋅ x a 2 ⋅ 2 2 ⋅ ⋯ ⋅ x a n ⋅ 2 n , a i ∈ { 0 , 1 } x^{y} = x^{a_{0} \cdot 2^{0} + a_{1} \cdot 2^{1} + a_{2} \cdot 2^{2} + \cdots + a_{n} \cdot 2^{n}} = x^{a_{0} \cdot 2^{0}} \cdot x^{a_{1} \cdot 2^{1}} \cdot x^{a_{2} \cdot 2^{2}} \cdot \cdots \cdot x^{a_{n} \cdot 2^{n}} , a_{i} \in \{0, 1\} xy=xa0⋅20+a1⋅21+a2⋅22+⋯+an⋅2n=xa0⋅20⋅xa1⋅21⋅xa2⋅22⋅⋯⋅xan⋅2n,ai∈{0,1}
又因为模运算有如下性质
( a ⋅ b )   m o d   c = ( ( a   m o d   c ) ( b   m o d   c ) )   m o d   c (a \cdot b)\bmod c = ((a \bmod c)(b \bmod c)) \bmod c (a⋅b)modc=((amodc)(bmodc))modc
于是我们有
x y   m o d   k = ( ( x a 0 ⋅ 2 0   m o d   k ) ⋅ ( x a 1 ⋅ 2 1   m o d   k ) ⋅ ( x a 2 ⋅ 2 2   m o d   k ) ⋅ ⋯ ⋅ ( x a n ⋅ 2 n   m o d   k ) )   m o d   k , a i ∈ { 0 , 1 } x^{y} \bmod k = ((x^{a_{0} \cdot 2^{0}} \bmod k) \cdot (x^{a_{1} \cdot 2^{1}} \bmod k) \cdot (x^{a_{2} \cdot 2^{2}} \bmod k) \cdot \cdots \cdot (x^{a_{n} \cdot 2^{n}} \bmod k)) \bmod k, a_{i} \in \{0, 1\} xymodk=((xa0⋅20modk)⋅(xa1⋅21modk)⋅(xa2⋅22modk)⋅⋯⋅(xan⋅2nmodk))modk,ai∈{0,1}
该算法即在按照上式从左到右一项一项的计算。体现为,遍历y的每一个二进制位,如果该位为1,将当前的计算结果乘以当前x的值并求余,如果该位为0,则不乘或者说乘以1;每遍历y的一位,将x做一次平方,对应着上式中依次乘2的x的指数。
2. 大数的进制转换
给定a进制的数x(字符串形式),转换为b进制的数y
// big-endian for input
// small-endian for output
unsigned int convert_base(std::vector<unsigned int> &from_num, std::vector<unsigned int> &to_num, unsigned int from_base, unsigned int to_base)
{
for(int i = 0; i < from_num.size(); )
{
int carry = 0;
for(int j = i; j < from_num.size(); j++)
{
int tmp = (from_num[j] + carry * from_base) % to_base;
from_num[j] = (from_num[j] + carry * from_base) / to_base;
carry = tmp;
}
to_num.push_back(carry);
while(from_num[i] == 0)
{
i++;
}
}
}
此处使用的是除k取余法,输入的vector也被改变了,用来存储每步计算的商。