关于数的高次幂运算取余,解决大数溢出问题

博客主要探讨幂取模运算问题。当幂运算结果很大时,先求幂再取模易导致数溢出。文中引用多个基础数学公式并进行证明,还介绍了大数分解方法,将指数转化为二进制求解幂取模,最后提及算法实现及高效幂取余运算复杂度为O(1)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

解决思路

当一个幂运算很大,而模为整型数时,通常的做法(先求幂再取模),结果很大可能就是数溢出,无法表示这样的大数,导致运算失败。 可以先试着将数分割成几个部分,然后一个部分一部分的求取模数,从而实现目的。

引用基础数学公式

可以用数学公式解决的问题,千万别去计算能力暴力解决。因为可以用公式解决的问题,时间空间的复杂度很可能直接就是O(1)

公式1证明:

数学公式:
( a ∗ b ) m o d C = ( ( a m o d C ) ∗ b ) m o d C ; ( a , b , C 皆是是整数 ) (a * b ) \quad mod \quad C = (( a \quad mod \quad C) * b) \quad mod \quad C; (a, b, C 皆是是整数) (ab)modC=((amodC)b)modC;(a,b,C皆是是整数)
设:a=m+k, m 的值为 C 的 n 倍数(n>=0,且为整数), k < C
则:
( a ∗ b ) m o d C = ( ( m + k ) ∗ b ) m o d C = ( m b + k b ) m o d C = ( C n b + k b ) m o d C = k b m o d C \begin{align*} (a * b ) \quad mod \quad C&= ( ( m + k ) * b ) \quad mod \quad C \\ &= ( mb + kb ) \quad mod \quad C \\ &= ( Cnb + kb ) \quad mod \quad C \\ &= kb \quad mod \quad C \end{align*} (ab)modC=((m+k)b)modC=(mb+kb)modC=(Cnb+kb)modC=kbmodC
因为:
k = a m o d C k = a \quad mod \quad C k=amodC
所以:
( a ∗ b ) m o d C = ( ( a m o d C ) ∗ b ) m o d C (a * b ) \quad mod \quad C = ( ( a \quad mod \quad C ) * b ) \quad mod \quad C (ab)modC=((amodC)b)modC

公式2证明:

数学公式
a ∗ b m o d C = ( a m o d C ) ∗ ( b m o d C ) m o d C ( a , b , C 皆是是整数 ) a * b\quad mod \quad C = ( a\quad mod \quad C) * ( b\quad mod \quad C) \quad mod\quad C (a, b, C 皆是是整数) abmodC=(amodC)(bmodC)modC(a,b,C皆是是整数)
设: a = m + k 1 , b = n + k 2 , m , n 的值为 C 的 x 倍数( x > = 0 ,且为整数) , k 1 , k 2 < C a=m+k_1, b=n+k_2, m,n的值为 C 的 x 倍数(x>=0,且为整数), k_1,k_2 < C a=m+k1,b=n+k2,m,n的值为Cx倍数(x>=0,且为整数),k1,k2<C
则:
a ∗ b = ( m + k 1 ) ∗ ( n + k 2 ) = m n + m k 2 + k 1 n + k 1 k 2 由于 m 和 n 都是 C 的 x 倍得到: = C x 1 C x 2 + C x 1 k 2 + k 1 C x 2 + k 1 k 2 m o d C = k 1 k 2 m o d C k 1 = a m o d C k 2 = b m o d C 可得: a ∗ b m o d C = ( a m o d C ) ∗ ( b m o d C ) m o d C \begin{align*} a*b&=(m+k_1)*(n+k_2)\\ &=mn+mk_2+k_1n+k_1k_2 \\ 由于&m和n都是C的x倍 得到:\\ &=Cx_1Cx_2+Cx_1k_2+k_1Cx_2+k_1k_2 \quad mod \quad C\\ &=k_1k_2\quad mod \quad C\\ k_1&=a \quad mod \quad C\\ k_2&=b \quad mod \quad C\\ 可得:\\ a * b\quad mod \quad C &= ( a\quad mod \quad C) * ( b\quad mod \quad C) \quad mod\quad C \end{align*} ab由于k1k2可得:abmodC=(m+k1)(n+k2)=mn+mk2+k1n+k1k2mn都是Cx倍得到:=Cx1Cx2+Cx1k2+k1Cx2+k1k2modC=k1k2modC=amodC=bmodC=(amodC)(bmodC)modC

