CC++ & Algorithm

杨辉三角形的妙用——用Python算组合数、解决抽奖和概率问题

困难2
语言版本:C++Python
概述:学习杨辉三角形在选队长、抽奖、足球排列等生活场景中的应用,并用Python代码快速计算任意位置的组合数。

杨辉三角形的秘密武器——用Python轻松算出“有多少种选法”

你有没有想过,从班里选几个同学去参加比赛,或者从一堆零食中挑出几种口味,到底有多少种不同的选择方式?别急着一个个数,杨辉三角形里早就藏好了答案。上一课我们学会了用Python搭建杨辉三角形的金字塔,现在要揭开它更神奇的功能:每个数字都是一个组合数,能帮你快速算出生活中各种“选法”的数量。

什么是组合数?从选班长说起

新学期要选班长,老师从 5个候选人 中选出 2个人 担任正副班长。请问有多少种不同的选法?这就是一个典型的组合问题——从n个不同的东西里选出k个,不考虑顺序。数学上记作 C(n, k),读作“n选k”。

比如从5个人中选2个,结果就是C(5,2)。你可能想:第一个人有5种可能,第二个人有4种可能,但这样算出来会重复(因为选张三和李四,跟选李四和张三是同一种组合),所以要除以2。结果是(5×4)÷2=10。没错,答案是10种。

生活中的组合数无处不在:

  • 从10种口味的冰淇淋中选3种,有多少种搭配?
  • 从30个彩票号码中选6个,有多少种不同的彩票?
  • 从7个好朋友中选4个一起去游乐园,有多少种组队方式?

这些问题的答案就藏在杨辉三角形的每一行里。

杨辉三角形里的组合数秘密

还记得杨辉三角形的构造规则吗?每个数字等于它左上角和右上角两个数字之和。而且它的第n行(从第0行开始)的第k个数字(从第0个开始),恰好等于组合数 C(n, k)

我们看一个例子,杨辉三角形的前几行:

第0行:         1                →  C(0,0)=1
第1行:       1   1              →  C(1,0)=1, C(1,1)=1
第2行:     1   2   1            →  C(2,0)=1, C(2,1)=2, C(2,2)=1
第3行:   1   3   3   1          →  C(3,0)=1, C(3,1)=3, C(3,2)=3, C(3,3)=1
第4行:  1   4   6   4  1        →  C(4,0)=1, C(4,1)=4, C(4,2)=6, C(4,3)=4, C(4,4)=1
第5行:1   5  10  10  5  1       →  C(5,0)=1, C(5,1)=5, C(5,2)=10, C(5,3)=10, C(5,4)=5, C(5,5)=1

看到没?第5行第2个数字是10,正好对应了刚才选班长的C(5,2)=10。第4行第2个数字是6,表示从4个同学中选2个当值日生,有6种选法。

注意:行和列都是从0开始数的!如果你要找C(5,2),就去第5行、第2个位置(而不是第6行第3个)。这个索引规则是新手最容易搞错的地方。

生活中的组合数故事

故事一:选队长(原例)

班级里有5个同学要选2名队长(不分正副),共有C(5,2)=10种选法。杨辉三角形第5行第2个数就是答案。

故事二:抽奖概率

买彩票从30个号码中选6个,中一等奖的概率是1 / C(30,6)。虽然C(30,6)的数字很大(大约59万),但用杨辉三角形思路,就是第30行第6个数字。当然手工算很麻烦,交给Python就简单了。

故事三:足球比赛出场顺序

学校足球队有11名队员,教练要选5个人首发。有多少种不同的首发阵容?答案是C(11,5)。我们查杨辉三角形第11行第5个数字……咦,第11行你可没写过?别担心,用下面的Python函数,随便多大的n都能算。

故事四:零食挑选

小明有8种不同口味的薯片,他只想买3种。有多少种组合?C(8,3) = 56种,来自第8行第3个数字。

用Python“挖宝”——从杨辉三角形取组合数

我们可以写一个函数,先逐行生成杨辉三角形,一直算到第n行,然后直接取出第k个数字。因为中学阶段还没学阶乘公式,这种“模拟搭三角形”的方法更直观。

下面是完整的代码,每一行都加上了中文注释:

def combination_by_triangle(n, k):
    """用杨辉三角形计算组合数C(n, k)"""
    # 如果k比n大或者k是负数,组合数就是0(不可能选出多于总数的人)
    if k < 0 or k > n:
        return 0
    # 为了效率,也可以利用对称性:C(n, k) = C(n, n-k)。但不做优化,直接算。
    # 逐行生成杨辉三角形,直到第n行
    row = [1]                # 第0行:只有1个数字1
    for i in range(1, n + 1):
        # 产生第i行,有i+1个数字,首尾都是1
        new_row = [1] * (i + 1)
        # 中间的数字等于上一行相邻两个数之和
        for j in range(1, i):            # j从1到i-1(不包括首尾)
            new_row[j] = row[j - 1] + row[j]
        row = new_row        # 更新当前行为第i行
    # 现在row就是第n行,返回第k个数字(索引从0开始)
    return row[k]

# --- 测试各种生活场景 ---

# 场景1:从5个同学中选2名队长
print("选法数(5选2):", combination_by_triangle(5, 2))   # 应该输出10

# 场景2:从10个礼物中选3个
print("选法数(10选3):", combination_by_triangle(10, 3)) # 应该输出120

