CC++ & Algorithm

二项式定理与杨辉三角

困难4
语言版本:通用
概述:通过展开(a+b)的幂次,发现系数规律就是杨辉三角,并学会用编程生成杨辉三角和计算二项式系数。

从买零食到多项式:二项式定理和杨辉三角的编程之旅

你有没有发现,当我们计算 (a+b)² 和 (a+b)³ 的时候,系数好像藏着某种规律?
1, 2, 1 → 1, 3, 3, 1 → 1, 4, 6, 4, 1 → ……
这些数字排成一个三角形,叫做杨辉三角(西方叫帕斯卡三角形)。它不但漂亮,还和组合数二项式定理紧紧相连。学会它,你不仅能快速展开 (a+b)ⁿ,还能在编程中轻松计算组合数,甚至估算 (1.01)^100 这种大幂。

本文就带你从口诀一样的三角形出发,一步步理解二项式定理,并用 C++ 和 Python 把它写进代码里。


1. 从多项式展开中发现的三角形

先回忆一下:

  • (a+b)² = a² + 2ab + b² → 系数:1, 2, 1
  • (a+b)³ = a³ + 3a²b + 3ab² + b³ → 系数:1, 3, 3, 1
  • (a+b)⁴ = a⁴ + 4a³b + 6a²b² + 4ab³ + b⁴ → 系数:1, 4, 6, 4, 1

把这些系数按行排成三角形:

第0行:       1
第1行:      1 1
第2行:     1 2 1
第3行:    1 3 3 1
第4行:   1 4 6 4 1
第5行:  1 5 10 10 5 1

这就是杨辉三角。每一行对应 (a+b)ⁿ 的展开系数,第 n 行第 k 个数(从 0 开始数)就是组合数 C(n, k)(读作“n 选 k”)。

生活中的例子:
你要从 5 个不同口味的棒棒糖里选 2 个,有多少种选法?答案就是 C(5,2) = 10 种。而杨辉三角第 5 行第 2 个数正是 10。


2. 二项式定理:一句话说出所有系数

二项式定理告诉我们:对于任何正整数 n,

(a+b)n=k=0n(nk)ankbk(a+b)^n = \sum_{k=0}^{n} \binom{n}{k} a^{n-k} b^k

其中 (nk)\binom{n}{k} 就是组合数 C(n, k)。

它的含义很简单:展开 (a+b)ⁿ 相当于从 n 个括号里,每次选一个出来取 a 或 b。想要得到 a^{n-k} b^k 这一项,就必须从 n 个括号中选出 k 个取 b,剩下的 n-k 个取 a。不同的选法个数正好是 C(n, k)

思考题:
如果你和 4 个朋友一起玩抛硬币游戏,每人抛一次,想知道恰好出现 2 次正面、3 次反面的概率,就和 C(5,2) 有关。因为 5 次抛掷选择 2 次为正面的方案数就是 C(5,2) = 10。


3. 杨辉三角的构造规则(加法递推)

杨辉三角最神奇的地方在于:除了两边的 1,中间每个数都等于它上方两个数之和

例如第 4 行中间的 6,等于第 3 行的 3 + 3;再比如第 5 行的 10,等于第 4 行的 4 + 6。

  • 首尾固定为 1。
  • 第 i 行第 j 个数(j 从 0 到 i)可由公式得到:
    tri[i][j] = tri[i-1][j-1] + tri[i-1][j]

这个加法规则跟组合数的递推公式 C(n,k) = C(n-1, k-1) + C(n-1, k) 完全一样,因此杨辉三角就是组合数的“加法表”。

生活中的类比:
你每天零花钱的算法:今天零花钱 = 昨天剩下的钱 + 今天新发的钱。杨辉三角的每个数也是“上面两个钱数加起来”。


4. 新手容易犯的错误

❌ 错误1:混淆行和列的下标从0还是从1开始

  • 杨辉三角的第 0 行只有一个数 1。
  • 第 n 行有 n+1 个数,第 0 列和最后一列都是 1。
  • 写代码时,如果循环从 1 开始,容易漏掉边界,导致数组越界。

正确做法
第 i 行数组大小应为 i+1,循环 j 从 0 到 i,首尾单独赋 1,中间用递推。

❌ 错误2:递推时忘记更新上一行的数据

如果用二维数组,递推时要用 triangle[i-1][j-1]triangle[i-1][j]。如果误写成 triangle[i][j-1]triangle[i-1][j-1] 但当前行还没赋值,就会拿错值。

❌ 错误3:计算组合数时直接算阶乘溢出

直接写 n! / (k! * (n-k)!) 在 n=20 时就会非常大,导致整数溢出。用杨辉三角递推或帕斯卡恒等式可以避免大数阶乘。


5. 编程实现:生成杨辉三角并计算二项式系数

