python itertools.permutations函数——全排列 函数使用 优化及算法实现思路 150帧gif教程让你看到爽

目录

说明

permutations

一、函数的使用

二、算法

思考

优化

思路

三、完整代码


说明

itertools 被称为「宝石(gem)」和「几乎是最酷的东西 ],它是用来操作迭代器的一个模块,功能非常强大。而今天的主角是组合迭代器这类操作里面的permutations函数。

什么是组合迭代器?组合迭代器(Combinatoric Iterators)——组合操作包括排列,笛卡儿积,或者一些离散元素的选择,组合迭代器就是产生这样序列的迭代器。

permutations

permutations函数返回的是可迭代元素中的一个排列组合(全排列)。

一、函数的使用

使用该方法要先导入itertools模块

import itertools

itertools模块返回大多都是可迭代序列,如果直接输出的话..

    print(itertools.permutations([1, 2, 3]))

 会返回它所在地址

<itertools.permutations object at 0x000001BFAB6B2E00>

因此可遍历返回的迭代对象

    for i in itertools.permutations([1, 2, 3]):
        print(i, end="  ")

结果:

(1, 2, 3)  (1, 3, 2)  (2, 1, 3)  (2, 3, 1)  (3, 1, 2)  (3, 2, 1)  

 另外还能加个参数,表示返回元素的长度

    for i in itertools.permutations([1, 2, 3], 2):
        print(i, end="  ")

结果:

(1, 2)  (1, 3)  (2, 1)  (2, 3)  (3, 1)  (3, 2)  

通过输出它的类型发现

    for i in itertools.permutations([1, 2, 3], 2):
        print(i, end="  ")
        print(type(i))

他返回的迭代对象都是元组。

(1, 2)  <class 'tuple'>
(1, 3)  <class 'tuple'>
(2, 1)  <class 'tuple'>
(2, 3)  <class 'tuple'>
(3, 1)  <class 'tuple'>
(3, 2)  <class 'tuple'>

二、算法

思考

实现算法前我们思考两个问题。

1.permutations函数返回的迭代序列有什么特点?

通过上面的例子我们知道,它返回的迭代序列是按接收进来序列元素的顺序进行迭代的。

如果要让大家写出一个数列的全部排列,那么你们会怎么写呢?我先说我,我是这样数的。以[1,2,3,4]为例,先选择第一个元素(1),然后选择(1)的下一个元素(2),然后选(2)的下个元素(3)··· 选下个元素(4),选了(4)之后,因为已经选了4个元素,长度与传入的序列([1, 2, 3, 4])等长了,所以得到第一个元组(1, 2, 3, 4)。继续,回到上一步(3),(3)因为下一个元素只有(4),而(4)已经选过了,所以就不能再选了。然后再回一步,回到(2),(2)的下一个元素是(3),而(3)已经选过了,所以就选(4),选了(4)之后,现在还剩(3)没选,所以就选(3),选了之后得到第二个元组(1, 2, 4, 3),(2)后面数完了之后就从(3)开始数(此时的序列为(1, 3))···省略···然后遍历到以(4)作为第一个元素···省略···最后得出最终结果。

我想,应该不少人数这用数列都是按这种顺序来数的吧。反正我高中有时候遇到简单一点的排列组合的填空选择题,懒得用A,C那些计算就会用类似这种但低级一点的方法去数。其实这种数法有个专有名词叫深度优先遍历 (Depth First Search) 简称 DFS。

2.permutations函数返回的迭代序列有会重复吗?

通过上面的例子发现,返回的迭代序列没有重复的。但是,当传入的序列里边存在相同元素时,会生成重复的元组。

    for i in itertools.permutations([1, 1, 2]):
        print(i, end="  ")

的结果是

(1, 1, 2)  (1, 2, 1)  (1, 1, 2)  (1, 2, 1)  (2, 1, 1)  (2, 1, 1)  

可以看到产生了重复的元组(1, 1, 2), (1, 2, 1), (2, 1, 1)

而在实际应用中我们一般不需要重复的元素,因此需要去重。最简单的做法就是使用集合去重,但在时间和空间复杂度上讲,这样不可行。在处理海量数据时,如果先遍历出全部结果再用集合去重的话会产生大量重复分支,大大降低执行效率,严重还可能出现卡顿现象。所以我们需要剪枝,而且是在没有生成的时候将其去除。

优化

permutations函数对存在相同元素的列表或元组使用时会产生重复元素, 我们应使用更高效的方法对返回的元素去重

思路

 剪枝:如果此元素与上一元素相等且上一元素未使用,需要剪枝去除。

注意:gif里面忘说了。需要先对原数组进行排序,否则剪枝功能会失效。

三、完整代码

# 自定义Itertools函数
class MyItertools:
    # 使用默认参数L用于定义输出长度
    def permutations(self, nums, L=-1):
        # 当传有长度参数且长度参数符合逻辑时,我们使用传入的参数作为返回长度,否则直接使用列表nums长度作为返回长度
        L = len(nums) if L <= 0 or L > len(nums) else L
        nums.sort()
        # 定义等长标记数组
        used = [False for i in nums]
        # 用于存储每一个输出结果
        result = []

        def dfs(res):
            # 终止条件
            if len(res) == L:
                result.append(res)
                return
            # enumerate函数:同时返回元素的下标和值
            for i, val in enumerate(nums):
                # 如果此元素使用过了直接跳过
                if used[i]:
                    continue
                # 剪枝判断条件
                if i > 0 and val == nums[i - 1] and not used[i - 1]:
                    continue
                # 标记使用
                used[i] = True
                # 选择下一个元素
                dfs(res + [val])
                # 状态重置
                used[i] = False
        dfs([])
        return result


if __name__ == '__main__':
    for i in MyItertools().permutations([1, 2, 1]):
        print(i)

  • 20
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愿此后再无WA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值