python实现排列组合,官方代码+自己写

想象一下,现在有A、B、C、D、E五个人中需要抽出三个人去比赛,请问有多少种参赛阵容?
这是一个简单的排列组合问题,结果如下:
ABC-ABD-ABE-ACD-ACE-ADE
BCD-BCE-BDE
CDE
聪敏的你也许已经看出来了一点规律

好了,现在我们用python自己实现个寂寞

先分析一下,可以感觉出来这个方法是FILO。像是一个井盖,可以容纳三个人,A先下井,B跟着下井,C其次。因为容量为3,C需要立马出去,D进来,当然D后面还有E排队等着下矿。E出去后B出去靠边站,C下井当老二,又是新的一轮。当D当完老二后,A出栈,B入栈当老大又是新的一轮。
显然我们需要用到循环或者是递归。但是递归不仅不容易理解,而且很占内存。不过我都给他防回去了,因为我都没做出来😔。
我的思路是一个函数接受三个参数(list, num, ret),list为需要组合的对象们,num为每个组合的规模,ret是为了递归后写入新的数据,就是把ret当作一个栈来用。搞了两个小时,愣是搞不明白。。。。。。。。。
我一直在进行递归方向的尝试,而且还在for循环里递归,最终觉得递归不是一个好的方法,是时候放弃了。

最后我顿悟看了官网手册

下面的中文注释是我的理解

def combinations(iterable, r):
    # combinations('ABCD', 2) --> AB AC AD BC BD CD
    # combinations(range(4), 3) --> 012 013 023 123
    pool = tuple(iterable)#强制可迭代对象对象成为一个稳定的元组,I suppose...
    n = len(pool) #获取元组长度
    if r > n: #如果小组最低数量大于总人数,则无意义
        return #并非返回None,因为下文有yield,所以返回的是个空的生成器
    indices = list(range(r)) #一个长度为要求小组长度的索引
    yield tuple(pool[i] for i in indices) #把前r个作为一个小组来
    while True: #开始循环输出不同的索引组合
        for i in reversed(range(r)):
            if indices[i] != i + n - r:
                break
        else:
            return
        indices[i] += 1 #for循环结束后,i的值仍未被GC回收,所以可以使用i    
        for j in range(i+1, r):
            indices[j] = indices[j-1] + 1
        yield tuple(pool[i] for i in indices)

看到到后我发现,自己好笨啊,没有直接从数字出发。
其实上面的while循环部分就干了一件事,对索引进行排列组合。其实我们唯一需要做的就是从一个索引列表如:[0, 1, 2, 3, 4]中输出以下索引:
[0, 1, 3]
[0, 1, 4]
[0, 2, 3]
[0, 2, 4]
[0, 3, 4]
[1, 2, 3]
[1, 2, 4]    
[1, 3, 4]
[2, 3, 4]

那么搞清楚了方法,具体的代码就容易了然而并没有搞清楚那个whil循环


只能说那些人太聪明了。自己感觉python简单只不过是因为无数前人在努力,不说了,日后填坑






评论

此博客中的热门博文

我们应该满足欲望吗?——谈谈为什么感觉不到幸福

浅谈公民、国家、政府和政党的区别

should we satisfy our desire? Yes, it's justified