前言
组合问题即从 个元素中抽取出 个元素,其中 。这个问题的算法很多,基本可以分为递规法,基于用树的分支法,或计数器法,这些办法虽然可以解决问题,但是大多比较麻烦,代码也比较繁琐。本方设计了一种基于二进制的遍历算法,不仅代码量少实现简单,而且算法高效计算速度快。
实现原理
当有 个元素时,可以看成序列: 的向量,每个向量中的任何一个维度只有 两个值。举例来说,当 时,可以排列成以下的列表。
A1 A2 A3 A4 A5 A6 A7 A8
0 0 0 0 0 0 1 1
0 0 0 0 0 1 0 1
...(省略若干行) ...
1 0 0 1 0 0 0 0
1 0 1 0 0 0 0 0
1 1 0 0 0 0 0 0
每行的0和1的组合,我们可以看成一个八位二进制的值,比如第1行 0 0 0 0 0 0 1 1
可以表示为
。这样所表示的数据的范围是
,即
共256个元素,所以我们只要遍历这些值,然后将其转换为二进制即可。用
表示选中,
表示未选中。当所有组合遍历实现以后,再计算每一行中
的个数,从而实现
。
实现方法
本文先给出实现方法。求全组合问题,其实可以非常简单,如下代码所示,可以以2行代码求出所有的组合。
def permutation(n):
return [bin(i)[2:].rjust(n,'0') for i in range(2**n)]
如果求组合 几行代码也可以搞定。
def permutation(n, m):
list = [] # 用于保存结果
for i in range(2**n):
s = '{}'.format(bin(i)[2:].rjust(n,'0'))
if s.count('1') == m: # 包括 m 个 1,则添加进结果列表
list.append(s)
return list
比如,求 ,使用以下测试代码:
r = permutation(8, 2)
print("C(8, 2) = " + str(len(r)) + ", listed as follows")
print('\t'.join(["A" + str(i) for i in range(8)]))
for v in r:
print('\t'.join(v))
结果输出如下:
C(8, 2) = 28, listed as follows
A0 A1 A2 A3 A4 A5 A6 A7
0 0 0 0 0 0 1 1
0 0 0 0 0 1 0 1
0 0 0 0 0 1 1 0
0 0 0 0 1 0 0 1
0 0 0 0 1 0 1 0
0 0 0 0 1 1 0 0
0 0 0 1 0 0 0 1
0 0 0 1 0 0 1 0
0 0 0 1 0 1 0 0
0 0 0 1 1 0 0 0
0 0 1 0 0 0 0 1
0 0 1 0 0 0 1 0
0 0 1 0 0 1 0 0
0 0 1 0 1 0 0 0
0 0 1 1 0 0 0 0
0 1 0 0 0 0 0 1
0 1 0 0 0 0 1 0
0 1 0 0 0 1 0 0
0 1 0 0 1 0 0 0
0 1 0 1 0 0 0 0
0 1 1 0 0 0 0 0
1 0 0 0 0 0 0 1
1 0 0 0 0 0 1 0
1 0 0 0 0 1 0 0
1 0 0 0 1 0 0 0
1 0 0 1 0 0 0 0
1 0 1 0 0 0 0 0
1 1 0 0 0 0 0 0
结论
综上,我们只需要利用二进制表示每一种情况,这样只需要 种情况全部都可以考虑进来,且不需要任何多余的计算,所以计算效果也特别高。