快速幂取模
快速幂取模
取模
取模运算的几个重要结论
- (a * b) % p = (a % p * b) % p
- (a + b) % p = (a % p + b) % p
- (a + b) % p = (a % p + b % p) % p
- (a - b)% p = (a % p - b % p) % p
- (a * b)% p = (a % p * b % p) % p
对于最后一条性质,即多个因子连续的乘积取模的结果等于每个因子取模后的乘积再取模的结果
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int main()
{
//底数和指数在int的范围之内
int base,power;
cin >> base >> power;
long long res = 1;
//执行power次循环,每次结果都乘一个base,即base的power次方
for (int i = 1;i <= power;i++)
{
res = res * base;
}
long long ans = res % 1000;
cout << ans << endl;
return 0;
}
我们在上面的代码中定义了
① long long ans = res % 1000
需要注意,此时的res其实是for循环执行完毕得到的结果res
变形一下
long long ans = ( res * 1 ) % 1000
再利用取模性质
long long ans = ( res % 1000 * 1 % 1000 ) % 1000
而1 % 1000 = 1,所以
②long long ans = ( res % 1000 ) % 1000
比较①和②,会发现多取一次模对于结果没有影响,那么为何不提前取模呢??(减少数据运算的次数,降低数据的范围,这是提前取模的两个优点)
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
long long base,power;
while (cin >> base >> power) //多组数据
{
if (base == 0 && power == 0) break;
long long res = 1;
//执行power次循环,每次结果都乘一个base,即base的power次方
for (long long i = 1;i <= power;i++)
{
res = res * base;
res = res % 1000;
}
long long ans = res % 1000;
cout << ans << endl;
}
return 0;
}
快速幂运算
虽然大数取模方法可以很快求出A的B次方的后几位数,但如果考虑算法的时间复杂度,假设我们求2的100次方,计算机则需要执行100次循环,分析得这个算法的时间复杂度为O(n)
快速幂算法可以解决时间上的问题
前面已经分析出,朴素的的求幂算法时间复杂度之所以高,就是因为当n很大时,执行的循环操作次数也很大。由此得到了快速幂算法的核心思想:指数减半,底数平方。
先举个例子让大家熟悉一下快速幂算法:
例如求3的10次方
可将其转化为3的平方的5次方 3 ^ 10 = (3 ^ 2 ^ 5)
即3 ^ 10 = 9 ^ 5
此时指数由10变成了5(减半),而底数从3变成了9(平方) ,虽然结果没变,但原本求3 ^ 10需要执行10次操作,求9 ^ 5只需要执行5次操作,提高了效率,降低了时间复杂度。
但是,快速幂算法中需要注意指数为奇数的问题
当指数是偶数时,由指数减半,底数平方,可以得到:
2 ^ 16 = 4 ^ 8 = 16 ^ 4 = 256 ^ 2 = 256 * 256 = 65536
但是,对于 9 ^ 5,5是一个奇数,5的一半是2.5,而指数不能为小数,因此不能直接执行5 / 2,故采取另一种方法表示9 ^ 5
即 9 ^ 5 =(9 ^ 4) 9,然后对9 ^ 4继续执行指数减半,底数平方的操作即可,直至出现一个数的0次方*,便可以计算出结果。
总结:指数是偶数时,由指数减半,底数平方,一直循环下去,从而减少运算次数
快速幂模板
//快速幂,a为底数,n为指数
long long quick_power(long a,long n)
{
long long ans = 1;
while(n)
{
if (n % 2) ans *= a;//指数为奇数
a = a * a;//底数平方
n /= 2;//指数减半
}
return ans;
}
快速幂与取模计算
#include <iostream>
using namespace std;
//快速幂与大数取模结合
long long quick_power(long long a,long long n)
{
long long ans = 1;
while(n!=0)
{
if (n % 2) ans = ans * a % 1000;//两个作用 1.奇数分出去 2.当n=1时将数据带出
a = a * a % 1000;//底数平方的最后3位数字
n /= 2;//指数减半
}
return ans%1000;
}
int main()
{
cout << quick_power(2,1000000000) << endl;
return 0;
}
循环简单代码
题目描述
乐乐是一个聪明而又勤奋好学的孩子。他总喜欢探求事物的规律。一天,他突然对数的正整数次幂产生了兴趣。众所周知,2的正整数次幂最后一位数总是不断的在重复2,4,8,6,2,4,8,6……我们说2的正整数次幂最后一位的循环长度是4(实际上4的倍数都可以说是循环长度,但我们只考虑最小的循环长度)。类似的,其余的数字的正整数次幂最后一位数也有类似的循环现象:
循环
循环长度
2
2、4、8、6
4
3
3、9、7、1
4
4
4、6
2
5
5
1
6
6
1
7
7、9、3、1
4
8
8、4、2、6
4
9
9、1
2
这时乐乐的问题就出来了:是不是只有最后一位才有这样的循环呢?对于一个整数n的正整数次幂来说,它的后k位是否会发生循环?如果循环的话,循环长度是多少呢?
注意:
1. 如果n的某个正整数次幂的位数不足k,那么不足的高位看做是0。
2. 如果循环长度是L,那么说明对于任意的正整数a,n的a次幂和a + L次幂的最后k位都相同。
输入格式
输入只有一行,包含两个整数n(1 <= n < 10100)和k(1 <= k <= 100),n和k之间用一个空格隔开,表示要求n的正整数次幂的最后k位的循环长度。
输出格式
输出一行,这一行只包含一个整数,表示循环长度。如果循环不存在,输出-1。
样例输入
32 2
样例输出
4
代码
import java.util.Scanner;
public class Main {
public String num;
public int k;
public static void main(String[] args) {
Main main = new Main();
Scanner scanner = new Scanner(System.in);
main.num = scanner.next();
main.k = scanner.nextInt();
int index=main.num.length()-main.k;
String word=main.num.substring(index);
int n=Integer.parseInt(word);
long count = 2;
boolean flag=true;
long res=(long)(Math.pow(n,1)%Math.pow(10,main.k));
while(true){
long num=main.Pow_Modular(n,count);
if(count>10000){
flag=false;break;
}
if(num==res) break;
count++;
}
count--;
System.out.println(flag?count:-1);
}
public long Pow_Modular(long a,long n) {
long ans = 1;
while (n!=0) {
if(n%2!=0)ans=(long)((ans*a)%(Math.pow(10,this.k)));
a= (long) (a*a%Math.pow(10,this.k));
n/=2;
}
ans= (long) (ans%Math.pow(10,this.k));
return ans;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理