递归计算其实是更容易理解的:
halfRes
;n
为偶数,很好办,再乘以一个halfRes
就可以了(再取模一下),也就是可以返回halfRes*halfRes
;n
为奇数的话,就需要再乘以一个a
,然后再返回; static long pow_mod(long a, long n, long mod) {
if (n == 0) // a^0 = 1
return 1;
// 先求一半的 --> 你先给我求出 a ^ (n/2) 的结果给我
long halfRes = pow_mod(a, n >> 1, mod); // n >> 1 --> n/2
long res = halfRes * halfRes % mod;
if ((n & 1) != 0) // odd num
res = res * a % mod;
return res;
}
75
的二进制形式为1001011
;10
的75
次方 = 1064 × 108 × 102 × 101;75
的二进制数形式总共有多少位,我们就使用了多少次乘法;64、8、2、1
对应到75
的二进制数中,相应位上是1
;而1032、1016、104不应该累乘,因为32、16、 4
对应到75
的二进制数中,相应位是0
; static long pow_mod2(long a, long n, long mod) {
long res = 1;
while (n > 0) {
if ((n & 1) != 0) // 二进制最低位 是 1 --> (n&1) != 0 --> 乘上 x ^ (2^i) (i从0开始)
res = res * a % mod;
a = a * a % mod; // a = a^2
n >>= 1; // n -> n/2 往右边移一位
}
return res;
}
// 计算 (a * b) % mod
static long mul_mod(long a, long b, long mod){
long res = 0;
while(b > 0){
if( (b&1) != 0) // 二进制最低位是1 --> 加上 a的 2^i 倍, 快速幂是乘上a的2^i )
res = ( res + a) % mod;
a = (a << 1) % mod; // a = a * 2 a随着b中二进制位数而扩大 每次 扩大两倍。
b >>= 1; // b -> b/2 右移 去掉最后一位 因为当前最后一位我们用完了,
}
return res;
}
可以使用非递归的乘法快速幂和上面的(a*b) % mod
来计算快速幂,差别不大:
// 计算 (a * b) % mod
static long mul_mod(long a,long b,long mod){
long res = 0;
while(b > 0){
if( (b&1) != 0) // 二进制最低位是1 --> 加上 a的 2^i 倍, 快速幂是乘上a的2^i )
res = ( res + a) % mod;
a = (a << 1) % mod; // a = a * 2 a随着b中二进制位数而扩大 每次 扩大两倍。
b >>= 1; // b -> b/2 右移 去掉最后一位 因为当前最后一位我们用完了,
}
return res;
}
//非递归 计算 (a^n) % mod 配合 mul
static long pow_mod3(long a,long n,long mod){
long res = 1;
while(n > 0) {
if( (n&1) != 0 ) // 二进制最低位 是 1 --> (n&1) != 0 --> 乘上 x ^ (2^i) (i从0开始)
res = mul_mod(res,a,mod) % mod;
a = mul_mod(a,a,mod) % mod; // a = a^2
n >>= 1; // n -> n/2 往右边移一位
}
return res;
}
import java.io.BufferedInputStream;
import java.util.Scanner;
/**
* 题目链接: http://xyoj.xynu.edu.cn/problem.php?id=1872&csrf=mmofuzhUWGip3c6WlmhiFY6bLxeVHZta
*/
public class Main { //提交时改成Main
//递归 计算 (a^n) % mod
static long pow_mod(long a,long n,long mod){
if(n == 0) // a^0 = 1
return 1;
// 先求一半的 --> 你先给我求出 a ^ (n/2) 的结果给我
long halfRes = pow_mod(a, n >> 1, mod); // n >> 1 --> n/2
long res = halfRes * halfRes % mod;
if( (n&1) != 0) // odd num
res = res * a % mod;
return res;
}
//非递归 计算 (a^n) % mod
static long pow_mod2(long a,long n,long mod){
long res = 1;
while(n > 0) {
if( (n&1) != 0 ) // 二进制最低位 是 1 --> (n&1) != 0 --> 乘上 x ^ (2^i) (i从0开始)
res = res * a % mod;
a = a * a % mod; // a = a^2
n >>= 1; // n -> n/2 往右边移一位
}
return res;
}
// 计算 (a * b) % mod
static long mul_mod(long a,long b,long mod){
long res = 0;
while(b > 0){
if( (b&1) != 0) // 二进制最低位是1 --> 加上 a的 2^i 倍, 快速幂是乘上a的2^i )
res = ( res + a) % mod;
a = (a << 1) % mod; // a = a * 2 a随着b中二进制位数而扩大 每次 扩大两倍。
b >>= 1; // b -> b/2 右移 去掉最后一位 因为当前最后一位我们用完了,
}
return res;
}
//非递归 计算 (a^n) % mod 配合 mul
static long pow_mod3(long a,long n,long mod){
long res = 1;
while(n > 0) {
if( (n&1) != 0 ) // 二进制最低位 是 1 --> (n&1) != 0 --> 乘上 x ^ (2^i) (i从0开始)
res = mul_mod(res,a,mod) % mod;
a = mul_mod(a,a,mod) % mod; // a = a^2
n >>= 1; // n -> n/2 往右边移一位
}
return res;
}
public static void main(String[] args) {
Scanner cin = new Scanner(new BufferedInputStream(System.in));
int T = cin.nextInt();
while(T-- > 0){
int a = cin.nextInt();
int n = cin.nextInt();
int mod = cin.nextInt();
// System.out.println(pow_mod(a,n,mod));
// System.out.println(pow_mod2(a,n,mod));
System.out.println(pow_mod3(a,n,mod));
}
}
}
这个题目和普通的求幂不同的是:
x
(底数)是double
类型的,且n
是范围是Integer
范围的(可正可负) :n
为负数的时候,我们可以转换成求1.0 / pow(x,-n)
;n = Integer.MIN_VALUE
的时候要特殊处理,因为整形范围是 − 2 31 到 2 31 − 1 -2^{31} 到 2^{31} - 1 −231到231−1 所以或者我们使用long
来存转换的数,或者特殊判断一下;两种写法意思一样,第二种写法更加简洁:
class Solution {
public double myPow(double x, int n) {
if (n > 0) {
return pow(x, n);
} else {
if (n == Integer.MIN_VALUE) {
return 1.0 / (pow(x, -(Integer.MIN_VALUE + 1)) * x); // MAX_VALUE = -(Integer.MIN_VALUE + 1)
}
return 1.0 / pow(x, -n);
}
}
public double pow(double x, int n) {
if (n == 0)
return 1;
double half = pow(x, n / 2);
if (n % 2 == 0)
return half * half;
else
return x * half * half;
}
}
class Solution {
public double myPow(double x, int n) {
if (n == 0)
return 1.0;
if (n < 0) {
if (n == Integer.MIN_VALUE) {
// return 1.0 / (myPow(x,-(Integer.MIN_VALUE+1)) * x);
return 1.0 / (myPow(x, Integer.MAX_VALUE) * x);
}
return 1.0 / myPow(x, -n);
}
double half = myPow(x, n / 2);
if (n % 2 == 0)
return half * half;
else
return x * half * half;
}
}
三种写法的意思都是一样,只不过处理Integer.MIN_VALUE
的方式不同而已。
class Solution {
public double myPow(double x, int n) {
if (n == 0)
return 1.0;
if (n < 0) {
if (n == Integer.MIN_VALUE) {
return 1.0 / (myPow(x, Integer.MAX_VALUE) * x);
} else {
return 1.0 / myPow(x, -n);
}
}
double res = 1.0;
while (n > 0) {
if ((n & 1) != 0)
res *= x;
x = x * x;
n >>= 1;
}
return res;
}
}
class Solution {
public double myPow(double x, int n) {
if (n == 0)
return 1.0;
double res = 1.0;
if (n < 0) {
x = 1 / x;
n = -(1 + n); // for Integer.MIN_VALUE
res *= x; // x is 1/x because n is -(n+1) so should do this
}
while (n > 0) {
if ((n & 1) != 0)
res *= x;
x = x * x;
n >>= 1;
}
return res;
}
}
class Solution {
public double myPow(double x, int n) {
if (n == 0)
return 1.0;
long abs = Math.abs((long) n); // also for Integer.MIN_VALUE
double res = 1.0;
while (abs > 0) {
if ((abs & 1) != 0)
res *= x;
x = x * x;
abs >>= 1;
}
if (n < 0)
return 1.0 / res;
return res;
}
}