# 场景3:从7个好朋友中选4个去游乐园
print("选法数(7选4):", combination_by_triangle(7, 4))   # 应该输出35

# 场景4:从8种零食中选3种
print("选法数(8选3):", combination_by_triangle(8, 3))   # 应该输出56

# 场景5:从30个彩票号码中选6个(看看有多大)
print("彩票组合数(30选6):", combination_by_triangle(30, 6))  # 输出593775

运行这段代码,你会得到正确的数字。注意最后那个彩票组合数接近60万,所以中一等奖的概率非常非常小哦!

新手最容易犯的3个错误

  1. 行和列从0开始数错
    比如想算“从5个人中选2个”,写成 combination_by_triangle(5, 2) 是对的(第5行第2个)。但有人会写成 (5, 2) 却以为对应第6行,或者写成 (5, 1) 以为第2个是索引1。记住:索引0是第一个选项(选0个人),索引1是第二个选项(选1个人),依此类推。

  2. 忘记检查k的范围
    如果k > n,比如从3个人中选5个队长,这是不可能的。函数中已经加了 if k < 0 or k > n: return 0,但如果你自己手动查表时也要注意。

  3. 误以为行数和数字个数一样
    第n行有n+1个数字,但最高索引是n。比如第5行有6个数字(索引0到5),所以C(5,5)=1在最后一个位置。

完整可运行示例(包含小挑战)

下面把上面的函数和测试代码整理成一个完整的程序,你复制到Python环境里就能运行。最后还有一个“小挑战”等着你。

def combination_by_triangle(n, k):
    """用杨辉三角形计算组合数C(n, k)"""
    if k < 0 or k > n:
        return 0
    # 生成第0行
    row = [1]
    # 逐行构建直到第n行
    for i in range(1, n + 1):
        new_row = [1] * (i + 1)
        for j in range(1, i):
            new_row[j] = row[j - 1] + row[j]
        row = new_row
    return row[k]

# 展示几个常见组合数
print("=== 生活中的组合数 ===")
print("C(5,2) 选队长:", combination_by_triangle(5, 2))
print("C(4,2) 选值日生:", combination_by_triangle(4, 2))
print("C(10,3) 选礼物:", combination_by_triangle(10, 3))
print("C(8,3) 选零食:", combination_by_triangle(8, 3))
print("C(30,6) 彩票:", combination_by_triangle(30, 6))

# 小挑战:从7个好朋友中选4个去游乐园,有多少种方式?
# 试试修改下面的调用,把参数改成(7, 4),看看答案是多少?
print("\n小挑战答案:", combination_by_triangle(7, 4))  # 应该输出35

小挑战
除了7选4,你还能自己出题吗?比如:

  • 从12个同学中选3个参加数学竞赛?
  • 从20个手游英雄中选5个组成战队?
  • 从100本书中选2本暑假阅读?

只要把n和k换成你想要的数字,运行代码就能得到答案。快去试试吧!

相关指引

学会了用杨辉三角形算组合数,你可以进一步探索:

  • 概率计算:组合数是概率的基础。比如掷骰子、扑克牌、抽奖等,先算总情况数,再算有利情况数。
  • 排列与组合的区别:组合不考虑顺序,排列考虑顺序。比如选班长(组合)和排座位(排列)不一样。
  • 用公式直接计算:高中会学阶乘公式 C(n,k) = n! / (k! * (n-k)!),用Python的math库可以快速计算。不过杨辉三角形的暴力方法对n不大时(比如n≤1000)完全够用,而且不容易出错。
  • 二项式定理:杨辉三角形还可以用来展开(a+b)的n次方,每个数字就是展开式的系数。

下一次,当你遇到“有多少种选法”的问题时,别忘了一件事:打开你的Python,召唤杨辉三角形!

例题精讲

1单选题

杨辉三角中,第7行(行号从0开始)的所有数之和是多少?

A64
B128
C256
D512
2判断题

使用杨辉三角计算组合数C(30,15)时,如果采用递推方式逐行构建杨辉三角直到第30行,那么需要构建的行数为31行(包含第0行)。

3填空题
下列函数利用杨辉三角的递推关系计算组合数C(n,k)。请补全代码。
def comb_by_pascal(n, k):
    # 创建一个二维列表,但为了节省空间,使用一维列表逐行更新
    # 初始化第0行
    row = [1]
    for i in range(1, n+1):
        # 生成第i行的列表,长度i+1
        new_row = [1] * (i+1)
        for j in range(1, i):
            new_row[j] = ___  # 填空处
        row = new_row
    return row[k]
4单选题

某班级有10名男生和8名女生,现需要从中选出3名同学参加知识竞赛,要求至少有1名女生。则不同的选法种数是多少?利用组合数计算,以下哪个表达式正确?

AC(18,3) - C(10,3)
BC(18,3) - C(8,3)
CC(8,1) * C(10,2)
DC(8,1) + C(8,2) + C(8,3)
5填空题
现有20个球,其中红色球8个,蓝色球12个。一次性随机抽取5个球,求恰好抽到2个红色球的概率。以下Python代码用于计算该概率,请补全。
import math
def comb(n, k):
    return math.comb(n, k)
total = comb(20, 5)
favorable = ___  # 填空处
prob = favorable / total
print(prob)