Python - 数据结构与算法之 排列与组合

目录

一.引言

二.排列 A-Permute

◆ 定义

◆ 计算

◆ 性质

◆ 实现

三.组合 C-Combine

◆ 定义

◆ 计算

◆ 性质

◆ 实现

四.经典算法题目

1.全排列 [无重复]

2.全排列 [有重复]

3.组合 [可重复]

4.子集 [无重复]

5.子集 [有重复]

五.总结


一.引言

关于排列前面已经介绍了一部分算法,例如求数组的全排列,求子集等等,我们可以使用回朔的方法进行计算,今天主要讲下数学上排列与组合的计算方式,因为有一些题目可以巧妙地使用排列组合快速得到结果,下面整理下定义与 Python 实现方式。

二.排列 A-Permute

◆ 定义

排列公式采用如下形式:

                         A_{6}^{2} 

其中 6、2 代表从 6 个元素里面选 2 个排列起来,注意这一要求有顺序 !

丽茹从 1-6 中选 2 个数字组成两位数,这个结果就是 A(6, 2)

又丽茹从 1-6 编号的同学中,一个当数学课代表,一个当语文课代表,那结果也是 A(6, 2)

◆ 计算

这是百度百科关于排列的定义和计算公式,下面我们简化下流程方便记忆:

A_{6}^{2} = \frac{6!}{6-2=4!} = \frac{6 \cdot 5 \cdot 4 \cdot 3 \cdot 2 \cdot 1 }{4 \cdot 3 \cdot 2 \cdot 1 } = 6 \cdot 5

有一个好记的方法,只要 A(m, n) 出来,我们就从 m! 开始往后写,写到第 n 个数字停止即可。例如 A(5, 3) 我们从 5 开始往后写 3 个数字即 5x4x3,ok 搞定了!

Tips:

如果从实际含义理解的话这是个无放回的问题,6 个里面选 2 个,第一次我们选一个人出来有 6 种可能,由于不能重复,所以现在还剩 5 个人,我们再从 5 个人里面随便抓一个出来就凑齐 2 个人了,所以是 6 x 5。

◆ 性质

A_n^{n} = n! = n \cdot \ (n-1) \cdots 2 \cdot 1

◆ 实现

def factorial(num):"""计算阶乘"""result = 1for i in range(1, num + 1):result *= ireturn resultdef permutation(m, n):"""计算排列 A(m, n)"""return factorial(m) // factorial(m-n)

计算两个阶乘相除即可,当然按照我们红色字体部分,我们可以快速计算,避免 (m-n)!的重复计算。

def fast_permutation(m, n):if m == 1:return 1# 从 m 开始往后乘 n 次完活re = 1for i in range(n):re *= mm -= 1return re

三.组合 C-Combine

◆ 定义

组合公式采用如下形式:

                         C_{6}^{2} 

其中 6、2 代表从 6 个元素里面选 2 个组合起来,注意这里 无顺序

丽茹从 1-6 中选 2 个数字,这个结果就是 C(6, 2)

又丽茹从 1-6 编号的同学中选两个打扫卫生,那结果也是 C(6, 2)

◆ 计算

还是先把长长的公式掏出来,下面我们简化下流程方便记忆: 

C_{6}^{2} = \frac{6!}{2! \cdot 4!} = \frac{6 \cdot 5 \cdot 4 \cdot 3 \cdot 2 \cdot 1}{(2 \cdot 1) \cdot (4\cdot 3\cdot 2\cdot 1)} = \frac{6\cdot 5}{1\cdot 2}

有一个好记的方法,对于给定的 m、n,分母 m 从大往小写 n 个,分子从 1 到 n 写下来 。例如 C(6, 2) 直接 6 从大到小写 2 个即 6x5,再从 1写到 n,即 1x2,除去吧,一除一个不知道。

Tips:

继续从实际情况下理解一下,首先这里还是无放回的问题,我们第一次能够取 m 个,下一次能取 m-1 个,依次类推。以 (3, 2) 为例,我们可以取 (1, 2) (1, 3)、(2,1) (2,3)、(3,1),(3,2),2个数字的组合 (1,2)、(2,1) 是一样的,所以我们重复了,重复了多少次就和 n 有关系了,n 能够排列 n! 种组合,所以就是 3 * (3-1) 直到 n 也就是 3 * 2,重复的话是 2! = 2 * 1,所以一共就 3 种组合 (1, 2)、(1、3)、(2, 3)。即 A(m, n) / n!