公式3证明:

数学公式:
a b m o d C = ( a m o d C ) b m o d C ( a , b , C 皆是是整数 ) a ^ b\quad mod \quad C = ( a\quad mod \quad C) ^ b \quad mod\quad C (a, b, C 皆是是整数) abmodC=(amodC)bmodC(a,b,C皆是是整数)

设: a = m + k , m 的值为 C 的 n 倍数( b , n > = 0 ,且为整数) , k < C a=m+k, m 的值为 C 的 n 倍数(b, n>=0,且为整数), k < C a=m+k,m的值为Cn倍数(b,n>=0,且为整数),k<C

则根据牛顿二项式展开得到:
a b m o d C = ( m + k ) b m o d C = ( ∑ i = 0 b d i m b − i k i ) m o d C = ( d 0 m b + d 1 m b − 1 k + d 2 m b − 2 k 2 + d 3 m b − 3 k 3 + ⋯ + d b − 1 m k b − 1 + d b k b ) m o d C d 0 , d 1 , d 2 , d 3 ⋯ d b > 0 且都是整数(牛顿二项式系数) \begin{align*} a ^ b \quad mod \quad C &= ( m + k ) ^ b \quad mod \quad C \\ &=\bigg( \displaystyle\sum_{i=0}^b d_im^{b-i}k^i \bigg) \quad mod \quad C\\ &= ( d_0m^b + d_1m^{b-1}k + d_2m^{b-2}k^2 + d_3m^{b-3}k^3 + \cdots + d_{b-1}mk^{b-1} + d_bk^b ) \quad mod \quad C \\ \\ d_0,d_1,d_2,d_3 \cdots d_b >0 &且都是整数(牛顿二项式系数) \\ \end{align*} abmodCd0,d1,d2,d3db>0=(m+k)bmodC=(i=0bdimbiki)modC=(d0mb+d1mb1k+d2mb2k2+d3mb3k3++db1mkb1+dbkb)modC且都是整数(牛顿二项式系数)
因为 d 0 , d b = 1 d_0,d_b=1 d0,db=1 所以得到:
( m b + d 1 m b − 1 k + d 2 m b − 2 k 2 + d 3 m b − 3 k 3 + ⋯ + d b − 1 m k b − 1 + k b ) ( m^b + d_1m^{b-1}k + d_2m^{b-2}k^2 + d_3m^{b-3}k^3 + \cdots + d_{b-1}mk^{b-1} +k^b ) \\ (mb+d1mb1k+d2mb2k2+d3mb3k3++db1mkb1+kb)
前 b 项全换成C的倍数形式,得到
( ( C n ) b + d 1 ( C n ) b − 1 k + d 2 ( C n ) b − 2 k 2 + d 3 ( C n ) b − 3 k 3 + ⋯ + d b − 1 ( C n ) k b − 1 + k b ) m o d C = k b m o d C k = a m o d C \begin{align*} & ( (Cn)^b + d_1(Cn)^{b-1}k + d_2(Cn)^{b-2}k^2 + d_3(Cn)^{b-3}k^3 + \cdots + d_{b-1}(Cn)k^{b-1} +k^b ) \quad mod \quad C \\ & = k^b \quad mod \quad C \\ k&= a \quad mod \quad C \\ \end{align*} k((Cn)b+d1(Cn)b1k+d2(Cn)b2k2+d3(Cn)b3k3++db1(Cn)kb1+kb)modC=kbmodC=amodC
所有的C的倍数全被排除了,所以:
a b m o d C = ( a m o d C ) b m o d C \begin{align*} a^b \quad mod \quad C = ( a \quad mod \quad C ) ^ b \quad mod \quad C \end{align*} abmodC=(amodC)bmodC

猜想,但我无法证明

任意一个质数a, 任何一个小于a且大于0的整数d,
则有: d a m o d a = d d^a \quad mod \quad a = d damoda=d

大数分解

