求 a的b次方对c求余 的结果,其中a b c都是非负整数
求 a的b次方 最简单的方法是
s = 1;
for( i=0; i!=b; ++i ) s*=a;
return s;
这个算法效率太低,假如只能想到这个算法,出门别说自己是搞软件的^_^
考虑到 a^b = (a的平方)^(b的一半) 这个公式,可以写成
if(b==0) return 1;
if(b==1) return b;
s = 递归( a*a, b/2 );
if(b%2==1) s*=a;
return s;
这个递归算法在long long取值范围内,最多递归64次,是可以接受的。
追求完美的人是不用递归的
递归时 a^21 = (a^2)^10 * a = ((a^2)^2)^5 * a = ((a^2)^2)^2 * ((a^2)^2) * a
从中可以看出是不是需要再多乘一个自身,取决于21的二进制10101中对应bit是否为1
即算法为:
s = 1;
for( t=a; b; t*=t,b>>=1 ) if(b&1)s*=t;
return s;
写得完整点是:
T foo( T a, T b ) // 求a的b次方
{
T s = 1 ;
for ( T t = a; b; t *= t,b >>= 1 )
{
if ( (b & 1 ) != 0 )
s *= t;
}
return s;
}
在上述所有代码中,都用到乘法运算,即数据可能溢出
解决方法是通过公式 (a*b)%c = ( a%c * b%c )%c 使中间值(即a%c,b%c)保持在一定范围内。
估计这个公式是题目唯一的考点,假如a,b在uint64_t范围内取值,那么c可以在uint32_t范围内取值而不出现溢出
所以我窃以为题目没写完整,应该有个对c取值范围的限制
假如对c不作限制,我将(a*b)%c转化为(a+a+a+……共b个)%c,用(a+b)%c=(a%c+b%c)%c使得中间值(即a%c,b%c)保持在一定范围内。
此时只要保证(a+b)%c不溢出就行了,我搞了好久,算法都不简洁,给出两个差强人意的算法
if(a+b>=a) return (a+b)%c;
return (a%c+b%c-c);
这个算法要点在于,形如a+b-c,如果最终结果不溢出,则不会因为a+b临时溢出而使最终结果变得错误。
if(a+b>=a) return (a+b)%c;
return ((a+b)%c + m%c + 1)%c;
式中m指类型的最大值,比如uint32_t是0xFFFFFFFFu,可以用uint32_t(-1)来表示
(
求证 a<=m, b<=m, c<=m 时 (a+b)%c + m%c + 1 <= m
因为 (a+b)%c+1 最大值为 c
所以只需要求证 c + m%c <= m
设 m = n*c + d,其中n>=1,d<c,则上式变为
c + (n*c+d)%c <= n*c + d;
c + d <= n*c + d;
1<=n
)
最终代码是:
typedef unsigned int T;
T MyPlus( T a, T b, T c ) // (a+b)%c
{
if (a + b >= a) return (a + b) % c; // 不溢出
return (a % c + b % c - c);
}
T MyMultiplies( T a, T b, T c ) // (a*b)%c
{
if (a * b / a == b) return (a * b) % c; // 不溢出
T s = 0 ;
for ( T t = a; b; t = MyPlus(t,t,c),b >>= 1 )
{
if ( (b & 1 ) != 0 )
s = MyPlus(s,t,c);
}
return s;
}
T MyPower( T a, T b, T c ) // (a^b)%c
{
T s = 1 ;
for ( T t = a; b; t = MyMultiplies(t,t,c),b >>= 1 )
{
if ( (b & 1 ) != 0 )
s = MyMultiplies(s,t,c);
}
return s;
}
#include < iostream >
int main( void )
{
std::cout << MyPower( 79 , 13 , 127 ) << std::endl; // 26
return 0 ;
}
不怎么好验证,所以没测试
进一步,windy0will 说 a^b%c 的结果具有周期性。
但我没找到合适的算法求得这个周期,以及周期开始处
http://blog.vckbase.com/bruceteen/archive/2010/07/15/50550.html