Chinaunix首页 | 论坛 | 博客
  • 博客访问: 990946
  • 博文数量: 158
  • 博客积分: 4380
  • 博客等级: 上校
  • 技术积分: 2367
  • 用 户 组: 普通用户
  • 注册时间: 2006-09-21 10:45
文章分类

全部博文(158)

文章存档

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;
写得完整点是:

typedef unsigned int T;
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 + (n*c+d)%c <= n*c + d;
    c + d <= n*c + d;
    1<=n
)

最终代码是:

// typedef unsigned long long T;
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 的结果具有周期性。
但我没找到合适的算法求得这个周期,以及周期开始处

阅读(1552) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

网友评论2012-11-26 15:25:05

fofolk
windy0will 说 a^b%c 的结果具有周期性:YES,没有规律(没有公因子时,会有C个结果,不然小于C个)

有个算法叫做哥德蒙利算法。
Exp(a,b)%c是RSA的基本运算