Python, Permutation and Combination

Python, Permutation and Combination

排列和组合

排列 P ( n , r ) = A n r = n ! ( n − r ) ! P(n,r)=A_n^r = \frac{n!}{(n-r)!} P(n,r)=Anr=(nr)!n!

组合 C ( n , r ) = C n r = ( r n ) = n ! r ! ( n − r ) ! C(n,r)=C_n^r=(_r^n)=\frac{n!}{r!(n-r)!} C(n,r)=Cnr=(rn)=r!(nr)!n!

重复:放回不放回

参考 Math is Fun

例子

[1,2,3,4]

排列无重复 P(4,2)

[[1, 2], [1, 3], [1, 4], [2, 1], [2, 3], [2, 4], [3, 1], [3, 2], [3, 4], [4, 1], [4, 2], [4, 3]]

排列有重复

[[1, 1], [1, 2], [1, 3], [1, 4], [2, 1], [2, 2], [2, 3], [2, 4], [3, 1], [3, 2], [3, 3], [3, 4], [4, 1], [4, 2], [4, 3], [4, 4]]

全排列P(4,4)

[[1, 2, 3, 4], [1, 2, 4, 3], [1, 3, 2, 4], [1, 3, 4, 2], [1, 4, 2, 3], [1, 4, 3, 2], [2, 1, 3, 4], [2, 1, 4, 3], [2, 3, 1, 4], [2, 3, 4, 1], [2, 4, 1, 3], [2, 4, 3, 1], [3, 1, 2, 4], [3, 1, 4, 2], [3, 2, 1, 4], [3, 2, 4, 1], [3, 4, 1, 2], [3, 4, 2, 1], [4, 1, 2, 3], [4, 1, 3, 2], [4, 2, 1, 3], [4, 2, 3, 1], [4, 3, 1, 2], [4, 3, 2, 1]]

组合无重复C(4,2)

[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]

组合有重复

[[1, 1], [1, 2], [1, 3], [1, 4], [2, 2], [2, 3], [2, 4], [3, 3], [3, 4], [4, 4]]

实现思路

1. 从迭代的角度,对于[1,2,3,4], 先取出一个元素,对余下元素进行取k排列

2. 从搜索回溯的角度,对于[1,2,3,4], 先对1取出,标记,然后对2,3,4, 回溯

代码

# %%
def permutation1(arr, k, combination=False):
    '''
    arr : 传入数组
    k : 选出的元素个数
    combination : True代表是组合,默认是排列,有顺序。
    '''
    def permutation_sub(temp, arr, result, s):
        '''
        递归方法
        temp : 以参数形式存储单个结果
        arr : 原始数组
        result : 存储所有排列结果
        s : 组合时是用到,确保每次递归后的元素不再参与递归。
        '''
        #  如果结果达到目标数量
        if len(temp) == k:
            # 添加到总的数组里
            result.append(list(temp))
            # 返回上一层递归
            return
        # 默认的是排列
        itero = range(len(arr))
        # 如果是组合
        if combination:
            # 需要保证递归过的元素不在参与递归
            itero = range(s, len(arr))
        # 选中一个元素arr[i]
        for i in itero:
            # temp+[arr[i]] : 本次递归产生的元素的结果
            # arr[:i]+arr[i+1:] : 除去arr[i]后arr所剩下的元素,对剩下元素进行排列
            # result : 结果集
            # i : 纪录for循环位置
            permutation_sub(
                temp+[arr[i]], arr[:i]+arr[i+1:], result, i)
    res = []
    permutation_sub([], arr, res, 0)
    return res


def permutation2(arr, k, combination=False, repeat=False):
    '''
    arr : 传入数组
    k : 选出的元素个数
    combination : True代表是组合,默认是排列,有顺序。
    repeat : 是否重复, 默认不重复
    '''
    def permutation_sub(temp, arr, used,   s, res):
        '''
        深度优先遍历+回溯
        temp : 一次结果存储
        arr : 原始数组
        used : 记录元素是否被遍历了
        s : 排列需要,同上
        res : 最终结果集
        '''
        # 如果 达到目标元素个数,
        if len(temp) == k:
            # 添加进结果集
            res.append(temp[:])
            # 结果本次深度搜索
            return
        # 默认是排列
        itero = range(len(arr))
        # 如果组合
        if combination:
            # 需要保证搜索过的元素不在参与搜索
            itero = range(s, len(arr))
        for i in itero:
            # 如果第i元素没有被使用过
            if not used[i]:
                # 添加进一次结果集
                temp.append(arr[i])
                # 标记为使用,如果可以重复的话,就标记为一直未使用
                used[i] = not repeat
                # 递归对后续元素进行搜索
                permutation_sub(temp, arr, used, i, res)
                # 在最深层pop出元素,进行回溯
                temp.pop()
                # 回溯后标记为未使用
                used[i] = False
    # 初始都为未使用.
    # 不能用used=[False]*len(arr),习惯不好。
    # a=[[1]*3]*2
    # a[0][0]=2
    # [[2, 1, 1], [2, 1, 1]]
    used = [False for _ in range(len(arr))]
    res = []
    permutation_sub([], arr, used, 0, res)
    return res

结果

if __name__ == "__main__":
    data = [1, 2, 3, 4]
    k = 2
    # 结果
    print("排列无重复P(4,2)")
    print(permutation1(data, k, False))
    print(permutation2(data, k, False, False))
    print("排列有重复4,2")
    print(permutation2(data, k, False, True))
    print("组合无重复P(4,2)")
    print(permutation1(data, k, True))
    print(permutation2(data, k, True, False))
    print("组合有重复4,2")
    print(permutation2(data, k, True, True))

排列无重复P(4,2)
[[1, 2], [1, 3], [1, 4], [2, 1], [2, 3], [2, 4], [3, 1], [3, 2], [3, 4], [4, 1], [4, 2], [4, 3]]
[[1, 2], [1, 3], [1, 4], [2, 1], [2, 3], [2, 4], [3, 1], [3, 2], [3, 4], [4, 1], [4, 2], [4, 3]]
排列有重复4,2
[[1, 1], [1, 2], [1, 3], [1, 4], [2, 1], [2, 2], [2, 3], [2, 4], [3, 1], [3, 2], [3, 3], [3, 4], [4, 1], [4, 2], [4, 3], [4, 4]]
组合无重复P(4,2)
[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
组合有重复4,2
[[1, 1], [1, 2], [1, 3], [1, 4], [2, 2], [2, 3], [2, 4], [3, 3], [3, 4], [4, 4]]

速度

if __name__ == "__main__":
    data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    k = 4
    # jupyter 魔法糖
    # 循环执行随机次数,每次运行100个循环
    %timeit - n 100 permutation1(data, k, False)
    %timeit - n 100 permutation2(data, k, False, False)

def permutation1(arr, k, combination=False):…
6.2 ms ± 56.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
4.71 ms ± 43.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

显然第二个快 1 3 \frac{1}{3} 31

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值