下面给出完整可运行的 C++ 和 Python 代码。
代码中变量名都用简短英文单词,并且每条变量定义都加了中文注释,方便你理解。

? C++ 实现(完整版)

#include <iostream>
#include <vector>
using namespace std;

/**
 * 生成杨辉三角的前 n 行(第0行到第n行)
 * @param n 最大行号(非负整数)
 * @return 一个二维vector,每行是一个vector<long long>
 */
vector<vector<long long>> generateYangHui(int n) {
    // triangle: 存储整个三角形,共有n+1行
    vector<vector<long long>> triangle(n + 1);
    for (int i = 0; i <= n; i++) {
        // 第 i 行有 i+1 个数,先分配空间
        triangle[i].resize(i + 1);
        // 首尾都是 1
        triangle[i][0] = 1;
        triangle[i][i] = 1;
        // 中间元素通过递推计算
        for (int j = 1; j < i; j++) {
            // 上一行的第 j-1 列 + 上一行的第 j 列
            triangle[i][j] = triangle[i-1][j-1] + triangle[i-1][j];
        }
    }
    return triangle;
}

/**
 * 打印 (a+b)^n 的展开式(仅显示系数与项)
 * @param n 指数
 */
void printBinomialExpansion(int n) {
    // 先获取杨辉三角第 n 行
    auto triangle = generateYangHui(n);
    auto row = triangle[n];  // 第 n 行的系数列表
    cout << "(a+b)^" << n << " = ";
    for (int k = 0; k <= n; k++) {
        long long coef = row[k];  // 系数 C(n,k)
        // 输出项:coef * a^(n-k) * b^k
        cout << coef;
        if (n - k > 0) cout << "*a";
        if (n - k > 1) cout << "^" << n - k;
        if (k > 0) cout << "*b";
        if (k > 1) cout << "^" << k;
        if (k < n) cout << " + ";
    }
    cout << endl;
}

int main() {
    int n;
    cout << "请输入杨辉三角的行数(从0开始): ";
    cin >> n;

    auto tri = generateYangHui(n);
    // 打印整个三角形
    cout << "\n杨辉三角(第0行到第"<< n <<"行):\n";
    for (int i = 0; i <= n; i++) {
        for (int j = 0; j <= i; j++) {
            cout << tri[i][j] << " ";
        }
        cout << endl;
    }

    // 单独打印第 5 行
    if (n >= 5) {
        cout << "\n第5行: ";
        for (int j = 0; j <= 5; j++) {
            cout << tri[5][j] << " ";
        }
        cout << endl;
    }

    // 打印 (a+b)^n 的展开式
    cout << "\n展开式:\n";
    printBinomialExpansion(n);

    return 0;
}

运行示例(输入 5):

请输入杨辉三角的行数(从0开始): 5

杨辉三角(第0行到第5行):
1 
1 1 
1 2 1 
1 3 3 1 
1 4 6 4 1 
1 5 10 10 5 1 

第5行: 1 5 10 10 5 1 

展开式:
(a+b)^5 = 1*a^5 + 5*a^4*b + 10*a^3*b^2 + 10*a^2*b^3 + 5*a*b^4 + 1*b^5

? Python 实现(完整版)

def generate_yanghui(n):
    """
    生成杨辉三角的前 n+1 行(第0行到第n行)
    :param n: 最大行号
    :return: 列表,每个元素是一行(列表)
    """
    triangle = []
    for i in range(n + 1):
        # 初始化一行,全部填充1,长度 i+1
        row = [1] * (i + 1)
        # 修改中间的元素(索引从1到i-1)
        for j in range(1, i):
            # 递推:上一行左边+上一行右边
            row[j] = triangle[i - 1][j - 1] + triangle[i - 1][j]
        triangle.append(row)
    return triangle

def print_binomial_expansion(n):
    """
    打印 (a+b)^n 的展开式
    :param n: 指数
    """
    # 获取第 n 行系数
    row = generate_yanghui(n)[n]
    terms = []
    for k in range(n + 1):
        coef = row[k]
        term = ""
        if coef != 1 or (n - k == 0 and k == 0):
            term += str(coef)
        if n - k > 0:
            term += "a"
            if n - k > 1:
                term += "^" + str(n - k)
        if k > 0:
            term += "b"
            if k > 1:
                term += "^" + str(k)
        terms.append(term)
    print("(a+b)^{} = {}".format(n, " + ".join(terms)))

# 主程序
n = int(input("请输入行数: "))
tri = generate_yanghui(n)
print("杨辉三角:")
for i, row in enumerate(tri):
    print("行{}: {}".format(i, row))

# 打印第5行系数
if n >= 5:
    print("第5行系数:", tri[5])

# 打印 (a+b)^n 展开式
print_binomial_expansion(n)

运行示例(输入 5):

