CC++ & Algorithm

Python中的组合——挑选水果拼盘

中等2
语言版本:C++Python
概述:用挑选水果的例子解释组合的概念,以及如何使用combinations函数生成所有组合。

选水果、选零食、选队友——用Python搞定组合问题

你有没有遇到过这样的场景:家里有苹果、香蕉、橘子三种水果,你想挑两种做水果拼盘。苹果+香蕉、苹果+橘子、香蕉+橘子,这三种搭配都算一种,但苹果+香蕉和香蕉+苹果是同一种拼盘——因为拼盘里最终放的是这两种水果,顺序不重要。这种“选几个,不管顺序”的数学概念,就叫做组合

在编程中,我们经常需要枚举出所有的组合,比如:从一堆零食中挑出几样、从班级同学中选出几个参加比赛(谁先谁后无所谓)、从彩票号码中选几个数字(顺序不决定奖金)。Python 的 itertools.combinations 函数就是专门干这个的——只要告诉它“从哪些元素里选”和“选几个”,它就能列出所有不重复的搭配。

1. 组合 vs 排列:关键区别在“顺序”

先通过两个例子帮你分清:

  • 排列:从三个字母 A、B、C 中选两个,并排成一列。AB 和 BA 是两种不同的排列,因为顺序不同。总共有 6 种:AB、AC、BA、BC、CA、CB。
  • 组合:从三个字母中选两个,组成一个“组”。AB 和 BA 里的字母都一样,只是顺序不同,但组合只看成员,所以只算一种。总共 3 种:AB、AC、BC。

生活中也是同样的道理:

  • 排队买冰淇淋,谁站在前面谁站在后面不一样 → 排列问题
  • 从篮子里抓一把糖果,抓到的糖果种类相同就算同一把 → 组合问题

2. 用 combinations 生成组合:就像排列的“亲兄弟”

combinations 函数和之前学过的 permutations(排列)用法非常像,只是它返回的是“不考虑顺序”的结果。它同样来自 itertools 模块,所以要先导入。

基本语法:

import itertools
组合对象 = itertools.combinations(列表或序列, 选择个数)

示例1:水果拼盘(原例子保留)

import itertools

# 三种水果
fruits = ["苹果", "香蕉", "橘子"]

# 从三种水果中选两种,一共有多少种组合?
combs = list(itertools.combinations(fruits, 2))

print("所有两个水果的组合:")
for c in combs:
    print(c)

运行输出:

所有两个水果的组合:
('苹果', '香蕉')
('苹果', '橘子')
('香蕉', '橘子')

注意:只有三种,没有出现 ('香蕉', '苹果'),因为组合认为它跟 ('苹果', '香蕉') 是同一个。

示例2:从朋友中选两人去值日(顺序无所谓)

import itertools

# 四个同学的名字
classmates = ["小明", "小红", "小刚", "小丽"]

# 每天选两人值日,一共有多少种选法?
pairs = list(itertools.combinations(classmates, 2))

print("所有两人值日组合:")
for pair in pairs:
    print(pair)

输出:

所有两人值日组合:
('小明', '小红')
('小明', '小刚')
('小明', '小丽')
('小红', '小刚')
('小红', '小丽')
('小刚', '小丽')

一共 6 种,正好对应从 4 个人中选 2 人的组合数公式:C(4,2)=6。

3. 组合的数学规律:公式与验证

组合的总数可以用公式计算: C(n,k)=n!k!(nk)!C(n, k) = \frac{n!}{k! \cdot (n-k)!} 其中 n 是总元素个数,k 是每次选的个数。

例如,从 5 种零食中选 3 种,组合数 = 5!/(3!×2!) = (5×4×3)/(3×2×1) = 10。我们用代码验证:

import itertools

# 五种零食
snacks = ["薯片", "巧克力", "饼干", "糖果", "果冻"]

# 选三种的所有组合
combos = list(itertools.combinations(snacks, 3))

print("组合总数:", len(combos))  # 应该输出10
for combo in combos:
    print(combo)

运行结果确实有 10 种,每种都不重复、不计顺序。

4. 新手容易犯的错误

错误一:把 combinations 写成 permutations

