diff --git a/Algorithm/A类:基本算法/10 近似算法.md b/Algorithm/A类:基本算法/10 近似算法.md new file mode 100644 index 00000000..3193abbf --- /dev/null +++ b/Algorithm/A类:基本算法/10 近似算法.md @@ -0,0 +1,19 @@ +# 近似算法 + +## 1 近似算法 + +### 概念 +不同的近似算法有各种各样的复杂度,但其中许多算法都是基于特定问题的直观推断贪婪算法。直观推断是一种来自于经验而不是来自于数学证明的常识性规则。如果我们使用的算法所给出的输出仅仅是实际最优解的一个逼近,我们就会想知道这个逼近有多精确。 + + +### 近似算法精度 + +对于一个对某些函数 f 最小化的问题来说,可以用近似解的相对误差规模 +$$ +re(S_a)=\frac{f(S_*)}{f(S^a)} +$$ + +### 近似算法的性能 + +对于问题的所有实例,它们可能的r(sa)的最佳(也就是最低)上界,被称为该算法的性能比,计作RA。 +性能比是一个来指出近似算法质量的主要指标,我们需要那些RA尽量接近1的近似算法。 diff --git a/Algorithm/A类:基本算法/10.1 遗传算法.md b/Algorithm/A类:基本算法/10.1 遗传算法.md new file mode 100644 index 00000000..aaa3d9c3 --- /dev/null +++ b/Algorithm/A类:基本算法/10.1 遗传算法.md @@ -0,0 +1,23 @@ +# 遗传算法 + +## 1 遗传算法概述 + +### 步骤 + +1. 随机产生一组初始个体构成初始种群,并评价每一个体的适配值(fitness value)。 +2. 判断算法收敛准则是否满足。若满足则输出搜索结果;否则执行以下步骤。 +3. 根据适配值大小以一定方式执行复制操作。 +4. 按交叉概率pc执行交叉操作。 +5. 按变异概率pm执行变异操作。 +6. 返回步骤2。 + +### 遗传算法的特点 + +遗传算法利用生物遗传和进化的思想实现优化 + +* 对问题参数编码成“染色体”后进行进化操作,而不是针对参数,这使得它不受某些函数约束条件的限制,如连续性、可导性等; + +* 搜索过程是从问题解的一个集合开始的,而不是从单个个体开始的,具有隐含并行搜索特性,从而大大减小了陷入局部极小的可能; +* 具有全局搜索能力; + +* 适应性、收敛性。 diff --git a/Algorithm/A类:基本算法/10.2 邻域搜索算法.md b/Algorithm/A类:基本算法/10.2 邻域搜索算法.md new file mode 100644 index 00000000..c64d5f1b --- /dev/null +++ b/Algorithm/A类:基本算法/10.2 邻域搜索算法.md @@ -0,0 +1,9 @@ +# 邻域结构优化算法 + +## 1 算法概述 + +### 算法原理 + +利用邻域结构进行逐步优化的局部搜索算法: + +算法从一初始可行解 s 出发,利用状态发生器持续地在s 的领域中搜索更好的解,若能找到更优解,则以其替代s 成为新的当前解,然后重复上述过程,直至终止条件满足。 diff --git a/Algorithm/A类:基本算法/10.3 禁忌搜索算法.md b/Algorithm/A类:基本算法/10.3 禁忌搜索算法.md new file mode 100644 index 00000000..138101b6 --- /dev/null +++ b/Algorithm/A类:基本算法/10.3 禁忌搜索算法.md @@ -0,0 +1,58 @@ +# 禁忌搜索算法 + + +## 1 算法说明 + +### 算法概述 + +禁忌搜索(TS)是对局部邻域搜索的一种扩展,是一种全局优化算法。TS算法通过引入一个禁忌表和相应的禁忌准则来避免局部迂回,并通过“渴望准则”来挽救某些被禁忌的相对优化解,进而保证全局的有效搜索以实现全局优化。 + +标记对应已搜索到的局部最优解的一些对象,并在进一步的迭代搜索中尽量避开这些对象,但不是绝对禁止循环,从而保证对不同的有效搜索途径的探索。 + +### 基本思想 + +* 给定一个当前解(初始解)和一种邻域结构,在当前解的邻域中确定若干候选解; +* 若最佳候选解对应的目标植优于 “best so far” ,则忽视其禁忌特性,用其替代当前解和“best so far”值,并将相应的对象加入禁忌表,同时修改禁忌表中各对象的禁忌任期; +* 若不存在上述候选解,则选择在候选解中非禁忌的最佳状态为新的当前解,而无视它与当前解的优劣,同时将相应的对象加入禁忌表,并修改禁忌表中各对象的任期; +* 重复上述迭代搜索过程,直至满足停止条件。 + + +### 算法原理 + +邻域 + +对于组合优化问题,给定任意可行解x,x∈D,D是决策变量的定义域,对于D上的一个映射:N:x∈D→N(x)∈2(D) 其中2(D)表示D的所有子集组成的集合,N(x)成为x的一个邻域,y∈N(x)称为x的一个邻居。 + +候选集合 + +候选集合一般由邻域中的邻居组成,可以将某解的所有邻居作为候选集合,也可以通过最优提取,也可以随机提取,例如某一问题的初始解是[1,2,3],若通过两两交换法则生成候选集合,则可以是[1,3,2],[2,1,3],[3,2,1]中的一个或几个。 + +禁忌表 + +禁忌表包括禁忌对象和禁忌长度。由于在每次对当前解的搜索中,需要避免一些重复的步骤,因此将某些元素放入禁忌表中,这些元素在下次搜索时将不会被考虑,这些被禁止搜索的元素就是禁忌对象; +禁忌长度则是禁忌表所能接受的最多禁忌对象的数量,若设置的太多则可能会造成耗时较长或者算法停止,若太少则会造成重复搜索。 + +评价函数 + +用来评价当前解的好坏,TSP问题中是总旅程距离。 + +特赦规则 + +禁忌搜索算法中,迭代的某一步会出现候选集的某一个元素被禁止搜索,但是若解禁该元素,则会使评价函数有所改善,因此我们需要设置一个特赦规则,当满足该条件时该元素从禁忌表中跳出。 + +终止规则 + +一般当两次迭代得到的局部最优解不再变化,或者两次最优解的评价函数差别不大,或者迭代n次之后停止迭代,通常选择第三种方法。 + +### 算法流程 + +![](image/禁忌搜索算法.png) + +1. 定义相邻结构和如下算法参数: tt(Tabu tenure)、m (number of candidates);m > tt ; +2. 以某种方法产生初始解s,置禁忌表为空; +3. 从s的邻域中选取m 个 候选解; +4. 选取候选解中的最优解X; +5. 若X不在禁忌表中,则s=x, 将X存入禁忌表且置其当前禁忌值为tt, 将禁忌表中其它元素值(禁忌值)减 1,然后将禁忌值为0的禁忌元素释放出禁忌表,转到(8); +6. 若X在禁忌表中,且X的目标函数值优于当前最优解(best so far),则s=x, 将X禁忌值置为tt, 将禁忌表中其它元素值(禁忌值)减 1,然后将禁忌值为0的禁忌元素释放出禁忌表,转到(8); +7. 若X在禁忌表中,且X的目标函数值不优于当前最优解(best so far),则从候选解中取得下一X,转到(4); +8. 判断算法终止条件是否满足?若是,则结束算法并输出优化结果;否则转到(3)。 diff --git a/Algorithm/A类:基本算法/10.4 模拟退火算法.md b/Algorithm/A类:基本算法/10.4 模拟退火算法.md new file mode 100644 index 00000000..e69de29b diff --git a/Algorithm/A类:基本算法/10.5 蚁群算法.md b/Algorithm/A类:基本算法/10.5 蚁群算法.md new file mode 100644 index 00000000..e69de29b diff --git a/Algorithm/A类:基本算法/3.1 查找算法.md b/Algorithm/A类:基本算法/3.1 查找算法.md index 19a1e9fb..fdfe684c 100644 --- a/Algorithm/A类:基本算法/3.1 查找算法.md +++ b/Algorithm/A类:基本算法/3.1 查找算法.md @@ -128,7 +128,8 @@ $$ > 注:对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择。 ### 复杂度分析 -查找成功或者失败的时间复杂度均为O(log2(log2n))。 +查找成功或者失败的时间复杂度均为$O(log_2(log_2n))$。 +最差时间复杂度O(n) ### 代码实现 ``` diff --git a/Algorithm/A类:基本算法/3.6 线性时间选择算法.md b/Algorithm/A类:基本算法/3.6 线性时间选择算法.md index 9e5f7ad0..2edb78cc 100644 --- a/Algorithm/A类:基本算法/3.6 线性时间选择算法.md +++ b/Algorithm/A类:基本算法/3.6 线性时间选择算法.md @@ -219,4 +219,47 @@ RANDOMIZED-SELECT 的最坏运行时间为 Θ(n2),即使是要选择最小元 106 107 static Random random = new Random(new Guid().GetHashCode()); 108 } -``` \ No newline at end of file +``` + +## 3 分治法选择最大最小值 + +### 问题描述 +在一个整数组 A[1…n]中,同时寻找最大值和最小值。 + +### 算法原理 + +* 非递归算法 + +``` + 1. x ← A[1]; y ← A[1] + 2. for i ← 2 to n + 3. if A[i ] < x then x ← A[i ] + 4. if A[i ] > y then y ← A[i ] + 5. end for + 6. return (x, y) +``` + +* 递归算法 + + 1. 将数组分割成两半:A [1… n/2] 和A [(n/2) +1… n]。(设 n 为2的幂 ) + 2. 在每一半中找到最大值和最小值,并返回这两个最小值中的最小值及这两个最大值中的最大值。 +``` +Procedure minmax (low, high) + if high – low = 1 then + if A [low] < A [high] then return (A [low], A[high]) + else return ( A[high], A [low]) + end if + else + mid ← + (x1, y1) ← minmax (low, mid) + (x2, y2) ← minmax (mid + 1, high) + x ← min {x1, x2} + y ← max {y1, y2} + return (x, y) + end if +``` + +### 算法效率 + +O(n) +> 书上给的分治法是O(log n)我觉得不对 \ No newline at end of file diff --git a/Algorithm/A类:基本算法/4 递归与分治法.md b/Algorithm/A类:基本算法/4 递归与分治法.md index 7a73bbfa..cec85bda 100644 --- a/Algorithm/A类:基本算法/4 递归与分治法.md +++ b/Algorithm/A类:基本算法/4 递归与分治法.md @@ -4,7 +4,7 @@ ### 基本思想 * 求解问题算法的复杂性一般都与问题规模相关,问题规模越小越容易处理。 -* 分治法的基本思想是,将一个难以直接解决的大问题,分解为规模较小的相同子问题,直至这些子问题容易直接求解,并且可以利用这些子问题的解求出原问题的解。各个击破,分而治之。 +* 分治法的基本思想是,将一个难以直接解决的大问题,分解为**规模较小**的**相同类型**的子问题,直至这些子问题容易直接求解,并且可以利用这些子问题的解求出原问题的解。各个击破,分而治之。 * 分治法产生的子问题一般是原问题的较小模式,这就为使用递归技术提供了方便。递归是分治法中最常用的技术。 ![](image/分治法原理.png) @@ -77,28 +77,182 @@ $$ T(n)=n^{\log_ba} +\sum_{i=2}^{\log_bn-1}a^jf(n/b^j) $$ * 关系式说明 -![](image/递归算法-等比收缩说明.png) -![](image/递归算法-时间复杂度.png) +$$ +T(n)=O(n^{\log_ba})+O(f(n))\log_bn +$$ +其真正的时间复杂度,由前后两部分决定。可以通过计算,得到较大部分的时间复杂度,为整体的时间复杂度。 +* 当f(n)为常数时 +$$ +T(n)=\begin{cases} + O(n^{\log_ba}) & a\not = 1\\ + O(\log n) & a=1 +\end{cases} +$$ + +* 当f(n)=cn时 + +$$ +T(n)=\begin{cases} + O(n)&ab +\end{cases} +$$ +### 递归算法理解 + +* 递归算法本质上是一种自顶向下的思考模式。即数学上所说的归纳法。有结果一步一步归纳,得到验证其条件的正确性。问题规模逐渐变小。 +* 非递归算法的本质是一种自底向上的思考模式。即数学上所说的推导法。将一些零碎的部分逐渐求解,渐渐得到最终的结果。问题逐渐组装成目标问题。 + +在使用递归的时候,应该从顶层考虑怎么分割。在使用非递归算法的时候,应该考虑怎样从顶层进行组合。 + +## 0 减治法概述 + +### 基本思想 + +一个问题给定实例的解和同样问题较小实例的解之间的关系。一旦建立了这样一种关系,我们既可以递归地,也可以非递归地地来运用减治技术。 + +### 分类 + +* 减去一个常量 (decrease by a constant) +* 减去一个常数因子(decrease by a constant factor) +* 减去的规模是可变的(variable size decrease) + +### 算法原理 +* 在减常量变种中,每次算法迭代总是从实例规模中减去一个规模相同的常量。经常地,这个常量等于一。函数f(n) = an可以用一递归定义来计算 +``` +f(n) = f (n-1) *a 如果n > 1 + = a 如果n = 1 +``` + + +* 减常因子技术意味着在算法的每次迭代中,总是从实例的规模中减去一个相同的常数因子。在多数应用中,这样的常数因子等于二。例如:计算an的值是规模为n的实例:an = (an/2)2。O(log n); + +* 在减治法的减可变规模变种中,算法在每次迭代时,规模减小的模式都是不同的。例如:欧几里得算法:gcd (m,n) = gcd (n, m mod n) + +### 算法效率 + +与蛮力法相同。但是思想不同。 + + +### 与分治法对比 + +该算法和基于分治思想的算法有所不同: +* 分治法分解成规模相似的类型相同的子问题。 +* 减治法直接通过运算将问题的规模减小。问题数量没有增加。 + + +## 0 变治法概述 + +### 基本思想 + +1. 变换为同样问题的一个更简单或者更方便 的实例—实例化简(Instance simplification)。 +2. 变换为同样实例的不同表现—改变表现(Representation Change). +3. 变换为另一个问题的实例, 这种问题的算法是已知的—问题化简(Problem reduction). + +> 根本上是转换问题的思路。 ## 1 分治法应用 ### 排列问题 + + ### 整数划分问题 -### 二分搜索问题 +### 二分搜索问题√ +O(\log n) + +* 迭代空间O(1) +* 递归空间O(\log n) +### 大数乘法√ +O(n^(log 3)) +### 矩阵乘法√ + +### 快速排序√ + +### 合并排序√ +O(n\log n) +### 线性时间选择√ +* 选择最大最小元素-分治或暴力 +O(n) +* 选择第k小元素-快排的一半 +O(n) +### 最近点对问题√ + +### 棋盘覆盖问题 + +## 2 减治法应用 + +### 拓扑排序 + +### 生成子集 +* 比特串方法 +### 假币问题 + +### 俄式乘法 + +### 约瑟夫问题 + +### gcd欧几里得算法 + +### 插值查找 + +### 二叉树查找 + + +## 3 变治法应用 +### 元素唯一性-预排序算法 + +### 模式计算-预排序算法 +* 问题描述 + + +在给定的数字列表中最经常出现的一个数值称为模式(Mode)。 (例:5,1,5,7,6,5,7,模式是5) + +* 暴力法O(n^2) +* 预排序O(n\log n) + + +### 线性方程组-高斯小区算法 + +* 问题描述 +n个线性方程构成的n 元联立方程组 +* 高斯消去O(n^3) + +### 霍纳法则 + +* 问题描述 + +针对一个给定的x 的多项式p(x) = anxn + an-1xn-1 + … + a1x + a0 求值 + +* 思想 + +它不断地把x作为公因子从降次以后的剩余多项式中提取出来:(x) =(…(anx + an-1)x + ..)x + a0 + +* 例子 + +``` +p(x) = 2x4 - x3 + 3x2 + x - 5 + = x(2x3 - x2 + 3x + 1 )- 5 + = x(x(2 x2 - x +3)+1)-5 + = x(x(x(2 x-1)+3)+1)-5 + +``` + +### lcm 最小公倍数 + +### AVL树 + +### 2-3树 + +### 堆排序 + +### 二进制幂 -### 大数乘法 -### 矩阵乘法 -### 快速排序 -### 合并排序 -### 线性时间选择 -### 最近点对问题 -### 棋盘覆盖问题 \ No newline at end of file diff --git a/Algorithm/A类:基本算法/4.1 排列问题.md b/Algorithm/A类:基本算法/4.1 排列问题.md index ef61d406..ec43d7be 100644 --- a/Algorithm/A类:基本算法/4.1 排列问题.md +++ b/Algorithm/A类:基本算法/4.1 排列问题.md @@ -41,4 +41,32 @@ T(n)=\begin{cases} O(1)& n=1\\ nT(n-1)+O(1)& n>1 \end{cases} -$$ \ No newline at end of file +$$ + +## 2 排列问题-Johnson-Trotter Algorithm + +### 算法原理 +给一个排列中的每个分量k 赋予一个方向。例如: +$$ +\overrightarrow{3}\overleftarrow{2}\overrightarrow{4}\overleftarrow{1} +$$ + +如果分量k 的箭头指向一个相邻的较小元素,我们说它在这个以箭头标记的排列中是可移动的。3和4是可移动的。 + +### 算法过程 + +``` +JohnsonTrotter(n) +//输入:一个正整数n +//输出:{1,…,n}的所有排列的列表 + 将第一个排列初始化为 + While 存在可移动整数 do + 求最大的可移动整数k + 把k 和它箭头指向的相邻整数互换 + 调转所有大于k 的整数的方向 + +``` + + + + \ No newline at end of file diff --git a/Algorithm/A类:基本算法/4.2 大整数乘法.md b/Algorithm/A类:基本算法/4.2 大整数乘法.md new file mode 100644 index 00000000..b6902099 --- /dev/null +++ b/Algorithm/A类:基本算法/4.2 大整数乘法.md @@ -0,0 +1,38 @@ +# 大整数乘法 + +## 1 大整数乘法-分治法 + +### 问题描述 + +设u 和v 是两个n 位的整数(二进制),传统的乘法算法需要(n2) 数字相乘来计算u 和v 的乘积。 + +### 算法原理 + +* 把每个整数分为两部分,每部分为n/2位,则u 和v 可重写为u = w2n/2 + x 和v = y2n/2 +z + +* u 和v 的乘积可以计算为: +``` + uv = (w2n/2 + x)(y2n/2 +z) = wy2n + (wz + xy) 2n/2 + xz +``` +用2^n做乘法运算相当于简单地左移n 位,它需要θ( n) 时间。 + +* 考虑用以下恒等式计算wz + xy。 +``` +wz + xy = (w + x) ( y + z) – wy – xz +``` +* 由于wy 和 xz 不需要做二次计算,结合以上二式,仅需3次乘法运算,即 +``` +uv = wy2n + (( w + x) ( y + z) – wy –xz) 2n/2 + xz +``` + + +### 算法效率 +* 此方法产生以下递推式 +``` +T (n) = d 若n = 1 + = 3T(n/2) +bn 若 n>1 +``` +* 定理得出 +$$ +T(n) =Θ(n^log 3) = O(n^1.59) +$$ \ No newline at end of file diff --git a/Algorithm/A类:基本算法/4.3 矩阵乘法.md b/Algorithm/A类:基本算法/4.3 矩阵乘法.md new file mode 100644 index 00000000..8e727a81 --- /dev/null +++ b/Algorithm/A类:基本算法/4.3 矩阵乘法.md @@ -0,0 +1,44 @@ +# 矩阵乘法 + +## 1 矩阵乘法-STRASSEN算法 + +### 问题描述 + + +### 算法原理 + +算法的基本思想在于以增加加减法的次数来减少乘法次数: +用了7次n/2 x n/2 矩阵乘法和18次n/2 x n/2 矩阵的加法 + + +### 算法过程 +* 将矩阵分块 +``` +A = a11 a12 + a21 a22 +B = b11 b12 + b21 b22 +``` +* 计算分块矩阵的乘法 +``` +d1 = (a11 + a22) (b11 + b22) +d2 = (a21 + a22) b11 +d3 = a11 (b12 – b22) +d4 = a22 (b21 –b11) +d5 = (a11 + a12) b22 +d6 = (a21 – a11) (b11 + b12) +d7 = (a12 – a22) ( b21 + b22) +``` + +* 将分块矩阵乘法进行合并 + + +### 算法效率 +``` +T (n) = m 若n=1 + =7T (n/2) + 18(n/2)2 a 若n≥ 2 +``` +``` +T (n) = mnlog7+6anlog7-6an2 +即运行时间为Θ (nlog7)= O(n2.81) +``` diff --git a/Algorithm/A类:基本算法/4.4 拓扑排序.md b/Algorithm/A类:基本算法/4.4 拓扑排序.md new file mode 100644 index 00000000..601cb488 --- /dev/null +++ b/Algorithm/A类:基本算法/4.4 拓扑排序.md @@ -0,0 +1,30 @@ + + +## 拓扑排序-减治法 +### 问题描述 + +考虑五门必修课的一个集合{C1, C2, C3, C4, C5}, +一个在职的学生必须在某个阶段修完这几门课程。 +可以按照任何次序学习这些课程,只要满足下面这些条件: +C1和C2没有任何先决条件; +修完C1 和C2才能修C3; +修完C3 才能修C4; +修完C3、C4才能修C5; +这个学生每个学期只能修一门课程。 + +若用一个图来建模,它的顶点代表课程,有向边表示先决条件,该问题为:是否可以按照这种次序列出它的顶点,使得对于图中每一条边来说,边的起始顶点总是排在边的结束顶点之前。 + +这个问题称为拓扑排序。 + +如果有向图具有一个有向的回路,该问题是无解的。因此,为了使得拓扑排序成为可能,问题中的图必须是一个无环有向图。 + + +### 减治法原理 + +基于减(减一)治技术的一个直接实现:重复以下过程: + +在余下的有向图中求出一个源,它是一个没有输入边的顶点; +然后把该源和所有从它出发的边都删除。(如果有多个这样的源,可以任意选择一个;如果这样的源不存在,算法停止,因为该问题是无解的) +顶点被删除的次序就是拓扑排序的一个解。 + +### 拓扑排序-深度搜索 diff --git a/Algorithm/A类:基本算法/4.5 约瑟夫问题.md b/Algorithm/A类:基本算法/4.5 约瑟夫问题.md new file mode 100644 index 00000000..e69de29b diff --git a/Algorithm/A类:基本算法/4.6 元素唯一性.md b/Algorithm/A类:基本算法/4.6 元素唯一性.md new file mode 100644 index 00000000..2cf30481 --- /dev/null +++ b/Algorithm/A类:基本算法/4.6 元素唯一性.md @@ -0,0 +1,44 @@ +# 元素唯一性 + +## 1 元素唯一性-暴力法 +### 问题描述 + +检验数组中元素的惟一性 + +### 算法原理 + +暴力法 +``` +UniqueElements(A[1,…n]) +//输出:如果A之元素全部惟一,返回“true”, 否则返回 “false” + +for i←1 to n-1 do + for j← i+1 to n do + if A[i]=A[j] return false +return true +``` + +### 算法效率 + +n(n-1)/2 + + +## 2 元素唯一性-预排序 + +### 算法原理 + +``` +PresortElementUniqueness(A[1..n]) +//先对数组排序来解元素惟一性问题 +//输入:n个可排序元素构成的一个数组A[1..n] +//输出:如果A没有相等的元素,返回“true”, 否则返回”false” + +对数组A排序 +for i← 1 to n-1 do + if A[i] = A[i+1] return false +return true +``` + +### 算法效率 + +T(n)=Tsort(n) + Tscan(n) ∈Θ (nlogn) +Θ (n) = Θ (nlogn) diff --git a/Algorithm/A类:基本算法/5 动态规划.md b/Algorithm/A类:基本算法/5 动态规划.md index 75203a0a..6ade6137 100644 --- a/Algorithm/A类:基本算法/5 动态规划.md +++ b/Algorithm/A类:基本算法/5 动态规划.md @@ -19,7 +19,10 @@ ### 基本步骤 1. 找出最优解的性质,刻画其结构特征。 -2. 递推地定义最优值 +2. 递推地定义最优值。需要给出状态转移变量 +$$ +f(n)=f(n-1)+f(n-2) +$$ 3. 以自底向上的方式计算出最优解。 4. 根据计算最优值时的到的信息,构造最有解。 @@ -28,8 +31,36 @@ * 重叠子问题 * 备忘录方法(矩阵表格) +### 基本概念 + +* 阶段;明确问题的各个阶段:将问题划分为相互联系的各个阶段。 +* 状态:明确问题的各个状态,表达每个阶段的状态,定义状态变量。描述过程状态的变量,称为状态变量。常用xk表示在第k段的某一状态。 +* 决策:明确状态从一个阶段到另一个阶段的变化。实现状态转移方程。 +* 最优指标函数:用来判断决策过程中的优劣问题,淘汰不好的解,留下最优子结构。决策就是某阶段状态给定以后,从该状态演变到下一阶段某状态的选择。描述决策的变量,称为决策变量。 + +### 可逆过程 +* 顺序解法:以G为始端,以A为终端的左行解法程序称为顺序解法 +* 逆序解法可以把段次颠倒过来求最优解的多阶段决策过程称为可逆过程。 + +* 顺序解法和逆序解法只表示行进方向的不同或始端的颠倒。但用动态规划方法求最优解时,都是在行进方向规定后,均要逆着这个规定的行进方向,从最后一段向前逆推计算,逐段找出最优途径 + +### 构建动态规划模型 + +1. 正确选择状态变量xk, 使它既能描述过程的状态,又要满足无后效性。动态规划中的状态与一般所说的状态概念是不同的,它必须具有三个特性: + * 要能够用来描述受控过程的演变特征。 + * 要满足无后效性。 所谓无后效性是指:如果某段状态给定,则在这段以后过程的发展不受前面各阶段状态的影响。 + * 可知性。即是规定的各段状态变量的值,由直接或间接都是可以知道的。 +2. 确定决策变量uk及每段的允许决策集合Dk(xk)={uk} +3. 写出状态转移方程如果给定第k段状态变量xk的值,则该段的决策变量uk一经确定,第k+1段状态变量xk+1的值也就完全确定。 +4. 列出指标函数Vk,n 关系,并要满足递推性。 + + + ## 1 常见问题 +### 最长公共子序列 + + ### 矩阵连乘问题 ### 凸多边形最优三角剖分 @@ -50,4 +81,21 @@ ### 最优二叉搜索树问题 -### 序列匹配问题 \ No newline at end of file +### 序列匹配问题 + +---- + + +### 最短路线问题 + +### 机器负荷问题 + +### 资源分配问题 + +### 可靠性问题 + +### 排序问题 + +### 设备更新问题 + +### 计算二项式系数 diff --git a/Algorithm/A类:基本算法/6 贪心算法.md b/Algorithm/A类:基本算法/6 贪心算法.md index 8b4a3f27..ae66f9f7 100644 --- a/Algorithm/A类:基本算法/6 贪心算法.md +++ b/Algorithm/A类:基本算法/6 贪心算法.md @@ -23,4 +23,20 @@ 方法 * 算法步数的归纳 -* 问题规模的归纳 \ No newline at end of file +* 问题规模的归纳 + +### 贪心的核心思想-特点 + +* 可行的:即它必须满足问题的约束。 +* 局部最优:它是当前步骤中所有可行性选择中最佳的局部选择。 +* 不可取消:即选择一旦做出,在算法的后面步骤中就无法改变了。 + + + +## 2 常见问题 +### 分数背包问题 +### 最短路径问题 +### Dijkstra’s Algorithm +### Prim’s +### Kruskal’s +### Huffman 算法与决策树 diff --git a/Algorithm/A类:基本算法/6.1 Huffman算法.md b/Algorithm/A类:基本算法/6.1 Huffman算法.md new file mode 100644 index 00000000..90a739ab --- /dev/null +++ b/Algorithm/A类:基本算法/6.1 Huffman算法.md @@ -0,0 +1,60 @@ +# Huffman算法 + +## Huffman算法 + +### 问题描述 +一个字符串文件,我们希望尽可能多地压缩文件,但源文件能够很容易地被重建。 + +用特定的比特串表示每个字符,称为字符的编码,文件的大小取决于文件中的字符数n。我们可以使用一种定长编码,对每个字符赋予一个长度同为m (m≥log2n)的比特串。 + +设文件中的字符集是C={c1,c2,…, cn}, 又设f(ci), 1≤i≤n,是文件中字符ci的频度,即文件中ci出现的次数。 + +由于有些字符的频度可能远大于另外一些字符的频度,所以用变长的编码。 + +去除编码的二义性: + +当编码在长度上变化时,我们规定一个字符的编码不能是另一个字符编码的前缀(即词头),这种码称为前缀码。 + +例如,如果我们把编码10和101赋予字符“a” 和“b”就会存在二义性,不清楚10究竟是“a” 的编码还是字符“b”的编码的前缀。 + +一旦满足前缀约束,编码即不具备二义性,可以扫描比特序列直到找到某个字符的编码。 + + +### 算法原理 + +由Huffman算法构造的编码满足前缀约束,并且最小化压缩文件的大小。 +算法重复下面的过程直到C仅由一个字符组成: +* 设ci和cj是两个有最小频度的字符,建立一个新节点c,它的频度是ci和cj频度的和,使ci和cj为c的子节点, +* 令C = (C-{ci, cj})∪{c}。 + +### 算法过程 + +1. 第一步:初始化n个单节点的树,为它们标上字母表的字符。把每个字符的概率记在树的根中,用来指出树的权重(更一般地来说,树的权重等于树中所有叶子的概率之和)。 + +2. 第二步:重复下面的步骤,直到只剩一棵单独的树: + * 找到两棵权重最小的树, 把它们作为新树中的左右子树,并把其权重之和作为新的权重记录在新的根中。 + +3. 构建树后然后进行编码,从根节点,每个路径上左0右1 +### 算法效率 + +O(n\log n) + +### 算法实现 + +``` +算法HUFFMAN +输入:n个字符的集合C={c1,c2,…, cn}和它们的频度{f(c1), f(c2),…, f(cn)}。 +输出:C的Huffman树(V,T)。 + +根据频度将所有字符插入最小堆H +V←C; T={} +for j←1 to n-1 + c←DELETEMIN(H) + c’←DELETEMIN(H) + f(v) ← f(c)+ f(c’) //新节点v + INSERT (H, v) + V =V∪{v} + T = T∪{(v, c), (v, c’)} // c, c’为T中v的孩子 +end for + +``` \ No newline at end of file diff --git a/Algorithm/A类:基本算法/7 回溯法.md b/Algorithm/A类:基本算法/7 回溯法.md index e2a3bc4d..3db9f78b 100644 --- a/Algorithm/A类:基本算法/7 回溯法.md +++ b/Algorithm/A类:基本算法/7 回溯法.md @@ -109,3 +109,7 @@ void iterativeBacktrack () ### 分支限界与回溯法对比 * 求解目标不同:回溯法的求解目标是找出解空间树中满足约束条件的所有解。而分支限界的求解目标是找出满足约束条件的一个解。这个解可能是最优解。 * 搜索方式不同:回溯法以深度优先的方式搜索解空间。而分支限界法则以广度优先或以最小消耗优先的方式搜索解空间。 + +## 2 常见问题 + +### 着色问题 \ No newline at end of file diff --git a/Algorithm/A类:基本算法/7.3 01背包问题.md b/Algorithm/A类:基本算法/7.3 01背包问题.md index d79791ab..7b2b8b7d 100644 --- a/Algorithm/A类:基本算法/7.3 01背包问题.md +++ b/Algorithm/A类:基本算法/7.3 01背包问题.md @@ -49,4 +49,28 @@ n=3, C=30, w={16, 15, 15}, v={45, 25, 25} ### 算法原理 ![](image/01背包问题-分支限界.png) -### 算法实现 \ No newline at end of file +### 算法实现 + + +## 3 01背包问题-动态规划 + +### 问题描述 + +0/1(同类物品数最大1)背包问题可以定义如下: + 设U={u1,u2,…,un}是一个准备放入容量为C的背包中的n项物品的集合。对于1≤j≤n,令sj和vj分别为第j项物品的体积和价值,C,sj,vj和j 都是正整数。 + 要解决的问题是用U中的一些物品来装 背包,这些物品的总体积不超过C,然而要使它们的总价值最大。 + +### 问题分析 + +导出递归公式, + 设V[i,j] 表示从前i项{u1,u2,…,ui}中取出来的装入体积为j的背包的物品的最大价值。这里,i的范围是从0到n, j的范围是从0到C。 +则,要寻求的是值V[n,C]。 +有V[0,j]对于所有j的值是0,当背包中没有物品。 + V[i,0]对于所有i的值为0,当没有物品可放到容积为0的背包里。 + +### 算法原理 +``` +V[i,j] = 0 若i=0或j=0 + =V[i-1,j] 若j0) (i, j=1, 2,…, n)表示指派第i人去完成第j向任务的效率(或时间,成本等)。解题时,我们引入0-1变量xij,令 xij=1,当指派第i人去完成第j项任务时, + =0,当不指派第i人去完成第j项任务时 + +### 算法原理-匈牙利法 + +指派问题最优解的性质:如果从系数矩阵(cij)的一行(列)各元素分别减去该行(列)的最小元素,得到新矩阵(bij) 。那么,以(bij)为系数矩阵的指派问题的最优解(xij)和原问题的最优解相同。 diff --git a/Algorithm/A类:基本算法/9 随机化.md b/Algorithm/A类:基本算法/9 随机化.md index 12be51e7..9d97249e 100644 --- a/Algorithm/A类:基本算法/9 随机化.md +++ b/Algorithm/A类:基本算法/9 随机化.md @@ -1,5 +1,21 @@ # 随机化算法 + +### 随机算法的概述 + +将算法必须对所有可能的输入都正确地求解问题的条件放宽,只要求它的可能不正确性解能够相对安全地忽略掉,比如说它的出现可能性非常低; +而且也不要求对于特定的输入,算法的每一次运行的输出都必须相同。 + +随机算法可以做如下定义: + 它是在接收输入的同时,为了随机选择的目的,还接收一串随机比特流并且在运行过程中使用该比特流的算法。 + +一个随机算法在不同的运行中对于相同的输入可以有不同的结果。由此得出对于相同的输入两次不同的随机算法的执行时间可能不同。 + +### 随机算法的优点 + +* 首先,较之那些我们所知的解决同一问题最好的确定性算法,随机算法所需的运行时间或空间通常常小一些; +* 其次,观察迄今为止已经发明的各种随机算法,我们发现这些算法总是易于理解和实现。 + ## 1 伪随机数 ### 伪随机数的产生 diff --git a/Algorithm/A类:基本算法/NP问题.md b/Algorithm/A类:基本算法/NP问题.md index 3c144364..aff1f3e5 100644 --- a/Algorithm/A类:基本算法/NP问题.md +++ b/Algorithm/A类:基本算法/NP问题.md @@ -1,11 +1,26 @@ -时间复杂度 -时间复杂度并不是表示一个程序解决问题需要花多少时间,而是当问题规模扩大后,程序需要的时间长度增长得有多快。也就是说,对于高速处理数据的计算机来说,处理某一个特定数据的效率不能衡量一个程序的好坏,而应该看当这个数据的规模变大到数百倍后,程序运行时间是否还是一样,或者也跟着慢了数百倍,或者变慢了数万倍。不管数据有多大,程序处理花的时间始终是那么多的,我们就说这个程序很好,具有O(1)的时间复杂度,也称常数级复杂度;数据规模变得有多大,花的时间也跟着变得有多长,这个程序的时间复杂度就是O(n),比如找n个数中的最大值;而像冒泡排序、插入排序等,数据扩大2倍,时间变慢4倍的,属于O(n^2)的复杂度。还有一些穷举类的算法,所需时间长度成几何阶数上涨,这就是O(a^n)的指数级复杂度,甚至O(n!)的阶乘级复杂度。不会存在O(2*n^2)的复杂度,因为前面的那个“2”是系数,根本不会影响到整个程序的时间增长。同样地,O (n^3+n^2)的复杂度也就是O(n^3)的复杂度。因此,我们会说,一个O(0.01*n^3)的程序的效率比O(100*n^2)的效率低,尽管在n很小的时候,前者优于后者,但后者时间随数据规模增长得慢,最终O(n^3)的复杂度将远远超过O(n^2)。我们也说,O(n^100)的复杂度小于O(1.01^n)的复杂度。 - 容易看出,前面的几类复杂度被分为两种级别,其中后者的复杂度无论如何都远远大于前者:一种是O(1),O(log(n)),O(n^a)等,我们把它叫做多项式级的复杂度,因为它的规模n出现在底数的位置;另一种是O(a^n)和O(n!)型复杂度,它是非多项式级的,其复杂度计算机往往不能承受。当我们在解决一个问题时,我们选择的算法通常都需要是多项式级的复杂度,非多项式级的复杂度需要的时间太多,往往会超时,除非是数据规模非常小。 -P类问题的概念 -如果一个问题可以找到一个能在多项式的时间里解决它的算法,那么这个问题就属于P问题。 -NP问题的概念 -这个就有点难理解了,或者说容易理解错误。在这里强调(回到我竭力想澄清的误区上),NP问题不是非P类问题。NP问题是指可以在多项式的时间里验证一个解的问题。NP问题的另一个定义是,可以在多项式的时间里猜出一个解的问题。比方说,我RP很好,在程序中需要枚举时,我可以一猜一个准。现在某人拿到了一个求最短路径的问题,问从起点到终点是否有一条小于100个单位长度的路线。它根据数据画好了图,但怎么也算不出来,于是来问我:你看怎么选条路走得最少?我说,我RP很好,肯定能随便给你指条很短的路出来。然后我就胡乱画了几条线,说就这条吧。那人按我指的这条把权值加起来一看,嘿,神了,路径长度98,比100小。于是答案出来了,存在比100小的路径。别人会问他这题怎么做出来的,他就可以说,因为我找到了一个比100 小的解。在这个题中,找一个解很困难,但验证一个解很容易。验证一个解只需要O(n)的时间复杂度,也就是说我可以花O(n)的时间把我猜的路径的长度加出来。那么,只要我RP好,猜得准,我一定能在多项式的时间里解决这个问题。我猜到的方案总是最优的,不满足题意的方案也不会来骗我去选它。这就是NP问题。当然有不是NP问题的问题,即你猜到了解但是没用,因为你不能在多项式的时间里去验证它。下面我要举的例子是一个经典的例子,它指出了一个目前还没有办法在多项式的时间里验证一个解的问题。很显然,前面所说的Hamilton回路是NP问题,因为验证一条路是否恰好经过了每一个顶点非常容易。但我要把问题换成这样:试问一个图中是否不存在Hamilton回路。这样问题就没法在多项式的时间里进行验证了,因为除非你试过所有的路,否则你不敢断定它“没有Hamilton回路”。 - 之所以要定义NP问题,是因为通常只有NP问题才可能找到多项式的算法。我们不会指望一个连多项式地验证一个解都不行的问题存在一个解决它的多项式级的算法。相信读者很快明白,信息学中的号称最困难的问题——“NP问题”,实际上是在探讨NP问题与P类问题的关系。 +## 1 时间复杂度 +时间复杂度并不是表示一个程序解决问题需要花多少时间,而是当问题规模扩大后,程序需要的时间长度增长得有多快。也就是说,对于高速处理数据的计算机来说,处理某一个特定数据的效率不能衡量一个程序的好坏,而应该看当这个数据的规模变大到数百倍后,程序运行时间是否还是一样,或者也跟着慢了数百倍,或者变慢了数万倍。 -NPC问题的定义 -同时满足下面两个条件的问题就是NPC问题。首先,它得是一个NP问题;然后,所有的NP问题都可以约化到它。证明一个问题是 NPC问题也很简单。先证明它至少是一个NP问题,再证明其中一个已知的NPC问题能约化到它(由约化的传递性,则NPC问题定义的第二条也得以满足;至于第一个NPC问题是怎么来的,下文将介绍),这样就可以说它是NPC问题了。 \ No newline at end of file +不管数据有多大,程序处理花的时间始终是那么多的,我们就说这个程序很好,具有O(1)的时间复杂度,也称常数级复杂度;数据规模变得有多大,花的时间也跟着变得有多长,这个程序的时间复杂度就是O(n),比如找n个数中的最大值;而像冒泡排序、插入排序等,数据扩大2倍,时间变慢4倍的,属于O(n^2)的复杂度。 + +还有一些穷举类的算法,所需时间长度成几何阶数上涨,这就是O(a^n)的指数级复杂度,甚至O(n!)的阶乘级复杂度。不会存在O(2*n^2)的复杂度,因为前面的那个“2”是系数,根本不会影响到整个程序的时间增长。同样地,O (n^3+n^2)的复杂度也就是O(n^3)的复杂度。因此,我们会说,一个O(0.01*n^3)的程序的效率比O(100*n^2)的效率低,尽管在n很小的时候,前者优于后者,但后者时间随数据规模增长得慢,最终O(n^3)的复杂度将远远超过O(n^2)。我们也说,O(n^100)的复杂度小于O(1.01^n)的复杂度。 + +容易看出,前面的几类复杂度被分为两种级别,其中后者的复杂度无论如何都远远大于前者:一种是O(1),O(log(n)),O(n^a)等,我们把它叫做多项式级的复杂度,因为它的规模n出现在底数的位置;另一种是O(a^n)和O(n!)型复杂度,它是非多项式级的,其复杂度计算机往往不能承受。当我们在解决一个问题时,我们选择的算法通常都需要是多项式级的复杂度,非多项式级的复杂度需要的时间太多,往往会超时,除非是数据规模非常小。 + +## P类问题的概念 +如果一个问题可以找到一个能在多项式的时间里解决它的算法,那么这个问题就属于P问题。 + +## NP问题的概念 +这个就有点难理解了,或者说容易理解错误。在这里强调(回到我竭力想澄清的误区上),NP问题不是非P类问题。NP问题是指可以在多项式的时间里验证一个解的问题。NP问题的另一个定义是,可以在多项式的时间里猜出一个解的问题。 + +比方说,我RP很好,在程序中需要枚举时,我可以一猜一个准。现在某人拿到了一个求最短路径的问题,问从起点到终点是否有一条小于100个单位长度的路线。它根据数据画好了图,但怎么也算不出来,于是来问我:你看怎么选条路走得最少?我说,我RP很好,肯定能随便给你指条很短的路出来。然后我就胡乱画了几条线,说就这条吧。那人按我指的这条把权值加起来一看,嘿,神了,路径长度98,比100小。于是答案出来了,存在比100小的路径。别人会问他这题怎么做出来的,他就可以说,因为我找到了一个比100 小的解。在这个题中,找一个解很困难,但验证一个解很容易。验证一个解只需要O(n)的时间复杂度,也就是说我可以花O(n)的时间把我猜的路径的长度加出来。那么,只要我RP好,猜得准,我一定能在多项式的时间里解决这个问题。我猜到的方案总是最优的,不满足题意的方案也不会来骗我去选它。这就是NP问题。 + +当然有不是NP问题的问题,即你猜到了解但是没用,因为你不能在多项式的时间里去验证它。下面我要举的例子是一个经典的例子,它指出了一个目前还没有办法在多项式的时间里验证一个解的问题。很显然,前面所说的Hamilton回路是NP问题,因为验证一条路是否恰好经过了每一个顶点非常容易。但我要把问题换成这样:试问一个图中是否不存在Hamilton回路。这样问题就没法在多项式的时间里进行验证了,因为除非你试过所有的路,否则你不敢断定它“没有Hamilton回路”。 + +之所以要定义NP问题,是因为通常只有NP问题才可能找到多项式的算法。我们不会指望一个连多项式地验证一个解都不行的问题存在一个解决它的多项式级的算法。相信读者很快明白,信息学中的号称最困难的问题——“NP问题”,实际上是在探讨NP问题与P类问题的关系。 + +## NPC问题的定义 +同时满足下面两个条件的问题就是NPC问题。首先,它得是一个NP问题;然后,所有的NP问题都可以约化到它。证明一个问题是 NPC问题也很简单。先证明它至少是一个NP问题,再证明其中一个已知的NPC问题能约化到它(由约化的传递性,则NPC问题定义的第二条也得以满足;至于第一个NPC问题是怎么来的,下文将介绍),这样就可以说它是NPC问题了。 + +尽管该理论未能证明确实不存在求解这些难问题的高效算法,但是却已证明这些难问题大多数是互相等价的,即:如果我们可以找到针对某一个这类问题的高效算法,那么我们就能找到求解该类问题中其他所有问题的高效算法。 +我们把这类 “计算上等价” 的问题称为NP-complete问题. diff --git a/Algorithm/A类:基本算法/image/禁忌搜索算法.png b/Algorithm/A类:基本算法/image/禁忌搜索算法.png new file mode 100644 index 00000000..d06c4a96 Binary files /dev/null and b/Algorithm/A类:基本算法/image/禁忌搜索算法.png differ diff --git a/Algorithm/A类:基本算法/image/递归算法-时间复杂度.png b/Algorithm/A类:基本算法/image/递归算法-时间复杂度.png deleted file mode 100644 index b8343839..00000000 Binary files a/Algorithm/A类:基本算法/image/递归算法-时间复杂度.png and /dev/null differ diff --git a/Algorithm/A类:基本算法/image/递归算法-等比收缩说明.png b/Algorithm/A类:基本算法/image/递归算法-等比收缩说明.png deleted file mode 100644 index 8c9f7d6e..00000000 Binary files a/Algorithm/A类:基本算法/image/递归算法-等比收缩说明.png and /dev/null differ