◆ 性质

若 a+b = n 则存在以下关系,这个可以逆向思维理解一下,这里就不多解释了:

C_n^{a} = C_n^{b}

根据公式也可以推导出特殊情况:

C_n^{0} = C_n^{n} = 1

◆ 实现

def factorial(num):"""计算阶乘"""result = 1for i in range(1, num + 1):result *= ireturn resultdef combination(m, n):"""计算排列 A(m, n)"""return factorial(m) // factorial(m-n) // factorial(n)

套公式的话按上面的方法写,也可以用我们红字的快速计算方法,避免重复计算。按照这个公式直接套用即可 C(m, n) = A(m, n) / n!

def fast_combination(m, n):if m == 1:return 1re = 1for i in range(n):re *= mm -= 1return re // factorial(n)

四.经典算法题目

下面四个题目来自: Python - Divide Conquer 分治 & Backtrack 回朔

1.全排列 [无重复]

全排列[无重复]: https://leetcode-cn.com/problems/permutations/

class Solution(object):def permute(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""re = []def dfs(position):if position == len(nums) - 1:re.append(nums[:])# 固定第一个位置,再寻找下一个位置,循环for i in range(position, len(nums)):nums[position], nums[i] = nums[i], nums[position]dfs(position + 1)nums[position], nums[i] = nums[i], nums[position]dfs(0)return re

这里 for i in range(position, len(nums)) 就是一个不断逼近的过程,就像 A(m,m) 一样,第一次有 m 个选择,下一次有 m-1 个选择,一直循环到最后一个数字。

上面这版交换索引不好理解的话,就用我们全排列的思想,固定第一个无放回,从剩下的找,这里无放回的操作我们使用 set,用过的就放到 set 不能用了:

class Solution(object):def permute(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""re = []def dfs(cur, used):if len(cur) == len(nums):re.append(cur[:])for i in range(0, len(nums)):if nums[i] in used:continue# 下一层进发cur.append(nums[i])used.add(nums[i])dfs(cur, used)# 恢复状态cur.pop()used.remove(nums[i])used = set()dfs([], used)return re

2.全排列 [有重复]

全排列[有重复]: https://leetcode.cn/problems/permutations-ii/

class Solution(object):def permuteUnique(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""re = []def dfs(position):if position == len(nums) - 1:re.append(nums[:])repeat = set()for i in range(position, len(nums)):if nums[i] in repeat:continuerepeat.add(nums[i])nums[position], nums[i] = nums[i], nums[position]dfs(position + 1)nums[position], nums[i] = nums[i], nums[position]dfs(0)return re

固定第一个位置,固定第二个位置依次类推,这里变换位置时,如果有重复就不再变换了,排除这次的结果。

同理,我们可以使用人脑思维,即固定一个,再找剩下的不过同理需要进行去重操作:

class Solution(object):def permuteUnique(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""re = []def dfs(cur, used):if len(cur) == len(nums):re.append(cur[:])repeat = set()for i in range(0, len(nums)):# 当前索引用过了 或者 当前元素重复了if i in used or nums[i] in repeat:continuerepeat.add(nums[i])# 下一层进发cur.append(nums[i])used.add(i)dfs(cur, used)# 恢复状态cur.pop()used.remove(i)used = set()dfs([], used)return re

加了两个 set 没想到效率就是不一样。

3.组合 [可重复]

组合: https://leetcode.cn/problems/combinations/description/

class Solution(object):def combine(self, n, k):res = []def dfs(cur, position):if len(cur) == k:res.append(cur[:])return for i in range(position, n):cur.append(i + 1)dfs(cur, i + 1)cur.pop()dfs([], 0)return res

[0, 1, 2, 3] 固定 0,再去找 123 组合、再固定 1 去找 23 组合,直到最后结束。 

4.子集 [无重复]

子集: https://leetcode.cn/problems/subsets/description/

class Solution(object):def subsets(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""res = [[]]# 遍历每个数字for i in nums:res = res + [[i] + re for re in res]return res

不断累加 res 的值,最终得到全部的子集结果。当然这个题其实可以用组合来配合,因为全部的子集就是 C(n,i) for i in range(0, n+1) 的结果,我们可以执行合并操作。

换递归的实现操作下:

class Solution(object):def subsets(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""res = []    n = len(nums)def dfs(position, cur):res.append(cur)for i in range(position, n):dfs(i + 1, cur + [nums[i]])dfs(0, [])return res

上图可以方便大家理解本题,相当于 1 和后面的 2、3 组合,1,2 和后面的 3 组合,1 和 3 组合以此类推。

5.子集 [有重复]

子集: https://leetcode.cn/problems/subsets-ii/

class Solution(object):def subsetsWithDup(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""res = []n = len(nums)nums.sort()def dfs(position, cur):res.append(cur)uset = set()for i in range(position, n):if nums[i] in uset:continueuset.add(nums[i])dfs(i + 1, cur + [nums[i]])dfs(0, [])return res

 在前面求子集的基础上增加了 repeat 判重。

五.总结

上面整理了排列组合的相关定义与公式,并在下面实现了几种常见的排列组合算法,这几个算法大家可以熟练掌握,应为很多问题的解决都可以使用这些方法直接简化我们的流程,同时一些路径的问题使用排列组合公式可以直接出答案,非常的方便。

Tips:

上面的代码解析在 分治与回溯 的章节里有详细的注释和分析过程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/591206.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

云原生十二问

一、什么是云原生? 云原生是在云计算环境中构建、部署和管理现代应用程序的软件方法。现代企业希望构建高度可扩展、灵活且具有弹性的应用程序,可以快速更新以满足客户需求。为此,他们使用现代工具和技术,这些工具和技术本质上支…

科普帖:什么是XaaS-一切皆服务模型(包含10个示例类别)

有时似乎有太多的商业首字母缩写词要记住。随着快速变化的技术进步和云计算的出现,新的类别将不断涌现。XaaS 就是这样一个最新的补充。 该领域的大多数读者都知道SaaS(软件即服务)、IaaS(基础设施即服务)和PaaS&…

【CISSP学习笔记】5. 安全架构和工程

该知识领域涉及如下考点,具体内容分布于如下各个子章节: 使用安全设计原理来研究、实施与管理工程过程理解安全模型的基本概念(例如 Biba、Star Model、Bell-LaPadula 等模型)基于系统安全要求选择控制措施理解信息系统 (IS) 的安…

强大的隐藏应用 Hides 5中文 for mac

Hides 5是一款Mac上的应用程序,旨在帮助用户隐藏其他应用程序并专注于当前任务,从而提高工作效率。其主要功能包括对焦模式、隐藏所有打开的应用程序、隐藏除当前活动应用之外的所有打开的应用程序、支持全局热键、可定制性、支持多种显示方式等。 Hide…

【map】【滑动窗口】【优先队列】LeetCode480滑动窗口中位数

作者推荐 动态规划 多源路径 字典树 LeetCode2977:转换字符串的最小成本 本文涉及的基础知识点 C算法:滑动窗口总结 map 优先队列 题目 中位数是有序序列最中间的那个数。如果序列的长度是偶数,则没有最中间的数;此时中位数是最中间的两…

注意力机制(attention mechanism)

1、注意力 灵长类动物的视觉系统接收了大量的感官输入,这些感官输入远远超出了大脑能够完全处理的能力。然而,并非所有刺激的影响都是同等的。意识的汇聚和专注使灵长类动物能够在复杂的视觉环境中将注意力引向感兴趣的物体,例如猎物和天敌。…

【XR806开发板试用】FreeRTOS创建任务测试

这篇来学习下,XR806开发板在FreeRTOS系统下创建两个任务测试,由于没有找到学习的文档,试着参考例程来测试。 一、复制工程 上篇测试了hello_demo的测试例程,直接复制这个工程文件,在此基础上修改 rootubuntu:/home/…

HarmonyOS官网案例解析——保存应用数据

介绍 本篇Codelab将介绍如何使用基础组件Slider,通过拖动滑块调节应用内字体大小。要求完成以下功能: 实现两个页面的UX:主页面和字体大小调节页面。拖动滑块改变字体大小系数,列表页和调节页面字体大小同步变化。往右拖动滑块字体…

Linux安装consul的两种方式(在线和离线)

目录 📚第一章 前言📗背景📗软件概述 📚第二章 部署📗在线部署📕需要root权限📕执行安装命令📕启动consul服务📕验证consul服务 📗离线部署📕下载…

(九)上市企业实施IPD成功案例分享之——欧普

LED通用照明应用是LED照明应用市场的第一驱动力,由于LED照明技术发展迅速,以及成本快速下降,已成为全球主流照明光源。近年来,通过将智能控制模块嵌入LED通用照明终端,形成了具有自动控制、系统化控制等功能的智能照明…

听GPT 讲Rust源代码--library/panic_unwind

File: rust/library/panic_unwind/src/seh.rs 在Rust源代码中,rust/library/panic_unwind/src/seh.rs这个文件的作用是实现Windows操作系统上的SEH(Structured Exception Handling)异常处理机制。 SEH是Windows上的一种异常处理机制&#xff…

EDI 项目推进流程

EDI 需求确认 交易伙伴发来EDI对接邀请,企业应该如何应对? 首先需要确认EDI需求,通常包括传输协议和报文标准以及传输的业务单据类型。可以向交易伙伴发送以下内容: (中文版) 与贵司建立EDI连接需要使用…

【没有哪个港口是永远的停留~论文解读】stable diffusion

了解整个流程: 【第一部分】输入图像 x (W*H*3的RGB图像)【第一部分】x 经过编码器 生成 (latent 空间的表示) h*w*c (具体设置多少有实验)【第二部分】 逐步加噪得到 ,和噪声标签【第二部分】由 Unet( &#xff…

Kubernetes(k8s):Namespace详解

Kubernetes(k8s):Namespace详解 一、Namespace简介1.1 什么是Namespace1.2 Namespace的作用1.3 命名空间的分类 二、创建和管理Namespace2.1 创建Namespace2.2 管理Namespace 三、Namespace的实战应用3.1 部署多个项目3.2 环境隔离3.3 资源配…

从零开始配置kali2023环境:配置jupyter的多内核环境

在kali2023上面尝试用anaconda3,anaconda2安装实现配置jupyter的多内核环境时出现各种问题,现在可以通过镜像方式解决 1. 搜索镜像 ┌──(holyeyes㉿kali2023)-[~] └─$ sudo docker search anaconda ┌──(holyeyes㉿kali2023)-[~] └─$ sudo …

C++初阶——权限与继承

目录 一、C权限方面的问题 1.访问权限 2.继承机制 二、Cconst引用 const引用有以下几个特点 临时对象引用 常量引用成员变量 二、c引用空间相关问题 三.auto 一、C权限方面的问题 【C入门】访问权限管控和继承机制详解_权限继承功能-CSDN博客文章浏览阅读840次。(2)但…

YOLOv8训练自己的数据集(超详细)

一、准备深度学习环境 本人的笔记本电脑系统是:Windows10 YOLO系列最新版本的YOLOv8已经发布了,详细介绍可以参考我前面写的博客,目前ultralytics已经发布了部分代码以及说明,可以在github上下载YOLOv8代码,代码文件夹…

LVM和磁盘配额

一:LVM概述: LVM 是 Logical Volume Manager 的简称,译为中文就是逻辑卷管理。 能够在保持现有数据不变的情况下,动态调整磁盘容量,从而提高磁盘管理的灵活性 /boot 分区用于存放引导文件,不能基于LVM创建…

MongoDB vs MySQL:项目选择哪一个数据库系统?

由于市场上有各种可用的数据库,用户经常会就MongoDB与MySQL进行辩论,以找出更好的选择。 使用MySQL等关系数据库的组织在根据不断变化的需求管理和存储数据时可能会面临一定的困难。同时,新公司想知道选择什么数据库,这样他们就不…

【ArcGIS微课1000例】0085:甘肃省白银市平川区4.9级地震震中位置图件制作

据中国地震台网正式测定,12月31日22时27分在甘肃白银市平川区发生4.9级地震,震源深度10公里,震中位于北纬36.74度,东经105.00度。 文章目录 一、白银市行政区划图1. 县级行政区2. 乡镇行政区二、4.9级地震图件制作1. 震中位置2. 影像图3. 震中三维地形一、白银市行政区划图…