如: a b a^b ab mod C 求解 (a, b, C 皆是是整数)

  1. 首先把 b 转化成二进制
    如: b 0 , b 1 , b 2 , b 3 , b 4 ⋯ b 31 b_0, b_1, b_2, b_3, b_4 \cdots b_{31} b0,b1,b2,b3,b4b31
    即: b = b 0 ∗ 2 31 + b 1 ∗ 2 30 + b 2 ∗ 2 29 + ⋯ + b 31 ∗ 2 0 ; b = b_0*2^{31} + b_1*2^{30} + b_2*2^{29} + \cdots + b_{31}*2^0; bb0231+b1230+b2229++b3120;
    也就是把
    a b = a ( b 0 ∗ 2 31 + b 1 ∗ 2 30 + ⋯ + b 31 ) = a ( b 0 ∗ 2 31 ) ∗ a ( b 1 ∗ 2 30 ) ∗ a ( b 2 ∗ 2 29 ) . . . . . ∗ a ( b 31 ∗ 2 0 ) ; \begin{align*} a^b &= a ^{ (b_0*2^{31} + b_1*2^{30}+ \cdots + b_{31}) } \\ &= a^{(b_0*2^{31})} * a^{(b_1*2^{30})} * a^{(b_2*2^{29})} ..... * a^{(b_{31}*2^0)}; \end{align*} ab=a(b0231+b1230++b31)=a(b0231)a(b1230)a(b2229).....a(b3120);
    例如: 8 25 8^{25} 825 mod 5 ; 25转化成二进制为11001,即:
    8 25 = 8 ( 1 ∗ 2 16 ) ∗ 8 ( 1 ∗ 2 8 ) ∗ 8 ( 0 ∗ 2 4 ) ∗ 8 ( 0 ∗ 2 2 ) ∗ 8 ( 1 ∗ 2 0 ) = 8 16 ∗ 8 8 ∗ 8 0 ∗ 8 0 ∗ 8 1 = 8 1 ∗ 8 0 ∗ 8 0 ∗ 8 8 ∗ 8 16 \begin{align*} 8^{25} &= 8^{(1*2^{16})} * 8^{(1*2^8)} * 8^{(0*2^4)} * 8^{(0*2^2)} * 8^{(1*2^0)} \\ &= 8^{16} * 8^8 * 8^0 * 8^0 * 8^1 \\ &= 8^1 * 8^0 * 8^0 * 8^8 * 8^{16} \end{align*} 825=8(1216)8(128)8(024)8(022)8(120)=81688808081=81808088816

所以:
8 25 m o d 5 = ( 8 1 ∗ 8 0 ∗ 8 0 ∗ 8 8 ∗ 8 16 ) m o d 5 \begin{align*} 8^{25} \quad mod \quad5 &= (8^1 * 8^0 * 8^0 * 8^8 * 8^{16}) \quad mod \quad5 \end{align*} 825mod5=(81808088816)mod5
程序逻辑实现:
以指数为25时11001为例:

循环权重赋值取余二进制值说明
11W= a % cW=aresult = (result * W) % c;1该位值为1,所以将当前权重值保存到result中
22W= (W* W) % cW= a 2 a^2 a2*0为了计算高阶的值,继续计算权重
该位值为0,result不需要这个值
34W= (W* W) % cW= a 4 a^4 a4*0为了计算高阶的值,继续计算权重
该位值为0,result不需要这个值
48W= (W * W) % cW= a 8 a^8 a8result = (result * W) % c1为了计算高阶的值,继续计算权重
该位值为1,所以将当前权重值保存到result中
516W= (W * W) % cW= a 16 a^{16} a16result = (result * W) % c1为了计算高阶的值,继续计算权重
该位值为1,所以将当前权重值保存到result中

指数=1+2+8+16,相对a来说,就是result= a 1 ∗ a 2 ∗ a 8 ∗ a 16 m o d c a^1*a^2*a^8*a^{16} \quad mod \quad c a1a2a8a16modc

