美文网首页
组合数的四种求法

组合数的四种求法

作者: madao756 | 来源:发表于2020-05-07 19:33 被阅读0次

前言:acwing 也做了这么久了,是时候开始复习了

0X00 四种方法以及相应的代码

方法 1 迭代法

acwing 求组合数 I

from sys import stdin

def read(n=2):
    if n == 1:
        return int(stdin.readline())
    return map(int, stdin.readline().split())

mod = 10 ** 9 + 7

N = 2010
n = read(1)
comb = [[0] * N for _ in range(N)]

for i in range(N):
    for j in range(i+1):
        if j == 0:
            comb[i][j] = 1
        else:
            comb[i][j] = (comb[i-1][j]+comb[i-1][j-1]) % mod

while n > 0:
    n -= 1
    a, b = read()
    print(comb[a][b])

核心思想是:C_i^{j} = C_{i-1}^{j-1} + C_{i-1}^{j}

方法 2 简单逆元

求组合数 II

from sys import stdin

def read(n=2):
    if n == 1:
        return int(stdin.readline())
    return map(int, stdin.readline().split())

N = 100010
fact = [0] * N
infact = [0] * N

fact[0] = infact[0] = 1

mod = 10 ** 9 + 7

def qmi(a, k, p):
    res = 1
    while k > 0:
        if k & 1 == 1:
            res = res * a % mod
        k >>= 1
        a = a * a % mod
    return res

for i in range(1, N):
    fact[i] = fact[i-1] * i % mod
    infact[i] = infact[i-1] * qmi(i, mod-2, mod) % mod

n = read(1)
while n > 0:
    n -= 1
    a, b = read()
    print(fact[a] * infact[a - b] * infact[b] % mod)
   

逆元的通俗解释是:a * b = a / b\ mod\ p, 也就是说除以一个数对 p 取余的结果,等于乘以一个数对 p 取余

在模数 p 的情况下, b 存在逆元的充分必要条件是 p 是质数

由于组合数可能很大,所以题目为了不超过 int 往往会让答案对一个质数取余数(比如 1e9 + 7)

我们回到组合数的基本算法:

C_{a}^{b} = \frac{a!}{b!(a-b)!} = fact[a] * infact[a-b] * infact[b]

其中 fact[a] 表示 a 的阶乘,infact[b] 表示 b 的阶乘的逆元并且 fact[0] = infact[0] = 1

方法3 lucas 算法

求组合数 III

from sys import stdin 

def read(n = 2):
    if n == 1:
        return int(stdin.readline())
    return map(int, stdin.readline().split())

def qmi(a, k, p):
    res = 1
    while k > 0:
        if k & 1:
            res = res * a % p
        k >>= 1
        a = a * a % p
    
    return res
            
    

def C(a, b, p):
    if b > a: return 0
    x, y = 1, 1
    j = 1
    for i in range(a, a-b, -1):
        x = x * i % p
        y = y * j % p
        j += 1
    # print("x, y", x, y)
    return x * qmi(y, p-2, p) % p 
    
def lucas(a, b, p):
    if a < p and b < p:
        return C(a, b, p)
    return C(a % p, b % p, p) * lucas(a // p, b // p, p) % p

n = read(1)
while n > 0:
    n -= 1
    a, b, p = read()
    print(lucas(a, b, p))
    
摘自 oi wiki

除此之外代码中有对 C_{a}^{b} 的化简

C_{a}^{b} = \frac{a!}{(a-b)!b!} = \frac{a(a-1)...(a-b+1)}{b!}

方法 4 分解质因数

求组合数 IV

N = 5010
cnt = 0
primes = [0] * N
st = [True] * N

def getPrimes(n):
    global cnt
    for i in range(2, n+1):
        if st[i]:
            primes[cnt] = i
            cnt += 1
        j = 0
        while primes[j] <= n // i:
            st[primes[j] * i] = False
            if not (i % primes[j]):
                break
            j += 1

def get(a, p):
    
    res = 0
    while a > 0:
        res += a // p
        a //= p
    
    return res
    
a, b = map(int, input().split())
getPrimes(a)
sums = [0] * cnt
for i in range(cnt):
    p = primes[i]
    sums[i] = get(a, p) - get(a-b, p) - get(b, p)

res = 1
for t, v in zip(sums, primes):
    res *= v ** t
print(res)

C_{a}^{b} = \frac{a!}{(a-b)!b!}

再回到这个式子上,我们知道任意一个数字 n 都可以写成 n = p_1^{\alpha_1}p_2^{\alpha_2}...p_n^{\alpha_n} 也就是分解质因数

所以我们只需要求出 C_a^{b} 的所有质因数以及他出现的次数就可以了

回到一个最简单的例子 n! 假设所有 <= n 的质数存在 ps 中如何计算所有质数出现的次数呢?(每个质数出现的次数至少一次)

\alpha = \lfloor \frac{n}{p} \rfloor + \lfloor \frac{n}{p^2} \rfloor...\lfloor \frac{n}{p^t} \rfloor

0X01 总结

我们来总结一下四种方法:

  • 底数比较小用第一种方法
  • 有模数底数比较大用第二种
  • 有模数,但模数比较小,底数很大用第三种
  • 无模数,底数比较大,用第四种

相关文章

  • 组合数的四种求法

    前言:acwing 也做了这么久了,是时候开始复习了 0X00 四种方法以及相应的代码 方法 1 迭代法 acwi...

  • Python四种组合数据类型梳理

    Python四种组合数据类型梳理 元组tuple 可以存放一组有顺序的可以重复的不可以改变的数据!就是一种少了许多...

  • 2018-07-16

    四种组合数据类型的含义、声明、增删改查,遍历 四种组合类型: 列表list: [append/insert/ext...

  • 01:列表list

    python学习day_1: 四种组合数据类型(list:列表 tuple:元组 set:集合 dict...

  • 02:元组tuple

    python学习day_2: 四种组合数据类型(list:列表 tuple:元组 set:集合 dict...

  • 03:集合set

    python学习day_3: 四种组合数据类型(list:列表 tuple:元组 set:集合 dict...

  • 04:字典dict

    python学习day_4: 四种组合数据类型(list:列表 tuple:元组 set:集合 dict...

  • python的组合数据类型

    python中的数据类型 Python的组合数据类型有四种,分别是:列表(list)、元组(tuple)、集合(s...

  • Oracle 集合处理sql

    /根据一组坐标+半径+生成的坐标精度生成一组圆形集合数据/SELECT SDO_UTIL.CIRCLE_POLYG...

  • 2020-10-31 .小组Day7笔记

    四种组学基因组 转录组 蛋白组 代谢组 三代测序方法

网友评论

      本文标题:组合数的四种求法

      本文链接:https://www.haomeiwen.com/subject/snkzghtx.html