Authonr:Lijb
五の一般的に使用されるアルゴリズム
1、递归与分治
递归算法:直接或者间接不断反复调用自身来达到解决问题的方法。这就要求原始问题可以分解成相同问题的子问题。
示例:阶乘、斐波纳契数列、汉诺塔问题
斐波纳契数列:又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21、……在数学上,斐波纳契数列以如下被以递归的方法定义:F1=1,F2=1,Fn=F(n-1)+F(n-2)(n>2,n∈N*))。
分治算法:待解决复杂的问题能够简化为几个若干个小规模相同的问题,然后逐步划分,达到易于解决的程度。
1、将原问题分解为n个规模较小的子问题,各子问题间独立存在,并且与原问题形式相同
2、递归的解决各个子问题
3、将各个子问题的解合并得到原问题的解
示例:棋盘覆盖、找出伪币、求最值
棋盘覆盖:在一个(2^k)*(2^k)个方格组成的棋盘上,有一个特殊方格与其他方格不同,称为特殊方格,称这样的棋盘为一个特殊棋盘。要求对棋盘的其余部分用L型方块填满
2、动态规划
动态规划与分治法相似,都是组合子问题的解来解决原问题的解,与分治法的不同在于:分治法的子问题是相互独立存在的,而动态规划应用于子问题重叠的情况。
动态规划方法通常用来求解最优化问题,这类问题可以有很多可行解,每个解都有一个值,找到具有最优值的解称为问题的一个最优解,而不是最优解,可能有多个解都达到最优值。
设计动态规划算法的步骤:
1、刻画一个最优解的结构特征
2、递归地定义最优解的值
3、计算最优解的值,通常采用自底向上的方法
4、利用算出的信息构造一个最优解
示例:0-1背包问题,钢条切割问题等。
3、贪心算法
贪心算法是就问题而言,选择当下最好的选择,而不从整体最优考虑,通过局部最优希望导致全局最优。
贪心算法的要素
1)贪心选择性质:可以通过局部最优选择来构造全局最优解。换言之,直接做出在当前问题中看来最优的选择,而不必考虑子问题的解。
2)最优子结构:一个问题的最优解包含其子问题的最优解。
贪心算法的设计步骤:
1)将最优化问题转换为这样的形式:对其做出一次选择后,只剩下一个子问题需要求解
2)证明做出贪心选择后,原问题总是存在最优解,即贪心选择总是安全的
3)证明做出贪心选择后,剩余的子问题满足性质:其最优解与贪心选择组合即可得到原问题的最优解,这样就得到了最优子结构。
示例:背包问题,均分纸牌,最大整数
4、回溯法
回溯法是一种搜索算法,从根节点出发,按照深度优先搜索的策略进行搜索,到达某一节点后 ,探索该节点是否包含该问题的解,如果包含则进入下一个节点进行搜索,若是不包含则回溯到父节点选择其他支路进行搜索。
回溯法的设计步骤:
1)针对所给的原问题,定义问题的解空间
2)确定易于搜索的解空间结构
3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数除去无效搜索。
示例:0-背包问题、旅行商问题、八皇后问题
5、 分支限界法
和回溯法相似,也是一种搜索算法,但回溯法是找出问题的许多解,而分支限界法是找出原问题的一个解。或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解
在当前节点(扩展节点)处,先生成其所有的儿子节点(分支),然后再从当前的活节点(当前节点的子节点)表中选择下一个扩展节点。为了有效地选择下一个扩展节点,加速搜索的进程,在每一个活节点处,计算一个函数值(限界),并根据函数值,从当前活节点表中选择一个最有利的节点作为扩展节点,使搜索朝着解空间上有最优解的分支推进,以便尽快地找出一个最优解。
分支限界法:
1)FIFO分支限界法
3)优先队列分支限界法:按照优先队列中规定的优先级选取优先级最高的节点成为当前扩展节点。
示例:装载问题,旅行售货员问题
動的なプログラミングアルゴリズム
編集距離の問題
二つの文字列(編集距離)の編集距離とは何ですか?S1、文字列s1とs2を考えると、以下の操作で:
- (挿入)文字を挿入します
- 削除文字を(削除)
- 文字を交換(置き換え)
何回、このような操作は、S1、S2を作るために必要な最小限に変換することができますか?
例えば、単語「猫」と「帽子」は、この操作は「H」のように「猫」を置き換える必要がありますすることができ、単に「C」、時間の最小値が必要です。単語「リコール」と「呼び出し」は、このような操作は、最低2回必要で、単に「リコール」「R」と「E」は削除することができますする必要があります。英語は「日曜日」と「土曜日」、そのような操作を3回の最低必要、「S」と「U」と「日曜日」の「a」および「T」を挿入し、次いで「n」は「R」、すなわちによって置換しますことができます。
だから、効率的なアルゴリズムは、迅速かつ正確に二つの文字列、それの編集距離を計算することができますがありますか?
動的なプログラミングアルゴリズム
我々は2つの文字列の編集距離を計算するために、動的プログラミングアルゴリズム(動的計画法)を使用します。
私たちは、s1と前方に考えられs2は最も端の2つの文字列を横断します。S1の長さmを仮定する、長さS2は次のようなアルゴリズムであり、nは次のとおりです。
- 同一の最後の2つの文字列は、その後、我々は2つのM-1の文字列及び再帰N-1の場合の長さを計算することができる場合。
- 最後の2つの文字列が同じでない場合は、次の3つのシナリオのように入力します。
-
- 挿入:再帰的に最後の文字のS1、S2に挿入され、最後に、このようなS1およびS2として終了文字は、ケース1であるため、M n-1の長さと二つの文字列の場合を計算します;
- 削除:長さM-1文字を削除するの端子s1にある再帰的にNの二つの文字列の場合を計算します。
- あるいは:s1と同じ文字のS2の端部は、となるように端子s1が文字置換の最後の文字が、S2となっているので、再帰的に、ケースの長さ二つのM-1〜N-1の文字列を計算します場合;
したがって、我々は、サブ構造の問題を抱えています。動的プログラミングアルゴリズムのために、我々はまた、2次元テーブルを維持するために、次に、中間の初期化処理を必要とします。次のように初期化プロセスである:mが0である場合、少なくとも必要な操作をn回、S1で一つずつS2すなわち、追加の文字、n回の合計であり; nが0である場合には、少なくとも必要な操作m倍、つまり一つ一つS1文字それ、m回の合計を削除します。
Python実装
次のように二つの文字列のPythonコードを解決するための編集距離アルゴリズムを使用してDP:
'''
[@author](https://my.oschina.net/arthor) lijb
[@company](https://my.oschina.net/u/3478402) 东网科技
[@date](https://my.oschina.net/u/2504391) 2019/3/22 10:06
'''
#使用动态规划算法计算字符串的编辑距离
#传入两个字符串类型的参数
def edit_distance(s1, s2):
m, n = len(s1), len(s2)
#创建数组
dp = [[0 for _ in range(n+1)] for _ in range(m+1)]
# 使用动态规划算法
for i in range(m + 1):
for j in range(n + 1):
if i == 0:
dp[i][j] = j
elif j == 0:
dp[i][j] = i
elif s1[i-1] == s2[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = 1 + min(dp[i][j-1], # Insert
dp[i-1][j], # Remove
dp[i-1][j-1]) # Replace
return dp[m][n]
# Driver program
str1 = "ljb"
str2 = "lijb"
edit_dist = edit_distance(str1, str2)
print("The Edit Distance of '%s' and '%s' is %d."%(str1, str2, edit_dist))
出力:
The Edit Distance of 'ljb' and 'lijb' is 1.
Java実装
次のように二つの文字列のJavaコードを解決するための編集距離アルゴリズムを使用してDP:
package com.neunn;
/**
* [@author](https://my.oschina.net/arthor) lijb
* [@company](https://my.oschina.net/u/3478402) 东网科技
* @date 2019/3/22 10:06
*/
public class Dynamic_Programming {
public static void main(String[] args) {
String str1 = "lijb";
String str2 = "ljb";
int edit_dist = edit_distance(str1, str2);
System.out.println("The Edit Distance of" + str1 + " and " + str2 + " is: " + edit_dist);
}
/**
* 计算两个字符串的编辑距离(Edit Distance)
* @param str1 字符串参数1
* @param str2 字符串参数2
* @return 编辑距离
*/
public static int edit_distance(String str1, String str2) {
int m = str1.length();
int n = str2.length();
// 初始化表格,用于维护子问题的解
int[][] dp = new int[m + 1][n + 1];
for (int i = 0; i <= m; i++)
for (int j = 0; j <= n; j++)
dp[i][j] = 0;
/**
* 使用动态规划算法
*/
for (int i = 0; i <= m; i++) {
for (int j = 0; j <= n; j++) {
if (i == 0) {
dp[i][j] = j;
} else if (j == 0) {
dp[i][j] = i;
} else if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
/*
* dp[i][j-1]: Insert
* dp[i-1][j]: Remove
* dp[i-1][j-1]: Replace
*/
dp[i][j] = 1 + min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]);
}
}
}
return dp[m][n];
}
public static int min(int i, int j) {
return (i <= j) ? i : j;
}
}
出力:
The Edit Distance of ljb and lijb is: 1
Scalaは達成します
次のように二つの文字列Scalaのコードを解決するための編集距離アルゴリズムを使用してDP:
package com.neunn
/**
* @author lijb
* @company 东网科技
* @date 2019/3/22 10:19
*/
class Dynamic_Programming {
}
object Dynamic_Programming {
def main(args: Array[String]): Unit = {
val str1 = "ljb";
val str2 = "lijb";
val edit_dist = edit_distance(str1, str2)
println("The Edit Distance of " + str1 + " and " + str2 + " is: " + edit_dist);
}
def edit_distance(str1: String, str2: String): Int = {
val m = str1.length
val n = str2.length
/**
* 创建数组并初始化
*/
val dp = Array.ofDim[Int](m + 1, n + 1)
for (i <- 0 to m) {
for (j <- 0 to n) {
dp(i)(j) = 0
}
}
/**
* 使用动态规划算法
*/
for (i <- 0 to m) {
for (j <- 0 to n) {
if (i == 0) {
dp(i)(j) = j
} else if (j == 0) {
dp(i)(j) = i
} else if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
dp(i)(j) = dp(i - 1)(j - 1)
} else {
dp(i)(j) = 1 + min(min(dp(i)(j - 1), dp(i - 1)(j)), dp(i - 1)(j - 1))
}
}
}
dp(m)(n)
}
def min(i: Int, j: Int): Int = if (i <= j) i
else j
}
出力:
The Edit Distance of ljb and lijb is: 1