如果你用了 permutations,就会得到包含顺序的重复结果(AB、BA 都算),而且总数量会翻倍(n!/(n-k)! 个)。比如从 3 个水果中选 2 个排列,会得到 6 个结果,而组合只有 3 个。使用时一定要想清楚场景:顺序是否重要?

错误二:忘记将结果转成列表直接打印

combinations 返回的是迭代器对象,不能直接查看内容。初学者如果写 print(combs) 只会看到类似 <itertools.combinations object at 0x...> 的东西。正确做法是用 list() 转换,或者用 for 循环逐个打印。

错误三:选择个数大于总元素个数

如果写 itertools.combinations(snacks, 6),而 snacks 只有 5 个元素,那组合是空的,不会报错,但你可能得不到任何结果。所以写代码前要检查 k ≤ n。

5. 完整可运行的示例:抽卡片游戏

假设有 6 张卡片,上面分别写着数字 1 到 6,你要随机抽 4 张,所有可能的组合有哪些?下面是一个完整的程序:

import itertools

# 六张卡片:数字 1 到 6
cards = ["1", "2", "3", "4", "5", "6"]  # 用字符串方便显示

# 选 4 张的所有组合
hand_combinations = list(itertools.combinations(cards, 4))

print("所有可能的4张卡片组合:")
for hand in hand_combinations:
    # 用 join 把元组里的数字连起来显示
    print("、".join(hand))

print("\n一共有", len(hand_combinations), "种组合。")

运行后你会看到 15 种组合(C(6,4)=15),例如 "1、2、3、4"、"1、2、3、5" 等等。注意没有顺序重复,比如不会同时出现 "1、2、3、4" 和 "4、3、2、1"。

6. 更多生活中的组合场景

  • 点菜:从 8 道菜中选 3 道组成套餐,不考虑上菜顺序,共有 C(8,3)=56 种搭配。
  • 抽奖:从 49 个数字中选 6 个,不计顺序,这就是彩票规则,组合数高达 13,983,816 种。
  • 组队:从 10 个同学中选 5 人参加拔河比赛,谁在左边的位置不重要,只关心是哪几个人,组合数 C(10,5)=252。

7. 相关知识点指引

如果你想进一步探索,可以看看这些内容:

  • itertools.permutations:排列函数,与组合相反,顺序重要。适合安排比赛出场顺序、密码排列等问题。
  • itertools.combinations_with_replacement:允许同一个元素被多次选出的组合(比如从口味罐里抓出 3 颗糖,每种口味可以重复抓)。适合计算“允许重复”的搭配问题。
  • 数学组合数公式math.comb(n, k) 可以直接计算组合数量,不需要列出所有组合。例如 import math; print(math.comb(6, 4)) 输出 15。

组合不仅仅在数学竞赛中出现,更是编程中解决“可能性枚举”问题的利器。下次遇到“从一堆东西里选几个,无论顺序”的问题时,记得用 combinations 来帮忙。

例题精讲

1单选题

小明有苹果、香蕉、橙子、草莓、葡萄5种水果,他要挑选3种做水果拼盘(不考虑顺序),请问一共有多少种不同的拼盘?

A10种
B15种
C20种
D30种
2判断题

在Python中,使用itertools.combinations函数生成组合时,combinations('ABC', 2)会返回包含('A','B')、('A','C')、('B','C')三个元组的迭代器。

3填空题
以下代码使用combinations函数从水果列表中选出所有2种水果的组合,请补全代码。

import itertools
fruits = ['苹果', '香蕉', '橙子']
combo = itertools.combinations(___, ___)
for c in combo:
    print(c)
4单选题

关于itertools.combinations函数,下列说法错误的是?

A返回的是一个迭代器,可以转换为列表查看所有组合
B生成的组合中元素顺序与输入序列中出现的顺序一致
C可以处理重复元素的情况,如combinations([1,1,2], 2)会生成(1,1)和(1,2)
D组合中不包含元素自身重复的元组(即每个元素在组合中最多出现一次)
5填空题
请在下面代码的空白处填入正确的表达式,使得程序输出所有满足“至少包含一个苹果”的水果拼盘组合(从['苹果','香蕉','橙子']中选2种)。

import itertools
fruits = ['苹果','香蕉','橙子']
alls = list(itertools.combinations(fruits, 2))
result = [c for c in alls if ___]
print(result)