第七章复习
引言:
-
分治算法的不足之处:
Fibonacci数列:f
1
=1,f2
=1,求f(n);递归的方法:
long fib(n){ if(n==1 || n==2){ return 1; } else{ return fib(n-1)+fib(n-2); } }
时间复杂度分析:
f ( n ) = 1 5 ∗ ( 1 + 5 2 ) n − 1 5 ∗ ( 1 − 5 2 ) n f(n) = \frac{1}{\sqrt 5}*(\frac{1+\sqrt 5}{2})^n - \frac{1}{\sqrt 5}*(\frac{1-\sqrt 5}{2})^n f(n)=51∗(21+5)n−51∗(21−5)n;
自底向上的计算方法:
long fib(n){ if(n==1||n=2){ return 1; } for(i=3;i<=n;i++){ result =f1+f2; f1=f2; f2=result; } return result; }
时间复杂度: O ( n ) O(n) O(n);
-
思考:给出复杂度 O ( log n ) O(\log n) O(logn)的 f ( n ) f(n) f(n)求值算法;
-
二项式例子不再给出(P130);
-
启发:
- 递归的思路分析问题;
- 自底向上的方法实现;
下面的每一个例子都很重要
最长公共子序列问题
-
问题:
- A = <A
1
A2
···An
>; - B = <B
1
B2
···Bn
>; - 蛮力的搜索方法: Θ ( m ∗ 2 n ) Θ(m*2^n) Θ(m∗2n)或者 Θ ( n ∗ 2 n ) Θ(n*2^n) Θ(n∗2n);
- A = <A
-
算法思路:
-
先求最长公共子序列的长度;
-
定义L[i,j]表示<A
1
A2
…Ai
>和<B1
B2
…Bj
>的最长公共子序列的长度,则L[i,0]=L[0,j]=0;
问题的目标是求L[n,m];
对于任意的L[i,j],当i>0且j>0,我们有
如果 A i = B j Ai=Bj Ai=Bj, L [ i , j ] = L [ i − 1 , j − 1 ] + 1 L[i,j]=L[i-1,j-1]+1 L[i,j]=L[i−1,j−1]+1;
如果 A i ≠ B j Ai \neq Bj Ai=Bj, L [ i , j ] = m a x L [ i , j − 1 ] , L [ i − 1 , j ] L[i,j]=max{L[i,j-1],L[i-1,j]} L[i,j]=maxL[i,j−1],L[i−1,j];
-
总结:
L [ i , j ] = { 0 ( i = 0 ∣ ∣ j = 0 ) L [ i − 1 , j − 1 ] + 1 ( i > 0 , j > 0 , a i = b j ) m a x L [ i , j − 1 ] , L [ i − 1 , j ] ( i > 0 , j > 0 , a i ≠ b j ) L[i,j] = \begin{cases} 0(i=0||j=0)\\L[i-1,j-1]+1(i>0,j>0,ai=bj)\\max{L[i,j-1],L[i-1,j]}(i>0,j>0 ,ai \neq bj) \end{cases} L[i,j]=⎩⎪⎨⎪⎧0(i=0∣∣j=0)L[i−1,j−1]+1(i>0,j>0,ai=bj)maxL[i,j−1],L[i−1,j](i>0,j>0,ai=bj)
-
初始化:
for(i=0;i<n;i++){ L[i,0]=0 } for(j=0;j<m;j++){ L[0,j]=0; }
- 自底向上计算:
for(i=1;i<n;i++){ for(j=1;j<m;j++){ if(a[i]==b[j]){ L[i,j] = L[i-1,j-1]+1; } else { L[i,j]=max{L[i,j-1],L[i-1,j]}; } } } return L[n,m];
-
-
算法优化:
- 空间复杂度(能否只使用一维数组):
LCS(){ Q[1,2,3...,n] = 0; for(j=1;j<=m;j++){ if[Bj=A1] T=1; else T=Q[1]; for(i=2;i<=n;i++){ if(Bj=Ai){ S=Q[i-1]+1; Q[i-1]=T; T=S; } else{ Q[i-1]=T; T=max{T,Q[i]}; } } Q[n]=T; } return Q[n]; }
- 如何从长度得到最长公共子序列(思路)
- 我们已经可以得知最长子序列的长度;
- 然后由最长子序列的长度逐渐回溯找到最长公共子序列;
矩阵链相乘
- 问题:求出计算n个矩阵相乘M
1
M2
…Mn
所需要的最少的乘法次数;- 引例:如果M
1
M2
分别是 a ∗ b a*b a∗b和 b ∗ c b*c b∗c的,那么计算M1
M2
分需要的数量乘法为 a ∗ b ∗ c a*b*c a∗b∗c次; - 计算M
1
M2
M3
(其维度分别是10 * 2,2 * 10,10 * 2);- 第一种计算顺序:M
1
M2
M3
=[M1
M2
]M3
;需要的乘法的次数是200+200=400; - 第二种计算书讯:M
1
M2
M3
=M1
[M2
M2
];需要的乘法的次数是40+40=80;
- 第一种计算顺序:M
- 蛮力法:列举每一种可能的结合顺序,比较其所需的乘法次数;
- 假设n个矩阵相乘可能的结合顺序有f(n)种,则(M
1
M2
M3
···Mk
) * (Mk+1
Mk+2
···Mn
); - f ( k ) ∗ f ( n − k ) f(k)*f(n-k) f(k)∗f(n−k),其中k = 1,2,…,n-1;
- f ( n ) = ∑ k = 1 n − 1 f ( k ) f ( n − k ) f(n) = \sum_{k=1}^{n-1}f(k)f(n-k) f(n)=∑k=1n−1f(k)f(n−k) , f ( 2 ) = 1 , f ( 3 ) = 2 f(2)=1,f(3)=2 f(2)=1,f(3)=2;
- 假设n个矩阵相乘可能的结合顺序有f(n)种,则(M
- 引例:如果M
- 算法思路:
- 动态规划:
- 数组r[1,2,…,n+1]中存储每个矩阵的行数和列数;
- 设置r[i]存储矩阵M
i
的行数,则i>1时,r[i]必然也是矩阵Mi-1
的列数; - 设置r[n+1]存储矩阵M
n
的列数;
- 最优解的结构:
- 记M
i,j
=Mi
Mi+1
…Mj
; - C[i,j]表示计算所需要的数量乘法的最少次数:(M
i
Mi+1
…Mk-1
)(Mk
Mk+1
…Mj
); (其中k=i+1,…,j); - C [ i , j ] = m i n ( C [ i , k − 1 ] + C [ k , j ] + r [ i ] ∗ r [ k ] ∗ r [ j + 1 ] ) , ( i < k ≤ j ) C[i,j] = min(C[i,k-1]+C[k,j]+r[i]*r[k]*r[j+1]),(i<k\leq j) C[i,j]=min(C[i,k−1]+C[k,j]+r[i]∗r[k]∗r[j+1]),(i<k≤j)
- 这里需要求出的是C[1,n],且C[i,i]=0;
- 记M
- 动态规划:自底向上的算法求C[1,n];
- 计算C[i,j]需要得知同一行的所有值,同一列的所有值;
- 按照对角线方向计算值,直到找到C[1,n];
- 动态规划:
- 算法呈现:
void MatrixChain(){
for(i=1;i<=n;i++){
C[i,i]=0;
}
for(d=1;d<=n-1;d++){
for(i=1;i<=n-d;i++){
j=i+d;
C[i,j]=C[i+1,j]+r[i]*r[i+1]*r[j+1];
for(k=i+2;k<=j;i++){
C[i,j]=min{C[i,j],C[i,k-1]+C[k,j]+r[i]*r[k]*r[j+1]};
}
}
}
return C[1,n];
}
- 算法分析:时间复杂度 O ( n 3 ) O(n^3) O(n3);
所有点的最短路径
-
问题:G=(V,E)有向带权图
- l[i,j]为i到j的边的权值,如果没有边则为 + ∞ +∞ +∞;
- V包含了n个顶点,记为{1,2,···,n};
- 求出任意两个顶点之间的最短路径长度;
- 最终计算结果的存储:n*n的二维数组
- 比如D[1,2,…,n] [1,2,…,n],其中D[i] [j]表示i到j的最短路径长度;
- D
0
[i] [j]=l[i] [j],Dn
[1,2,…,n] [1,2,…,n];
-
Floyd算法:
- 考虑 d ( i , j ) k , 1 ≤ k ≤ n d(i,j)^{k}, 1\leq k \leq n d(i,j)k,1≤k≤n, d ( i , j ) k d(i,j)^k d(i,j)k与 d ( i , j ) k − 1 d(i,j)^{k-1} d(i,j)k−1的区别在于是否经过顶点k;
- 从i到j有两种可能
- 不经过k;
- 经过k;
- d ( i , j ) k = m i n ( d ( i , j ) k − 1 , d ( i , k ) k − 1 + d ( k , j ) k − 1 ) d(i,j)^k=min(d(i,j)^{k-1},d(i,k)^{k-1}+d(k,j)^{k-1}) d(i,j)k=min(d(i,j)k−1,d(i,k)k−1+d(k,j)k−1),若 1 ≤ k ≤ n 1\leq k\leq n 1≤k≤n; ( D ( k − 1 ) → D ( k ) D(k-1) \rightarrow D(k) D(k−1)→D(k));
- 用权值l[i,j]初始化数组D
0
;
for(k=1;k<=n;j++){ for(i=1;i<=n;i++){ for(j=1;j<=n;j++){ D(k)[i][j]=min{D(k-1)[i][j],D(k-1)[i][k]+D(k-1)[k][j]}; } } }
- 算法优化(能否减少空间开销只收用一个数组):
for(k=1;k<=n;j++){ for(i=1;i<=n;i++){ for(j=1;j<=n;j++){ D[i][j]=min{D[i][j],D[i][k]+D[k][j]}; } } }
背包问题
-
问题:物品集U={u
1
,u2
,…,un
},背包容量为C,si
和vi
分别为ui
的体积和价值,求出U的子集S,使得 ∑ u i ∈ S s i ≤ C \sum_{ui \in S}si \leq C ∑ui∈Ssi≤C;常用于密码算法设计中;- 定义V[i,j]表示从前i个物品{u
1
,u2
,…,ui
},选择若干物品装入j个体积,能够获得最大价值 - 那么:
- 若i=0或j=0,V[i,j]=0;
- 若j<s
i
,V[i,j]=V[i-1,j]; - 若
i
>
0
i>0
i>0且
j
≥
j\geq
j≥s
i
,有两种可能:(1)不装ui
(2)装ui
; - V[i,j] = max{V[i-1,j],V[i-1,j-s
i
]+vi
};
- 总结: V [ i , j ] = { 0 ( i = 0 O r j = 0 ) V [ i − 1 , j ] ( j < s i ) m a x ( V [ i − 1 , j ] , V [ i − 1 , j − s i ] + v i ) ( i > 0 A n d j ≥ s i ) V[i,j] = \begin{cases} 0(i=0 Or j=0)\\V[i-1,j](j<si)\\max(V[i-1,j],V[i-1,j-si]+vi)(i>0Andj\geq si)\end{cases} V[i,j]=⎩⎪⎨⎪⎧0(i=0Orj=0)V[i−1,j](j<si)max(V[i−1,j],V[i−1,j−si]+vi)(i>0Andj≥si)
- 定义V[i,j]表示从前i个物品{u
-
算法呈现:
for(i=0;i<=n;i++){
V[i,0]=0;
}
for(j=0;j<=C;j++){
V[0,j]=0;
}
for(i=1;i<=n;i++){
for(j=1;j<=C;j++){
V[i,j]<--V[i-1,j];
if(si<=j){
V[i,j]=max{V[i,j],V[i-1,j-si]+vi};
}
}
}
return V[n,C];
-
算法分析:时间复杂度 O ( n ∗ C ) O(n*C) O(n∗C);
-
算法优化
- 只使用一维数组:
void knapsack() { W[0,1,2,...,C]=0; Q[0,1,2,...,C]=0; for(i=1;i<=n;i++){ for(j=1;j<=C;j++){ Q[j]=W[j]; if(si<=j){ Q[k]=max{Q[j],W[j-si]+vi}; } W[1,2,...,C]=Q[1,2,...,C] } } return W[C]; }
- 还能进一步减小开销:
void knapsack(){ W[0,1,2,...,C]=0; for(i=1;i<=n;i++){ for(j=c;j>=1;j--){ if(si<=j){ W[j]=max{W[j],W[j-si]+vi}; } } } return W[C]; }
第八章复习
贪心策略
- 每一步都建立在局部最优解的基础上;
- 不一定可以获得全局最优解;
- 有些问题可以获得全局最优解:比如:最短路径;最小生成树;
最短路径
-
问题:G=(V,E)有向带权图;
-
Dijksra算法:
- 算法框架:
- X ← \leftarrow ← {1};Y ← \leftarrow ← V -{1};
- 对每个 v ∈ Y v\in Y v∈Y,如果存在从1到v的边,则令 λ [ v ] λ[v] λ[v] (v的标记)为边的长度;否则令 λ [ v ] = ∞ λ[v] =∞ λ[v]=∞,并设 λ [ 1 ] = 0 λ[1]=0 λ[1]=0;
- while Y ≠ \neq = {}
- 令 y ∈ Y y \in Y y∈Y,使得 λ [ y ] λ[y] λ[y]为最小;
- 将y从Y移到X;
- 更新那些在Y中与y相邻的顶点的标记
- end while;
- 算法呈现:
X={1};Y=V-{1};λ[1]=0; for(y=2;y<=n;y++){ if(y相邻于1){ λ[y]=length[1,y]; } else λ[y]=∞; } for(j=1;j<=n-1;j++){ 令y∈Y,使得λ[y]为最小 X=X∪{y} //{将顶点y加入X} Y=Y∪{y} //{将顶点y从Y中删除} for(每条边y,w){ if(w∈Y && λ[y]+length[y,w]<λ[w]){ λ[w]=λ[y]+length[y,w]; } } }
最小生成树
- 问题:G=(V,E)无向联通带权图;求(V,T),其中T是E的子集;
可以回顾前面所讲的树相关的知识;
-
Kruskal算法:
- 判断加入某条边,是否会导致回路:
- 思路:将节点分成若干个子集,已联通的节点放在同一子集里
- 并集操作
- 判定是否属于相同子集;
- 思路:将节点分成若干个子集,已联通的节点放在同一子集里
- MAKESET,FIND(x),UNION(y);
- 每次FIND和UNION的时间复杂度 O log n O\log n Ologn;
- 算法框架:
//按非降序权重将E中的边排序 for 每条边v∈V Makeset({v}); end for T={} while |T|<n-1 令(x,y)为E的下一条边 if FIND(x) != FIND(y) 将(x,y)加入T UNION(x,y); end if end while
- 算法分析:时间复杂度 O ( n 2 ∗ log n ) O(n^2 * \log n) O(n2∗logn);
- 判断加入某条边,是否会导致回路:
-
Prim算法:
- 算法框架:
T={};X={1};Y=V-{1}; while Y !={} 设(x,y)是最小权重的边,其中x∈X,y∈Y X=X∪{y}; Y=Y-{y}; T=T∪{(x,y)}; end while
-
算法的实现:
- 数组N[1,2,…,n]和C[1,2,…,n],C[y]表示边(y,N[y])的权值;
- 算法步骤一:初始化
T={};X={1};Y=V-{1}; for(y=2;y<=n;y++){ if(y邻接于1){ N[y]=1; C[y]=c[1,y]; } else C[y]=∞; }
- 算法步骤二:
for(j=1;j<=n-1;j++){ 令y∈Y,使得C[y]最小 T=T∪{(y,N[y])}; X=X∪{y}; Y=Y-{y}; for(每个邻接于y的顶点w∈Y){ if(c[y,w]<C[w]){ N[w]=y; C[w]=c[y,w]; } } }
-
算法分析:时间复杂度 O ( n 2 ) O(n^2) O(n2);
第九章复习
广度优先和深度优先这里不在赘述;
第十章复习
- 易解问题与难解问题的主要区别
- 多项式函数与指数函数的增长率有本质区别;
- 计算机性能的提高对易解问题与难解问题算法的影响;
- 多项式时间复杂性忽略了系数,不影响易解问题与难解问题的划分;
P类问题和NP类问题
-
划分标准是基于对所谓判定问题的求解方式
-
对判定问题求解,可以采用确定性算法:
-
定义一:设A是求解问题Π的一个算法,如果在算法的整个执行过程中,每一步只有一个确定的选择,则称算法A是确定性算法
特点:对同一输入实例,运行算法A,所得结果是一样的
-
定义二(P类问题):如果对于某个判定问题Π,存在一个非负整数k,对于输入规模为n的实例,能够以 O ( n k ) O(n^k) O(nk)的时间运行一个确定性算法,得到yes或no的答案,则称该判定问题Π是一个P(Polynomial)类问题;
-
定义三(非确定性算法):设A是求解问题Π的一个算法,如果算法A以如下猜测+验证的方式工作,则称A为非确定性(nondeterminism)算法:
- 猜测阶段:对问题的输入实例产生一个任意子串y,在算法的每次运行,y可能不同,因此猜测是以非确定的形式工作,这个工作一般可以在线性时间内完成
- 验证阶段:在这个阶段,用一个确定性算法验证两件事:首先验证猜测的y是否是合适的形式,若不是,则算法停下并回答no;若是合适形式,则继续检查它是否是问题x的解,如果确实是x的解,则停下来并回答yes,否则停下来回答no,要求验证阶段在多项式时间内完成。
-
注意对非确定性算法输出yes/no的理解:
- 若输出No,并不意味着不存在一个满足要求的解,因为猜测可能不正确;若输出yes,则意味着对于该判定问题的某一输入实例,至少存在一个满足要求的解。
-
定义四(NP类问题):如果对于判定问题Π,存在一个非负整数k,对于输入规模为n的实例,能够以 O ( n k ) O(n^k) O(nk)的时间运行一个非确定性算法,得到yes/no的答案,则该判断问题Π是一个NP(nondeterminism Polynomial)类问题,通俗的说:多项式时间内可以验证某个值是否是正确答案。
注意:NP类问题是对于判定问题定义的,事实上,可以在多项式时间内应用非确定性算法解决的所有问题都属于NP类问题;
-
-
关于P与NP关系的初步思考:
-
P ⊆ N P P\subseteq NP P⊆NP
-
若问题Π属于NP类,则存在一个多项式时间的非确定性算法,来猜测并且验证它的解;但不一定能够构造一个多项式时间的确定性算法,来对它进行求解或判定
因此,人们猜测 P ≠ N P P\neq NP P=NP,但是否成立,至今未得到证明。
-
NP完全问题
-
NP完全问题是NP类问题的子类,一个具有特殊性质与特殊意义的子类;
-
问题归约:NP类问题在最坏情况下的时间复杂性一般都是快速增长的指数函数,我们希望能够在NP类问题内部找到一种方法,比较两个问题的复杂性。
-
定义五(多项式时间归约):若在 O ( τ ( n ) ) O(τ(n)) O(τ(n))时间内完成上述输入/输出转换,则称问题Π以 τ ( n ) τ(n) τ(n)时间归约到问题Π’,记为 Π ∝ τ ( n ) Π ′ Π∝τ(n)Π' Π∝τ(n)Π′,其中,n为问题规模;若在多项式时间内完成上述转换,则称问题Π以多项式时间归约到问题Π’,记为 Π ∝ p o l y Π ′ Π∝polyΠ' Π∝polyΠ′
-
举例(多项式归约):
-
排序问题的算法A
-
配对问题Π:
-
求解配对问题,需要进行三个变换:
- 将配对问题的输入x,y变成排序问题的两个输入I
1
,I2
; - 应用算法A对I
1
,I2
分别排序,得到两个排序输出O1
,O2
; - 将两个排序输出O
1
,O2
,转换成配对问题的输出O;
- 将配对问题的输入x,y变成排序问题的两个输入I
-
-
多项式时间归约的性质:
- 传递性
- 自反性
-
NP完全问题是一类具备如下特殊性质的NP类问题:
- Π(该问题本身)就是一个NP类问题;
- 每个NP类问题都可以多项式时间归约为Π;
-
定义六(NP完全问题):令Π是一个判定问题,如果问题Π属于NP类问题,并且对NP类问题中每一个问题Π’,都有 Π ∝ p o l y Π ′ Π∝polyΠ' Π∝polyΠ′,则称判定问题Π是一个NP类完全问题。
- NP完全问题是NP类问题中最难的一类问题;
- 如果某一个NP完全问题能在多项式时间内解决,则每一个NP完全问题都能在多项式时间内解决;
- 这些问题也许存在多项式时间算法,也可能不存在多项式时间算法;
-
如何证明一个问题是NP完全问题
- 证明问题Π是NP类问题
- 证明NP类问题中的每一个问题都能在多项式时间内变换为问题Π
-
已经证明的NP完全问题:SAT问题,最大团问题,图着色问题,哈密顿回路问题,旅行商问题,背包问题,最长路径问题,扫雷游戏····
NP难问题
- 难解问题中还有一类问题,虽然也能证明所有NP类问题,可以在多项式时间内变换到问题Π,但并不能证明Π也是NP类问题,所以Π不是NP完全的,但问题Π至少与任意NP类问题有同样的难度,这样的问题是NP难问题
NPI类问题
- NP类问题中还有一些问题,人们不知道是属于P类还是输于NP完全问题,还有待于证明其归属;
- 这些问题是NP完全问题的可能性非常小,也因为不相信他们在P中,我们人为地增加另一问题类来接纳这类问题,这个类被称为NPI类
- NPI类是一个人为定义的,动态的概念,随着人们对问题研究的深入,许多NPI类问题逐渐被明白无误地证明他们原本属于P类问题或NP类完全问题
- 例如:线性规划问题,素数判定问题
NP完全问题的处理技术简介
NP完全问题是计算机难以处理的,但是实际中经常遇到,因此人们提出了解决完全问题的各种方法:
- 采用先进的算法设计技术
- 近似算法
- 随机算法
- 并行计算
- 智能算法
十三章复习
回溯法
-
有组织的穷尽搜索
- 有明确的的适用对象
- 有明确的的设计步骤
-
例:着色问题
- V={v
1
,v2
,…,vn
} - c
i
={1,2,3} //颜色集合 - 每种着色方法:(c
1
,c2
,…,cn
); - 递归算法:
for(k=1;k<=n;k++){ c[k]=0; } flag = false; graphcolor(1); if(flag){ output c; } else output"no solution";
void graphcolor(k){ for(color=1;color<=3;color++){ c[k]=color; if(c为合法着色){ set flag<-true exit; } else{ if(c是部分着色){ graphcolor(k+1); } } } }
- 迭代算法:
for(k=1;k<=n;k++){ c[k]=0; } flag =false; k=1; while(k>=1){ while(c[k]<=2){ c[k]=c[k]+1; if(c为合法着色){ flag=true; exit; } else{ if(c为部分着色){ k=k+1; } } } c[k]=0; k=k-1; } if(flag){ output c; } else output"no solution";
- V={v
-
n皇后问题:
-
当n=4时:
- 解的表示形式{x
1
,x2
,x3
,x4
}; - 互相攻击的情况:
- x
i
=xj
; - x
i
-xj
=i-j;或者xi
-xj
=j-i;
- x
- 解的表示形式{x
-
递归算法:c[1,…,4]表示存放位置
- 主程序:
for(k=1 to 4) c[k]=0; flag =false; Rec4-queens(1); if(flag= true) output c[]; else output"no solution"
Rec4-queens(k){ for(col=1 to 4){ c[k]=col; if(c全合法){ flag = true; exit; } else{ if(c部分合法){ Rec4-queens(k+1); } } } }
-
迭代递归的算法:
- 算法过程;
for(k=1 to 4) c[k]=0; flag = false; k=1; while(k>=1){ while(c[k]<=3){ c[k]=k+1; if(c全合法){ flag = true; exit; } else{ if(c部分合法){ k=k+1; } } } c[k]=0; k=k-1; } if(flag= true) output c[]; else output"no solution"
-
简要分析:
- 蛮力分析: O ( n n ) O(n^n) O(nn)或者 O ( n ! ) O(n!) O(n!)
- 回溯法:经验上的期望运行时间少
-
-
回溯法适用于解决什么类型问题?
- 解的形式是定长向量
- 算法框架:
初始化 c[n]; flag = false; 回溯过程:递归/迭代 if(flag= true) output c[]; else output"no solution"
- 递归回溯:
advance(k){ for(每个x∈Xk){ c[k]=x; if(c合法){ flag=true; exit; } else{ if(c部分合法){ advance(k+1); } } } }
- 迭代回溯:
k=1; while(k>=1){ while(Xk尚未被穷举){ c[k]=Xk的下一个元素; if(c全合法){ flag = true; exit; } else{ if(c部分合法){ k=k+1; } } } 重置c[k]; k=k-1; }
第十四章复习
随机算法的分类
-
数值计算:pi的计算;
- 算法呈现
double Darts(int n){ int k=0; for(int i=1;i<=n;i++){ double x = Random(-1,1); double y = Random(-1,1); if((x*x+y*y)<=1){ k++; } } return 4*k/(double)n; }
-
拉斯维加斯算法:回溯法;
- n皇后问题
-
蒙特卡罗算法:多数元,素性测试;
- 素性测试
-
舍伍德算法:快排,元素选择;
- 快排算法呈现:
rquicksort(low,high){ if(low<high){ v=random(low,high); 互换A[low]和A[v]; SPILT(A[low···high],w); rquicksort(low,w-1); rquicksort(w+1,high); } }
-
随机算法的分析:每个固定实例上的期望运行时间;