模加与模乘
/**
* 模加
*
* @param x
* @param y
* @return (x + y) % mod
*/
public static long add(long x, long y, long mod) {
return ((x % mod) + (y % mod)) % mod;
}
/**
* 模乘
*
* @param x
* @param y
* @return x * y % mod
*/
public static long mul(long x, long y, long mod) {
return ((x % mod) * (y % mod)) % mod;
}
模快速幂算法
先说一下快速幂,快速幂用于快速得出一个整数的整数幂次的值,如下图所示
模快速幂,即在快速幂的基础上,每一部加上取模操作。
模快速幂代码如下,如不需要取模操作,只要将代码中取模操作相关的代码去掉即可。
这里的代码实现通过递归实现,迭代的实现方式这里不再展示。
/**
* 模快速幂
*
* @param x
* @param n
* @return x^n % mod
*/
public static long quickPower(long x, long n, long mod) {
if (n == 0) return 1;
if (n == 1) return x % mod;
long tmp = quickPower(x, n >> 1, mod);
// mul方法即为上面的模乘
return (n & 1) == 0 ? mul(tmp, tmp, mod) : mul(x, mul(tmp, tmp, mod), mod);
}
费马小定理求解分数的模
费马小定理:a^(p-1) mod p = 1 mod p
费马小定理使用的前提是p为素数,否则结果不正确。
比如求(b/a) % p
由费马小定理:
a^(p-1) mod p = 1 mod p
a * a^(p-2) mod p = 1 mod p
a^(p-2) mod p = a^(-1) mod p
(b/a) % p = b * a^(-1) % p = b * a^(p-2) % p
代码实现如下
/**
* 分数求模
* 费马小定理(前提是模是素数)
* a^(p-1) mod p = 1 mod p
* a * a^(p-2) mod p = 1 mod p
* a^(p-2) mod p = a^(-1) mod p
* (b/a) % p = b * a^(-1) % p = b * a^(p-2) % p
*
* @param a 分母
* @param b 分子
* @return (b / a) % mod
*/
public static long fractionMod(long a, long b, long mod) {
// 这里使用上面的模快速幂的方法
return mul(b, quickPower(a, mod - 2, mod), mod);
}
欧几里得算法(求最大公约数)
欧几里得算法,又名辗转相除法,具体原理这里不再说明
/**
* 欧几里得算法
* 获取两个整数的最大公约数
*
* @param a
* @param b
* @return
*/
public static int gcd(int a, int b) {
if (b == 0) {
return a;
} else {
return gcd(b, a % b);
}
}
扩展欧几里得算法(求最大公约数的同时获取ax + by = gcd(a, b)的一组解)
/**
* 扩展欧几里得算法
* 获取 ax + by = gcd(a, b)的一组解,且返回 a,b 的最大公约数
*
* @param a
* @param b
* @param variable 保存变量x, y,你也可以通过别的方式操作
* @return a, b 的最大公约数
*/
public static int exGcd(int a, int b, IntegerVariable variable) {
if (b == 0) {
variable.x = 1;
variable.y = 0;
return a;
}
int g = exGcd(b, a % b, variable);
int temp = variable.x;
variable.x = variable.y;
variable.y = temp - (a / b) * variable.y;
return g;
}
// 用于保存x与y的值,你也可以通过别的方式操作
public static class IntegerVariable {
int x;
int y;
public int getX() {
return x;
}
public int getY() {
return y;
}
@Override
public String toString() {
return "IntegerVariable{" +
"x=" + x +
", y=" + y +
'}';
}
}
求乘法逆元
乘法逆元的这里不再赘述,直接上代码
/**
* 求 a 模 m 的乘法逆元
*
* @param a
* @param m
* @return
*/
public static int inverse(int a, int m) {
IntegerVariable variable = new IntegerVariable();
int g = exGcd(a, m, variable);
return (variable.x % m + m) % m; //如果variable.x为负数,这里变为正数
}