算法设计与分析实验报告
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
算法设计与分析报告
学生姓名
学号
专业班级
指导教师
完成时间
目录
一、课程内容 (3)
二、算法分析 (3)
1、分治法 (3)
(1)分治法核心思想 (3)
(2)MaxMin算法分析 (3)
2、动态规划 (4)
(1)动态规划核心思想 (4)
(2)矩阵连乘算法分析 (5)
3、贪心法 (5)
(1)贪心法核心思想 (5)
(2)背包问题算法分析 (6)
(3)装载问题算法分析 (6)
4、回溯法 (7)
(1)回溯法核心思想 (7)
(2)N皇后问题非递归算法分析 (7)
(3)N皇后问题递归算法分析 (8)
三、例子说明 (9)
1、MaxMin问题 (9)
2、矩阵连乘 (9)
3、背包问题 (10)
4、最优装载 (10)
5、N皇后问题(非递归) (11)
6、N皇后问题(递归) (11)
四、心得体会 (11)
五、算法对应的例子代码 (12)
1、求最大值最小值 (12)
2、矩阵连乘问题 (13)
3、背包问题 (14)
4、装载问题 (17)
5、N皇后问题(非递归) (18)
6、N皇后问题(递归) (20)
一、课程内容
1、分治法,求最大值最小值,maxmin算法;
2、动态规划,矩阵连乘,求最少连乘次数;
3、贪心法,1)背包问题,2)装载问题;
4、回溯法,N皇后问题的循环结构算法和递归结构算法。
二、算法分析
1、分治法
(1)分治法核心思想
当要求解一个输入规模为n,且n的取值相当大的问题时,直接求解往往是非常困难的。
如果问题可以将n个输入分成k个不同子集合,得到k个不同的可独立求解的子问题,其中1<k≤n,而且子问题与原问题性质相同,原问题的解可由这些子问题的解合并得出。
那末,这类问题可以用分治法求解。
分治法的核心技术
1)子问题的划分技术。
2)递归技术。
反复使用分治策略将这些子问题分成更小的同类型子问题,直至产生出不用进一步细分就可求解的子问题。
3)合并技术。
(2)MaxMin算法分析
问题:在含有n个不同元素的集合中同时找出它的最大和最小元素。
分治策略设计思想:
将任一实例I=(n,A(1),…,A(n))分成两个实例如果MAX1和MIN1是I1中的最大和最小元素,MAX2和MIN2是I2中的最大和最小元素,MAX1和MAX2中的大者就是I中的最大元素MAX,MIN1和MIN2中的小者是I中的最小元素MIN。
如果I只包含一个元素,则不需要作任何分割直接就可得到其解。
核心算法如下:
procedure MAXMIN(i,j,fmax,fmin)
global n,A[1:n]
case
{ i =j : fmax ←fmin ←A[i] /*只有一个元素*/
i =j-1:if A[i] <A[j] then /*两个元素*/
fmax ←A[j ] ;fmin ←A[i]
else fmax ←A[i] ;fmin ←A[j]
else :mid ← /*分成两部分*/
MAXMIN(i,mid,max1,min1)
MAXMIN(mid+1,j,max2,min2)
fmax ←max(max1,max2)
fmin ←min(min1,min2)
}
MAXMIN 的时间复杂性分析
用T(n)表示比较次数,则所导出的递归关系式:
⎣⎦⎡⎤⎪⎩
⎪⎨⎧++=2)2/()2/(1
0)(n T n T n T
当n 是2的幂时,即对于某个正整数k ,n=2k ,有
T(n)=2T(n/2)+2
=2(2T(n/4)+2)+2
=4T(n/4)+4+
=2k-1T(2)+2k-1+……+2
=2k-1+2k-2
=3n/2-2
对于任意 n ,存在整数k,有2k-1<n ≤2k, T(n) ≤ 3n/2-2 2、动态规划
(1)动态规划核心思想
动态规划法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,但是经分解得到的子问题往往不是互相独立的。
用分治法求解时,有些子问题被重复计算了许多次。
如果能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,就可以避免大量重复计算,从而得到多项式时间算法。
设计动态规划法的步骤:
1、划分阶段(子问题),分析最优子结构性质。
2、找出阶段间的最优决策的递推关系,写出动态规划方程;
3、设计自底向上计算出最优值的步骤。
4、从最终决策的回溯子问题的决策,构造一个最优解。
(2)矩阵连乘算法分析
问题:给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2…,n-1。
如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。
两个矩阵相乘所需做的数乘次数为l*n*m,矩阵乘法满足结合律,故矩阵连乘有可以有许多不同的计算顺序。
计算顺序由加括号的方式确定,加括号的方式决定了矩阵连乘的计算量。
核心算法如下:
依次计算各层的子问题集
r=1,初始化
for i ←1 to n do
m[i][i] ←0
从r=2至r=n
for r←2 to n do
计算所有大小为r的子问题
MatrixChain(p[0:n],n,m[1:n][1:n],s [1:n][1:n])
{ 1) for i ←1 to n do //初始化
m[i][i] ←0
2) for r ←2 to n do // 子问题由小到大
3) for i ←1 to n - r+1 do //子问题的起点
4) {j←i+r-1 //子问题的终点
5) m[i][j]←m[i+1][j]+ p[i-1]*p[i]*p[j] // 初值考虑
6) s[i][j] ←i // 记录分割点
7) for k←i+1 to j-1 do //求最好的分割k
8) { t←m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j];
9) if t < m[i][j] then
10) { m[i][j] ←t
11) s[i][j] ←k
}
} }
}
3、贪心法
(1)贪心法核心思想
顾名思义,贪心算法总是作出在当前看来最好的选择。
也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。
希望通过局部最优选择,达到全局的最优
作出贪心决策的依据称为贪心准则(greedy criterion)。
虽然贪心算法不能对所有问题都得到整体最优解,但对许多问题它能产生整体最优解。
贪心法一般方法
1)根据题意,选取一种量度标准。
2)按这种量度标准对这n 个输入排序,
3)依次选择输入量加入部分解中。
如果当前这个输入量的加入,不满足约束条件,则不把此输入加到这部分解中。
(2)背包问题算法分析
问题:已知有n 种物品和一个可容纳M 重量的背包,每种物品i 的重量为wi 。
假定将物品i 的一部分xi 放入背包就会得到pixi 的效益,这里,0≤xi ≤1,pi>0。
如果这些物品重量的和大于M ,要求所有选中要装入背包的物品总重量不得超过M ,而装入背包物品获得的总效益最大。
即
,m ax
1i n i i x p ∑≤≤M
x w i n i i =∑≤≤1
满足约束条件的任一集合(x1,…,xn)是一个可行解(即能装下),使目标函数取最大值的可行解是最优解。
核心算法如下:
用利润/重量为量度
即每一次装入的物品应使它占用的每一单位容量获得当前最大的单位效益。
这就需使物品的装人次序按pi/wi 比值的非增次序来考虑。
procedure KNAPSACK(real P ,W ,M ,X ,n)
{ //P(1:n)和W(1:n)分别是n 个物品的重量,它们已按
P(i)/W(i)≥P(i+1)/W(i+1)排序,x(1:n)是解向量//
real cu ;int i ,n ;
X ←0 //将解向量初始化为零//
cu ←M //cu 是背包剩余容量//
for i ←1 to n do
{if W[i]>cu then break
X[i] ←1
cu ←cu-W[i]
}
if i ≤n then X[i] ←cu/ W[i]
}
(3)装载问题算法分析
问题:有一批集装箱要装上一艘载重量为M 的轮船。
其中集装箱i 的重量为Wi 。
最优装载问题要求确定在装载体积不受限制的情况下,将尽可能多的集装箱装上轮船
设装载入船的集装箱的子集为S
max{∑i | i ∈S,1≤i ≤n}
约束∑w[i] ≤M ,1≤i ≤n, i ∈S
核心算法如下:
采用重量最轻者先装的贪心选择策略
Loading( M, n)
global W[1..n],X[1..n]
// W[1..n] 为非递减序
X ←0 // 解向量清0
cu ←M
for i←1 to n do
{ if cu<W[i] then
break
X[i] ←1
cu ←cu -W[i]
}
4、回溯法
(1)回溯法核心思想
回溯法是一种组织得井井有条的,能避免不必要搜索的穷举式搜索法。
这种方法适用于解一些组合数相当大的问题。
回溯法在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树。
算法搜索至解空间树的任意一点时,先判断该结点是否满足约束。
如果不满足,则跳过对该结点为根的子树的搜索,逐层向其祖先结点回溯;否则,进入该子树,继续按深度优先策略搜索。
回溯法的一般步骤:
(1)针对所给问题,定义问题的解空间;
(2)确定易于搜索的解空间结构;
(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。
(2)N皇后问题非递归算法分析
问题:n皇后问题可以表示成n-元组(x1,…,xn),其中xi是放在第i行的皇后所在的列号。
于是,解空间由nn个n-元组组成。
显示约束条件为Si={1,2,…….,n},1i n。
隐式约束条件之一为没有两个xi相同(即任意两个皇后不在同一列上)。
将其加入到显式条件中,于是解空间的大小由nn个元组减少到n!个元组。
核心算法如下:
procedure NQUEENS(n)
X(1)←0;k←1 //k是当前行;X(k)是当前列//
While k>0 do //对所有的行执行以下语句//
1) { X(k)←X(k)+1 //移到下一列//
While X(k)≤n and not PLACE(k) do
2) X(k)←X(k)十l
if X(k)≤n //找到一个位置//
then if k=n //是一个完整的解吗//
then print(X) //是,打印这个数组//
3) else {k←k+1;X(k)←0;}
4) else k←k-1 //回溯//
}
end NQUEENS
测试X(K)是否合理
procedure PLACE(k)
//如果一个皇后能放在第k行和X(k)列,则返
回true;否则返回false。
X是一个全程数
组,进入此过程时已置了k个值。
//
global X(1:k);integer i,k
i←1
while i<k do
if X (i)=X(k) //在同一列有两个皇后//
or ABS(X(i)-X(k))=ABS(i-k)
//在同——条斜角线上//
then return(false)
endif
i←i+1
repeat
return(true) //满足约束//
end PLACE
(3)N皇后问题递归算法分析
使用递归算法使,反复的调用判断函数,判断后在决定位置。
核心算法如下:
public void setposition(int n) //n表示在第n行,table[n]表示列数
{
for(int i = 0; i <= 5; i++) //i表示在第i列上放置皇后
{
x[n] = i;
if(place(n)==false ) //如果没有皇后在同一列或斜线上(因为是按行依次放
置故不可能在同一行上,又因为是每一行是从左到右放置故不可能出现右斜线上有皇后){
if(n == 5) //棋盘满时输出方案
{
count++;
System.out.println("第" + count + "种解:");
for(int j=0;j<=5;j++){
System.out.print(x[j]+1+" ");
}System.out.println("");
//print();
}
else //一个棋盘未放满时继续放置
setposition(n + 1);
}
}
}
三、例子说明
经上机操作,各个经典问题的算法实现实现如下:
1、MaxMin问题
比较10个数字的大小,10, 23, 45, 11, 757, 2, 1236, 768, 1,-9
比较得出最大值为1236,最小值为-9。
2、矩阵连乘
给定6个矩阵A(30*35),B(35*15),C(15*5),D(5*10),E(10*20)G(20*25)。
3、背包问题
给定3个背包:三个物品的价值分别为60,120,100,三个物品的质量分别是10,30,20,背包容量为50。
得到装入物品的比例分别为1,2/3,1,总重量为50,最大价值为240.
4、最优装载
船的称重量为20,给定10个物品,重量分别是:
1.0f,5.0f,
2.0f,2.0f,4.0f,8.0f,
3.0f,1.0f,6.0f,2.0f
5、N皇后问题(非递归)
给定4皇后问题,得到4种解。
6、N皇后问题(递归)
给定6皇后问题,得到4种解。
四、心得体会
学习了栾老师的算法课,解决很多问题,老师说程序=算法+数据结构,首先老师教的知识对于逻辑思维能力有很大帮助,它可以培养我们思考分析问题能力,所谓算法简单来说,就是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,,之前学习,学习了算法的时间空间复杂度分析,之后讲的就是分治法,任何一个可用计算机求解的问题所需要的计算时间都与其规模有关.问题的规模越小,越容易直接求解,解题所需要的时间也越少,分治法的设计思想就是将一个难以解决的大问题,分割成为规模小的问题,分别解决.之后有动态规划问题各种问题,算法是一门基础学科应该学好.对于自己在以后的学习会有很大的帮助.
五、算法对应的例子代码
1、求最大值最小值
/*使用分治法求最大值最小值T(n)=3n/2-2*/
public class MaxMin {
static int count=0;
public static void main(String args[]) {
//实例化对象
MaxMin maxmin = new MaxMin();
//创建数组
int[] array = new int[] { 10, 23, 45, 11, 757, 2, 1236, 768, 1,-9 };
//取得最小值
int max = maxmin.getMax(array, 0, array.length-1);
int min = maxmin.getMin(array,0,array.length-1);
//输出
System.out.println("最大值是:"+max);
System.out.println("最小值是:"+min);
}
/* 求最大值*/
public static int getMax(int[] array, int i, int j) {
int Max1 = 0;
int Max2 = 0;
if (i == j) {
return Max1 = Max2 = array[j];
} else if (i == (j - 1)) {
Max1 = array[i];
Max2 = array[j];
return Max1 > Max2 ? Max1 : Max2;
}
else {
int mid = (i + j) / 2;
Max1 = getMax(array, i, mid);
Max2 = getMax(array, mid, j);
// count++;
//System.out.println(count);
return Max1 > Max2 ? Max1 : Max2;
}
}
/*求最小值*/
public static int getMin(int[]array,int i,int j){
i nt Min1=0;
i nt Min2=0;
i f(i==j){
return Min1=Min2=array[j];
}else if(i==(j-1)){
Min1=array[i];
Min2=array[j];
return Min1>Min2?Min2:Min1;
}else{
int mid=(i+j)/2;
Min1=getMin(array,i,mid);
Min2=getMin(array,mid,j);
return Min1>Min2?Min2:Min1;
}
}
}
2、矩阵连乘问题
/*矩阵连乘问题,i,r,k的三重循环,时间复杂度为O(n立方)*/
public class MatrixChainOrder {
int[] p;//矩阵维数
int[][] m;//最少乘次数
int[][] s;//分割点
int length;
public MatrixChainOrder(int[] p,int[][] m,int[][] s){ this.p = p;
this.length = p.length/2;
this.m = m;
this.s = s;
init();
clac();
printM();
}
public void init(){ //m初始化
for (int i=0;i<length;i++){
m[i][i] = 0;
}
}
public void clac(){
for (int i=1;i<length;i++){
for (int j=0;j<length-i;j++){
int r = j+i; //重复计算子问题,长度由1,到length
int t = Integer.MAX_VALUE; //定义当前最大值
for (int k = j;k<r;k++){
int temp = m[j][k] + m[k+1][r] + p[j*2]*p[k*2+1]*p[r*2+1];
if (t > temp){
t = temp;
m[j][r] = temp;
}
}
}
}
}
public void printM(){
for (int i=0;i<length;i++){
for (int j=0;j<length;j++){
System.out.print(m[i][j]+ "\t");
}
System.out.println();
}
}
public static void main(String args[]){
int p[] = {30,35,35,15,15,5,5,10,10,20,20,25};
int length = 6;
int[][] m = new int[6][6];
int[][] s = new int[6][6];
new MatrixChainOrder(p,m,s);
}
}
3、背包问题
/* 利用贪心算法,对背包问题的java实现*/
public class Knapsack {
private float m ; //背包容量
float[] v ; //三个物品的价值
float[] w ; //三个物品的质量
float[] x ; //往背包装东西的比例
int n ; //物品个数
double[] p_w_v ; //每个物品的单位重量价值
public Knapsack(){
this.m = 50.0f ; //背包容量为50
this.v = new float[]{60.0f,120.0f,100.0f} ; //三个物品的价值分别为60,120,100
this.w = new float[]{10.0f,30.0f,20.0f,} ; //三个物品的质量分别是10,30,20,
this.x = new float[3]; //往背包装东西的比例
this.n = 3 ; //三个物品
this.p_w_v = new double[n] ; //每个物品的单位重量价值
}
//对物品的单位重量价值进行排序
public void sort(int n , float[] v ,float[] w)throws Exception{
for (int i = 0 ; i < n ; i++){
p_w_v[i] = v[i] / w[i] ;//单位重量价值
}
print (p_w_v);
System.out.println ("");
Sort(p_w_v,w,v) ;
print (p_w_v);
}
public void print (double a[]){//打印输出数组
int len = a.length;
for (int i = 0; i < len; i++){
System.out.print(a[i] + " ");
}
}
public void print (String str){//打印输出数组
System.out.print(str + "\n");
}
public void Sort(double[] a , float[] b , float[] c){//冒泡排序,实现排序功能
int len = a.length;//获得数组长度
double temp;//临时变量,用于交换值
float w_temp ;
float v_temp ;
for (int i = 0; i < len-1; i++){ //通过循环实现主要算法
for (int j = 0; j < len-1-i; j++){
if (a[j+1] > a[j]){ //如果后一下值比前一个值大,则交换两个值的大小,
//以下几行代码是实现数值交换的
temp = a[j+1]; //从大到小排序
w_temp = w[j+1];
v_temp = v[j+1];
a[j+1] = a[j];
w[j+1] = w[j];
v[j+1] = v[j];
w[j] = w_temp;
v[j] = v_temp;
}
}
}
}
//贪心算法核心思想
public void knapsack()throws Exception{
sort (n , v , w) ;
int i ;
for (i = 0 ; i < n ; i++){
x[i] = 0 ;
}
float c = m ;
for (i = 0 ; i < n ; i++){
if (w[i] > c){
break ;
}
x[i] = 1 ;
c -= w[i] ;
}
if (i < n){
x[i] = c / w[i] ;
}
print ("\n物品一可以装入的比例: " + x[0]);
print ("物品二可以装入的比例: " + x[1]);
print ("物品三可以装入的比例: " + x[2]);
print ("可以装入的最大价值为: " + (x[0]*v[0] + x[1]*v[1] + x[2]*v[2]));
print ("可以装入的最大重量为" + (x[0]*w[0] + x[1]*w[1] + x[2]*w[2])); }
public static void main (String[] args)throws Exception{
Knapsack ksp = new Knapsack();
ksp.knapsack();
}
}
4、装载问题
/*装载问题,贪心法,时间复杂度上界函数为O(n方)*/
public class Loading {
private float m;//船的称重量
private float w[];//物品的质量
int[]x;//物品的选择
int n;//物品个数
public Loading(){
this.m=20.0f;//船的称重量为20
this.n=9;//10个物品
this.w=new float[]{1.0f,5.0f,2.0f,2.0f,4.0f,8.0f,3.0f,1.0f,6.0f,2.0f};//10个物品的重量this.x=new int[n];
System.out.println("给定的10个可选货物为:");
for(int i=0;i<w.length;i++){
System.out.print(w[i]+" ");
}
System.out.println("");
}
//对物品按照重量进行排序
public void sort(){
float temp=0f;
for(int i=0;i<n;i++){
for(int j=0;j<n-i;j++){
if(w[j]>w[j+1]){
temp=w[j];
w[j]=w[j+1];
w[j+1]=temp;}
}
}
System.out.println("排序后的数据为:");
for(int i=0;i<w.length;i++){
System.out.print(w[i]+"");
}
}
public void loading(){
int i;
System.out.println();
System.out.println("贪心选择为:");
for(i=0;i<n;i++){
x[i]=0;
}
for(i=0;i<n;i++){
if(w[i]>m){
break;
}
x[i]=1;
m=m-w[i];
if(m<0){
break;
}
System.out.print(w[i]+" ");
}
}
public static void main(String[]args){
Loading load=new Loading();
load.sort();
load.loading();
}
}
5、N皇后问题(非递归)
/*回溯法N皇后问题,非递归算法,时间复杂度为O(n!)*/
public class Queen1 {
static int count = 0;
static int n = 6;
static int x[] = new int[n+1];
void backtrack() {
int k = 1;
while (k > 0) {
x[k] += 1; // 第k列皇后向下移一行
while ((x[k] <= n) && !(place(k))) { // 如果当前第k列皇后未出界或者和其他皇后冲突
x[k] += 1; // 第k列皇后向下移一行继续寻找
}
if (x[k] <= n) { // 找到一个值并且未出界
if (k == n) { // 已是最后一列说明已找到一个方案
count++;
System.out.println("第" + count + "种解:");
for(int i=1;i<=k;i++) { //打印寻找策略
System.out.print(x[i]+" ");
} System.out.println();
}
// print(); }
else { // 不是最后一列故寻找下一列
k++;
x[k] = 0;
}
} else
// 找到的值已经出界,回退到上一列
k--;
}
}
// 判断皇后是否冲突
boolean place(int k) {
for (int j = 1; j < k; j++) {
if ((Math.abs(j - k) == Math.abs(x[j] - x[k])) || (x[j] == x[k])) return false;
}
return true;
}
public static void main(String[] args) {
for(int i = 0;i<n;i++){
x[i] = 0;
}
Queen1 huanghou = new Queen1();
huanghou.backtrack();
}
}
6、N皇后问题(递归)
/*回溯法N皇后问题,递归算法*/
public class Queen
{
private int [] x = new int [6];
private int count = 0;
public Queen()
{
setposition(0);
}
public void setposition(int n) //n表示在第n行,table[n]表示列数
{
for(int i = 0; i <= 5; i++) //i表示在第i列上放置皇后
{
x[n] = i;
if(place(n)==false ) //如果没有皇后在同一列或斜线上(因为是按行依次放置故不可能在同一行上,又因为是每一行是从左到右放置故不可能出现右斜线上有皇后)
{
if(n == 5) //棋盘满时输出方案
{
count++;
System.out.println("第" + count + "种解:");
for(int j=0;j<=5;j++){
System.out.print(x[j]+1+" ");
}System.out.println("");
//print();
}
else//一个棋盘未放满时继续放置
setposition(n + 1);
}
}
}
public boolean place(int n)
{
for(int i = 0; i < n; i++)
{
if(x[i] ==x[n] || Math.abs(x[i] - x[n]) == Math.abs(i - n))
//当放置的皇后同一列上含有皇后或左斜线上含有皇后时,则返回1
return true;
}
return false;
}
public static void main(String args[])
. . . .
{
new Queen();
}
}
. . 资.料. ..。