根据上述的公式可以推导出:
设: r e s u l t = 8 1 m o d 5 = 8 m o d 5 = 3 8 2 m o d 5 等于 r e s u l t = r e s u l t ∗ r e s u l t m o d 5 ; 此时 r e s u l t 就等于 8 2 m o d 5 = 4 8 4 m o d 5 等于 r e s u l t = r e s u l t ∗ r e s u l t m o d 5 ; 此时 r e s u l t 就等于 8 4 m o d 5 = 1 8 8 m o d 5 等于 r e s u l t = r e s u l t ∗ r e s u l t m o d 5 ; 此时 r e s u l t 就等于 8 8 m o d 5 = 1 8 16 m o d 5 等于 r e s u l t = r e s u l t ∗ r e s u l t m o d 5 ; 此时 r e s u l t 就等于 8 16 m o d 5 = 1 \begin{align*} 设:result &= 8^1 \quad mod \quad5 = 8 \quad mod \quad5 = 3\\ 8^2\quad mod \quad5 \quad等于\quad result &= result * result \quad mod \quad5 ; \quad此时\quad result\quad就等于8^2\quad mod\quad5 = 4\\ 8^4\quad mod \quad5 \quad等于\quad result &= result * result \quad mod \quad5 ; \quad此时\quad result\quad就等于8^4\quad mod\quad5 = 1\\ 8^8\quad mod \quad5 \quad等于\quad result &= result * result \quad mod \quad5 ; \quad此时\quad result\quad就等于8^8\quad mod\quad5 = 1\\ 8^{16}\quad mod \quad5 \quad等于\quad result &= result * result \quad mod \quad5 ; \quad此时\quad result\quad就等于8^{16}\quad mod\quad5 = 1 \end{align*} 设:result82mod5等于result84mod5等于result88mod5等于result816mod5等于result=81mod5=8mod5=3=resultresultmod5;此时result就等于82mod5=4=resultresultmod5;此时result就等于84mod5=1=resultresultmod5;此时result就等于88mod5=1=resultresultmod5;此时result就等于816mod5=1
8 25 m o d 5 8^{25}\quad mod \quad 5 825mod5

循环权重赋值权重取余结果取余说明
111W= 8 % 5W=aresult = (result * 8) % 5;8%5 = 3
202W= (W * W) % cW= 8 2 8^2 82 mod 3 = 4*
304W= (W * W) % cW= 8 4 8^4 84 mod 3 = 1*
418W= (W * W) % cW= 8 8 8^8 88 mod 3 = 1result = (result * W) % c3 * 1 % 5 = 3
5116W= (W * W) % cW= 8 16 8^{16} 816 mod 3 = 1result = (result * W) % c3 * 1 % 5 = 3

所以最终结果 8 25 m o d 5 = 3 8^{25}\quad mod \quad 5 = 3 825mod5=3

算法实现

这个取模数学公式证明了, a的任意次幂的模数,运算,可以通过先求模,然后再乘以其它的数,再次取模,结果是一致的
这段代码,写的是A*B的积取模

typedef unsigned long long ull;
int a_b_mod_c(int a, ull b, int c)
{
	int result = 1;
	int weight = a % c;		// 这个位置的权重是1,所以是a^1次方
	if(b==0) return 1 % c;	// 指数为0时,任何数的0次方都为1,可以直接得出结果。
	if(b & 0x1==1) result = weight % c;	// 因为这个位置的值为1,将引用weight到result.
	b >>= 1;	// B的第1位的权重就是a,已经处理完成,就右移一位。
	while(b)
	{
		weight = ((weight % c) * (weight % c)) % c;	 // 求指数2的N次方的余数
		if(b & 0x1==1) result = (result * weight) % c;	// 该位的值为1,则把该位对应的权重也计算到最终结果来。
		b >>= 1;
	}
	
	return result;
}
//上面的代码可以简化一下
int a_b_mod_c(int a, ull b, int c)
{
	int result = 1;
	int weight = a % c;
	while(b)
	{
		if(b & 0x1==1) result = (result * weight) % c;	// 该位的值为1,则把该位对应的权重也计算到最终结果来。
		b >>= 1;
		weight = ((weight % c) * (weight % c)) % c;	 // 求指数2的N次方的余数
	}
	
	return result;
}

下面用更巧妙的方法实现,不过比较难理解。

int a_b_Mod_c1(int a, int b, int c)
{
    int digit[32];
    int i, k, resualt = 1;
    i = 0;
    while(b)
    {
        digit[i++] = b&0x00000001;
        b >>= 1;
    }
    
    for(k = i-1; k >= 0; k--)
    {
        resualt = (resualt * resualt) % c;
        if(digit[k] == 1) resualt = (resualt * a) % c;
    }
    return resualt;
}

下面的这个代码,效率就比较低了。

int a_b_Mod_c2(int a, int b, int c)
{
    int i, result = 1;
    //参数非法较验就不写了
    if(b<0 || c<0 || a<0) return -1;
    for(i = 0; i < b; i++) result = (result * a) % c;
    return result % c;
}

可以看到高效的幂取余运算基本就是O(1)的常量级的
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烈火蜓蜻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值