Python中的组合——挑选水果拼盘
中等2选水果、选零食、选队友——用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. 组合的数学规律:公式与验证
组合的总数可以用公式计算: 其中 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 来帮忙。
例题精讲
小明有苹果、香蕉、橙子、草莓、葡萄5种水果,他要挑选3种做水果拼盘(不考虑顺序),请问一共有多少种不同的拼盘?
在Python中,使用itertools.combinations函数生成组合时,combinations('ABC', 2)会返回包含('A','B')、('A','C')、('B','C')三个元组的迭代器。
以下代码使用combinations函数从水果列表中选出所有2种水果的组合,请补全代码。
import itertools
fruits = ['苹果', '香蕉', '橙子']
combo = itertools.combinations(___, ___)
for c in combo:
print(c)关于itertools.combinations函数,下列说法错误的是?
请在下面代码的空白处填入正确的表达式,使得程序输出所有满足“至少包含一个苹果”的水果拼盘组合(从['苹果','香蕉','橙子']中选2种)。
import itertools
fruits = ['苹果','香蕉','橙子']
alls = list(itertools.combinations(fruits, 2))
result = [c for c in alls if ___]
print(result)