请输入行数: 5
杨辉三角:
行0: [1]
行1: [1, 1]
行2: [1, 2, 1]
行3: [1, 3, 3, 1]
行4: [1, 4, 6, 4, 1]
行5: [1, 5, 10, 10, 5, 1]
第5行系数: [1, 5, 10, 10, 5, 1]
(a+b)^5 = a^5 + 5a^4b + 10a^3b^2 + 10a^2b^3 + 5ab^4 + b^5

6. 杨辉三角与组合数的更多性质

6.1 每行之和等于 2ⁿ

因为 (1+1)ⁿ = 2ⁿ,展开后各项系数之和正好等于 2ⁿ。
例如第 5 行:1+5+10+10+5+1 = 32 = 2⁵。

6.2 斜线上的数字规律

  • 第 1 条斜线(最左边)全为 1。
  • 第 2 条斜线:1, 2, 3, 4, 5, … 表示自然数。
  • 第 3 条斜线:1, 3, 6, 10, 15, … 是三角形数(可以组成等边三角形点的个数)。
  • 第 4 条斜线:1, 4, 10, 20, … 是四面体数(三维的三角形)。

6.3 帕斯卡恒等式(递推基础)

组合数核心递推公式:
C(n, k) = C(n-1, k-1) + C(n-1, k)
这个公式就是杨辉三角中“上方两数之和”的数学表达。


7. 应用:用二项式定理简化复杂计算

7.1 估算 (1.01)^100

直接计算 1.01 的 100 次方很慢,但利用二项式定理展开:
(1+0.01)^100 = 1 + 100×0.01 + C(100,2)×0.01² + …
取前几项就能得到很好的近似值:约 2.7048(实际上 e ≈ 2.71828)。
编程中常用这种方法快速计算高次幂的近似值。

7.2 计算概率

抛 10 次硬币,恰好出现 6 次正面的概率是 C(10,6)/2¹⁰ = 210/1024 ≈ 0.205。
杨辉三角第 10 行第 6 个数就是 210,直接查表可得。


8. 练习题(附详细解答)

练习1:求 (x+2)^4 的展开式

思路:用二项式定理,系数是杨辉三角第 4 行:1, 4, 6, 4, 1。
注意每一项中 b=2,所以需要乘以 2 的相应次幂。

解答
(x+2)⁴ =
C(4,0) x⁴·2⁰ + C(4,1) x³·2¹ + C(4,2) x²·2² + C(4,3) x¹·2³ + C(4,4) x⁰·2⁴
= 1·x⁴·1 + 4·x³·2 + 6·x²·4 + 4·x¹·8 + 1·1·16
= x⁴ + 8x³ + 24x² + 32x + 16

练习2:用递推方法(不使用阶乘)输出杨辉三角第 n 行的所有数

思路:可以像代码中那样生成整个三角形,但也可以只存一行递推。
下面给出 Python 中只计算第 n 行的递推方法(利用帕斯卡恒等式):

def get_row(n):
    """返回杨辉三角第 n 行(n从0开始)"""
    row = [0] * (n + 1)
    row[0] = 1
    for i in range(1, n + 1):
        # 从后向前更新,避免覆盖
        for j in range(i, 0, -1):
            row[j] = row[j] + row[j - 1]
    return row

n = int(input("请输入行号: "))
print("第{}行:".format(n), get_row(n))

运行(输入 5)得到 [1, 5, 10, 10, 5, 1]


9. 小结与相关指引

二项式定理和杨辉三角是“组合数学”的基石。通过编程,你可以:

  • 快速生成任意行的系数
  • 递推计算组合数而不必担心溢出
  • 运用展开式解决概率、近似计算等实际问题

后续可以学习

  • 组合数的阶乘公式(及大数处理)
  • 二项式系数的模运算(在竞赛中常用)
  • 二项式定理的推广(如牛顿广义二项式定理)
  • 杨辉三角与分形(如谢尔宾斯基三角形)的关系

掌握了这些,你在数学和编程竞赛中会发现自己多了一把“万能钥匙”。

例题精讲

1单选题

已知杨辉三角中,第n行第k个数(从0开始计数)等于组合数C(n,k)。则(x+1)^6的展开式中,x^4的系数是多少?

A10
B15
C20
D30
2判断题

二项式展开式中,项数等于指数n加1。

3填空题
以下函数使用动态规划(杨辉三角递推)计算二项式系数C(n,k)。请补全递推部分。
int binomial(int n, int k) {
    int C[n+1][n+1];
    for (int i=0; i<=n; i++) {
        C[i][0] = C[i][i] = 1;
        for (int j=1; j<i; j++)
            C[i][j] = ___;
    }
    return C[n][k];
}
4单选题

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

A16
B32
C64
D128
5填空题
以下代码用递归方式计算二项式系数C(n,k),基于杨辉三角递推。请补全base case。
int C(int n, int k) {
    if (___ || ___) return 1;
    return C(n-1, k-1) + C(n-1, k);
}