title: 求组合数
date: 2022-11-14 23:08:43
tags: 组合数字
categories:
- 算法
- 数论
求组合数方法一:递推公式
时间复杂度 O ( n 2 ) O(n^2) O(n2)
使用递推公式: C ( a , b ) = ( C ( a − 1 , b − 1 ) + C ( a − 1 , b ) ) m o d p C(a, b) = (C(a - 1, b - 1) + C(a - 1, b))\mod p C(a,b)=(C(a−1,b−1)+C(a−1,b))modp
final int maxn = (int) 2005;
long[][] c = new long[maxn][maxn];
Arrays.fill(c[0], 0);
c[0][0] = 1;
for (int i = 1; i < maxn; i++) {
for (int j = 0; j < maxn; j++) {
if (j != 0) c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % p;
else c[i][j] = 1;
}
}
求组合数方法二: 使用费马小定理求逆元
package ZuHe;
/**
* @author: Zekun Fu
* @date: 2022/11/14 23:21
* @Description: 使用费马小定理,求阶乘和阶乘的逆元
*/
public class SecondZuHe {
public static long qui(int a, int b, int mod) {
long res = 1;
while (b != 0) {
if (b % 2 == 1) res = (res * a) % mod;
a = (int)((long)a * a % mod);
b >>= 1;
}
return res;
}
public static void getFac(long[] fac, long[] infac, int maxn, int mod) {
// System.out.println(qui(2, 3, mod));
fac[0] = infac[0] = 1;
for (int i = 1; i < maxn; i++) {
fac[i] = fac[i - 1] * i % mod;
infac[i] = qui(i, mod - 2, mod) * infac[i - 1] % mod;
}
}
public static void main(String[] args) {
System.out.println(qui(2, 3, 100));
}
}
求组和数三:使用lucass定理
C ( a , b ) m o d p = C ( a m o d p , b m o d p ) ∗ C ( a / p , b / p ) m o d b C(a, b) \mod p = C(a \mod p, b \mod p) * C(a / p, b / p) \mod b C(a,b)modp=C(amodp,bmodp)∗C(a/p,b/p)modb
package ZuHe;
/**
* @author: Zekun Fu
* @date: 2022/11/14 23:55
* @Description: 使用卢卡斯定理进行证明
* C(a, b) = C(a % p, b % p) * C(a / p, b / p)
*
* 1. 首先使用thirdZuhe进行初始化
* 2. 最后使用lucss进行计算。
*/
public class ThirdZuHe {
public static long qui(long a, long b, long p) {
long res = 1;
while (b != 0) {
if (b % 2 == 1) res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
private static long myC(long a, long b, long p) {
if (a < b) return 0;
long res = 1;
for (long i = a, j = 1; j <= b; i--, j++) {
res = res * i % p;
res = res * qui(j, p - 2, p) % p;
}
// for (int i = 1; i <= b; i++) res = res / i;
return (int)res % p;
}
private static long C(long a, long b, long p){
long res = 1;
for(long i = 1, j = a;i <= b;++i, --j){
res = res * j % p;
res = res * qui(i, p - 2, p) % p;
}
return res % p;
}
public static long mylucas(long a, long b, int p) {
if (a < p && b < p) return C(a, b, p);
return mylucas(a % p, b % p, p) * mylucas(a / p, b / p, p) % p;
}
public static long lucas(long a, long b, long p){
if(a < p && b < p) return C(a, b, p);
return C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}
public static void main(String[] args) {
// System.out.println(SecondZuHe.qui(1, 10, 10000));
System.out.println(C(211, 95, 23));
System.out.println(lucas(211, 95, 23));
}
}
求组合数四: 使用质因数分解 + 大数乘法
- 质因数分解
- 阶乘中包含质因数的个数
- 大数乘法
-
质因数分解,要求i * primes[j] <= a,否则可能数组越界。
-
为什么求a!中包含质因数的个数的时候,不能出现那种情况呢?
-
大数乘法过程最后的t别忘了 t % 10, t /= 10
package ZuHe;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
/**
* @author: Zekun Fu
* @date: 2022/11/15 17:52
* @Description: 第四种组合,大数字的组合
*
* 1. 首先求出阶乘的素数因子的幂
* 2. 其次使用大数乘法进行运算,不用使用快速幂了,因为因子的总个数不会超过
* 5000。
*
* p1^sum1 * p2 ^ sum2 ... * pn ^ sumn
*/
public class ForthZuHe {
// a * b
public static List<Integer> get_primes(int a) {
List<Integer>primes = new ArrayList<>();
boolean[] st = new boolean[a + 5];
Arrays.fill(st, true);
for (int i = 2; i <= a; i++) {
if (st[i]) {
primes.add(i);
}
for (int j = 0; primes.get(j) <= a / i;j++) {
st[primes.get(j) * i] = false;
if (i % primes.get(j) == 0) break;
}
}
return primes;
}
public static List<Integer> mul(List<Integer>a, int b) {
int n = a.size();
List<Integer>ans = new ArrayList<>();
int t = 0;
for (int i = 0; i < n; i++) {
t += a.get(i) * b;
ans.add(t % 10);
t /= 10;
}
while (t != 0) {
ans.add(t % 10);
t /= 10;
}
return ans;
}
// 计算a!中含有多少个素数p
private static int get(int a, int p) {
int s = 0;
// while (a >= p) { // 对于每一个数字有多少个个k就会被计算多少回
// s += a / p;
// p *= p;
// }
while (a != 0) {
s += a / p;
a /= p;
}
return s;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int a = sc.nextInt();
int b = sc.nextInt();
List<Integer>primes = get_primes(a);
List<Integer>sum = new ArrayList<>();
for (Integer p : primes) {
sum.add(get(a, p) - get(b, p) - get(a - b, p));
}
List<Integer>ans = new ArrayList<>();
ans.add(1);
// BigInteger ans = new BigInteger("1");
for (int i = 0; i < primes.size(); i++) {
int x = primes.get(i);
for (int j = 0; j < sum.get(i); j++) {
// ans = ans.multiply(new BigInteger(x + ""));
ans = mul(ans, x);
}
}
// System.out.println(ans);
for (int i = ans.size() - 1; i >= 0; i--) {
System.out.print(ans.get(i));
}
System.out.println();
}
}