求组合数的四种方式


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(a1,b1)+C(a1,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));
    }

}
 

求组合数四: 使用质因数分解 + 大数乘法

  • 质因数分解
  • 阶乘中包含质因数的个数
  • 大数乘法
  1. 质因数分解,要求i * primes[j] <= a,否则可能数组越界。

  2. 为什么求a!中包含质因数的个数的时候,不能出现那种情况呢?

  3. 大数乘法过程最后的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();
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值