2012年(158)
分类: C/C++
2012-11-26 15:24:31
求 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;
写得完整点是:
在上述所有代码中,都用到乘法运算,即数据可能溢出
解决方法是通过公式 (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 + d <=
n*c + d;
1<=n
)
最终代码是:
不怎么好验证,所以没测试
进一步,windy0will 说 a^b%c
的结果具有周期性。
但我没找到合适的算法求得这个周期,以及周期开始处