从穷举法到插板法:Python解决求和为12的正整数数学问题
在这篇博客中,我们将使用Python来解决一个有趣的小学数学问题:求出所有正整数组合,使得这些数的和为12。我们将演示如何找到这些组合,并计算每个组合的排列数,最终得到所有可能的排列组合数。
问题描述
我们的问题是找到所有满足 (a + b + c + d = 12) 的正整数组合,并计算每个组合的排列数。我们将展示完整的过程,包括如何找到所有组合,计算每个组合的排列数,并汇总最终结果。
解题思路2:插板法
把 12 看成 12 个 1,12 个 1 之间有 11 个间隔,要把 12 分成 4 个正整数的和,相当于在 11 个间隔中插入 3 个隔板,所以共有组合数 𝐶(11,3)=165。
from math import comb
n = 12
k = 4
combinations_count = comb(n - 1, k - 1)
print(f"Using the stars and bars method, the total number of unique combinations is: {combinations_count}")
结果
Using the stars and bars method, the total number of unique combinations is: 165
解题思路2:穷举法
我们将使用Python的两个步骤来解决这个问题:
- 找到所有满足条件的组合。
- 计算每个组合的排列数。
步骤1:找到所有组合
组合数的思考:考虑到每个变量至少为1,因此直接开始分配剩余的8个单位。
一个变量为8:如 (8, 0, 0, 0) => (9, 1, 1, 1)
一个变量为7:如 (7, 1, 0, 0) => (8, 2, 1, 1)
一个变量为6:如 (6, 2, 0, 0) => (7, 3, 1, 1)
依此类推直到 (2, 2, 2, 2) => (3, 3, 3, 3)
总共15种情况:通过系统的分配,可以得到15种组合
我们使用itertools
库中的combinations_with_replacement
函数来生成所有可能的组合。
from itertools import combinations_with_replacement
def find_solutions(n, k):
solutions = set()
for combo in combinations_with_replacement(range(1, n), k):
if sum(combo) == n:
solutions.add(tuple(sorted(combo)))
return solutions
n = 12
k = 4
solutions = find_solutions(n, k)
solutions_list = sorted(solutions)
print(f"Total number of unique combinations: {len(solutions_list)}")
print("Combinations:")
for combination in solutions_list:
print(combination)
运行结果:
Total number of unique combinations: 15
Combinations:
(1, 1, 1, 9)
(1, 1, 2, 8)
(1, 1, 3, 7)
(1, 1, 4, 6)
(1, 1, 5, 5)
(1, 2, 2, 7)
(1, 2, 3, 6)
(1, 2, 4, 5)
(1, 3, 3, 5)
(1, 3, 4, 4)
(2, 2, 2, 6)
(2, 2, 3, 5)
(2, 2, 4, 4)
(2, 3, 3, 4)
(3, 3, 3, 3)
步骤2:计算排列数
接下来,我们计算每个组合的排列数。我们使用math
库中的factorial
函数来实现这个过程。
from math import factorial
from collections import Counter
def count_permutations(combination):
counts = Counter(combination)
denominator = 1
for count in counts.values():
denominator *= factorial(count)
return factorial(len(combination)) // denominator
# 计算每个组合的排列数
permutation_counts = [count_permutations(comb) for comb in solutions_list]
# 统计每个排列数出现的次数
counter = Counter(permutation_counts)
# 生成输出表达式
output_parts = [f"{count}x{permutation_counts.count(count)}" for count in set(permutation_counts)]
# 计算总排列数
total_permutations = sum(permutation_counts)
output_expression = " + ".join(output_parts)
print(f"\nTotal number of unique solutions: {total_permutations}")
print("Detailed calculation:")
print(output_expression)
print("\nEach combination with its permutation count:")
for combination, perm_count in zip(solutions_list, permutation_counts):
print(f"{combination} - {perm_count} permutations")
运行结果:
Total number of unique solutions: 165
Detailed calculation:
4x2 + 12x8 + 24x2 + 6x2 + 1x1
Each combination with its permutation count:
(1, 1, 1, 9) - 4 permutations
(1, 1, 2, 8) - 12 permutations
(1, 1, 3, 7) - 12 permutations
(1, 1, 4, 6) - 12 permutations
(1, 1, 5, 5) - 6 permutations
(1, 2, 2, 7) - 12 permutations
(1, 2, 3, 6) - 24 permutations
(1, 2, 4, 5) - 24 permutations
(1, 3, 3, 5) - 12 permutations
(1, 3, 4, 4) - 12 permutations
(2, 2, 2, 6) - 4 permutations
(2, 2, 3, 5) - 12 permutations
(2, 2, 4, 4) - 6 permutations
(2, 3, 3, 4) - 12 permutations
(3, 3, 3, 3) - 1 permutation
可视化结果
为了更好地展示每个组合的排列数及其频率,我们使用matplotlib
生成一个条形图。这样可以直观地看到每个组合的排列数。
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
# 设置字体以支持中文
plt.rcParams['font.sans-serif'] = ['SimHei'] # 使用SimHei字体
plt.rcParams['axes.unicode_minus'] = False # 正常显示负号
# 数据
labels = [str(combination) for combination in solutions_list]
counts = permutation_counts
# 创建条形图
plt.figure(figsize=(15, 8))
plt.barh(labels, counts, color='skyblue')
plt.xlabel('排列数')
plt.ylabel('组合')
plt.title('每个组合的排列数')
plt.grid(axis='x', linestyle='--', alpha=0.7)
for index, value in enumerate(counts):
plt.text(value, index, str(value))
plt.tight_layout()
# 保存为图片
plt.savefig('permutation_counts.png', dpi=300, bbox_inches='tight')
plt.show()
通过生成的条形图,可以直观地看到每个组合的排列数,并理解每个组合在总排列数中的贡献。
总结
穷举法在解决复杂问题时非常有用,特别是在没有明确数学模型的情况下。其关键在于有一个清晰的规划和分类思路,确保所有可能性都被考虑到且不重复、不遗漏。在这篇博客中,我们展示了如何使用Python解决一个小学数学问题,并通过组合和排列的方法找出所有满足条件的组合及其排列数。这不仅帮助孩子们更好地理解数学问题,还展示了计算机编程在解决实际问题中的强大能力。
通过这篇博客,我们探索了两种不同的解题思路:穷举法和插板法。穷举法通过系统地枚举所有可能的组合来找到答案,而插板法则通过巧妙的数学转化快速解决问题。这两种方法展示了数学问题的多样性和灵活性,帮助孩子们学到多种解题技巧,培养他们的创造力和思考能力。
无论是直接的穷举法,还是巧妙的插板法,每种方法都有其独特的价值和应用场景,鼓励孩子们在解决问题的过程中不断创新和思考。
希望这篇博客对您有所帮助。如果有任何问题或建议,欢迎留言讨论。
完整代码
以下是完整的Python代码,包含了所有步骤和可视化部分:
from itertools import combinations_with_replacement
from math import factorial
from collections import Counter
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
def find_solutions(n, k):
solutions = set()
for combo in combinations_with_replacement(range(1, n), k):
if sum(combo) == n:
solutions.add(tuple(sorted(combo)))
return solutions
def count_permutations(combination):
counts = Counter(combination)
denominator = 1
for count in counts.values():
denominator *= factorial(count)
return factorial(len(combination)) // denominator
# 参数
n = 12
k = 4
# 找到所有组合
combinations = find_solutions(n, k)
combinations_list = sorted(combinations)
print(f"Total number of unique combinations: {len(combinations_list)}")
print("Combinations:")
for combination in combinations_list:
print(combination)
# 计算每个组合的排列数
permutation_counts = [count_permutations(comb) for comb in combinations_list]
# 生成输出表达式
output_parts = [f"{count}x{permutation_counts.count(count)}" for count in set(permutation_counts)]
# 计算总排列数
total_permutations = sum(permutation_counts)
output_expression = " + ".join(output_parts)
print(f"\nTotal number of unique solutions: {total_permutations}")
print("Detailed calculation:")
print(output_expression)
print("\nEach combination with its permutation count:")
for combination, perm_count in zip(combinations_list, permutation_counts):
print(f"{combination} - {perm_count} permutations")
# 创建条形图展示每个组合及其排列数
plt.figure(figsize=(15, 8))
x_labels = [str(combination) for combination in combinations_list]
y_values = permutation_counts
plt.barh(x_labels, y_values, color='skyblue')
plt.xlabel('排列数')
plt.ylabel('组合')
plt.title('每个组合的排列数')
plt.grid(axis='x', linestyle='--', alpha=0.7)
for index, value in enumerate(counts):
plt.text(value, index, str(value))
plt.tight_layout()
# 保存为图片
plt.savefig('permutation_counts.png', dpi=300, bbox_inches='tight')
plt.show()