用分治算法求数组最小的两个元素的算法错误分析与改正

来源:互联网 发布:siri算不算人工智能 编辑:程序博客网 时间:2024/05/18 14:22

以下代码是书上151页例17求数组最小的两个元素的算法

总体思路是用分治算法,把数组分成两个子集,在两个子集中选择最小的两个元素,然后再把两个集合中,选出来的四个数选出较小的两个数.这样就把大问题分解成两个容易解决的小问题了.直到回溯结束就可以得到时原问题的解.

书上的算法如下:

#include <stdio.h>#include <algorithm>int a[100];int second(int n){void two(int ,int,int&,int&);int min2=0,min1=0;two(0,n-1,min2,min1);return min2;}void two(int i,int j,int &fmin2,int &fmin1){int lmin2,lmin1,rmin2,rmin1;int mid;if (i == j){fmin2 = fmin1 = a[i];} else if (i == j-1){if (a[i]<a[j]){fmin2 = a[j];fmin1 = a[i];}else{fmin2 = a[i];fmin1 = a[j];}}else{mid = (i+j)/2;two(i,mid,lmin2,lmin1);two(mid+1,j,rmin2,rmin1);if (lmin1 < rmin1){if (lmin2<rmin1){fmin1 = lmin1;fmin2 = lmin2;}else{fmin1 = lmin1;fmin2 = rmin1;}}else{if (rmin2<lmin1){fmin1 = rmin1;fmin2 = rmin2;}else{fmin1 = rmin1;fmin2 = lmin2;}}}}void main(){for (int i=0;i<10;i++){a[i]=10-i;}for (i=0;i<10;i++){std::random_shuffle(a,a+10);for (int j=0;j<10;j++){printf("%d ",a[j]);}printf("------第二小的数是%d\n",second(10)); }}

main函数中用不通的数据测试时,发现算法出错了.十次运算中,有五次结果是错的.出了几次4,出现了几次1.

通过检查,没有什么书写错误.

然后就开始思考是不是逻辑错误.

观察递归的结束条件,i==j和i==j-1;就是说最把问题分解成最小的问题是要处里的数组中只一个或者两个元素.

当只有一个元素时,最个两个数的返回值被赋予一样的值.

然在在回溯到时上一层时,本来只有一个元素的返回出了两个元素.

由于有一个元素返回两个元素的值,把问题的解合并时,看起来是从四个元素中选出两个最小的,实际上只是从三个元素中选出两个最小的,或都从丙个元素中选出两个最小的.

这样就会出现错误的结果

解题的分治算法思路还是正确的,只是算法的最小可解问题选择错误了.

我对算法进行了改写,当数组中只有两个元素时或都有三个元素时进行递归的回溯.

这样就不会出现上面的问题.

只中会出现,求解两个元素的最小两个数的过程,

这个不像从四个数中选择出两个最小的.

那四个数分两组,每组中是有大小之分.所以只有四种情况.

而从三个数中找出最小的两个数,是没有顺序的,所以穷举有六种情况.

然后解决了最小的元问题.回溯,就可以得到结果.

改写后的代码如下:

#include <stdio.h>#include <algorithm>int a[20];void min3(int &x,int &y,int a,int b,int c);void two(int i,int j,int &fmin2,int &fmin1){int lmin2,lmin1,rmin2,rmin1;int mid;if ((j-i) ==1){if (a[i]>a[j]){fmin1 = a[j];fmin2 = a[i];}else{fmin2 = a[j];fmin1 = a[i];}} else if ((j-i)==2){min3(fmin2,fmin1,a[i],a[i+1],a[j]); }else{mid = (i+j)/2;two(i,mid,lmin2,lmin1);two(mid+1,j,rmin2,rmin1);if (lmin1 > rmin2){ fmin1 = rmin1;fmin2 = rmin2;}else if (rmin1 > lmin2){fmin1 = lmin1;fmin2 = lmin2;}else if (lmin1 > rmin1){fmin1 = rmin1;fmin2 = lmin1;}else{fmin1 = lmin1;fmin2 = rmin1;}}}void main(){int n=20;for (int i=0;i< n;i++){a[i]=n-i;}int c,b;//测试数据,循环10每次数组数据不同for (i=0;i<10;i++){std::random_shuffle(a,a+n);for (int j=0;j<n;j++){printf("%d ",a[j]);}two(0,n-1,c,b);printf("--------%d,%d\n",c,b);  }}void min3(int &x,int &y,int a,int b,int c)//求三个数中最小的两个。{if (a>b && a>c){if (b>c){y=c;x=b;}else{y=b;x=c;}}else if (b >a && b>c){if (a>c){y=c;x=a;}else{y=c;x=a;}}else{if (a>b){y=b;x=a;}else{y=a;x=b;}}}

写写总结吧,从发现问题,找到问题,解决问题真花了不少时间.

遇到这种的问题,一定要理清思路,想清程序是怎么运行的,才能在其中发现漏洞.

从分治的思想上,先是分解问题,分解得的没有问题,然后再看分解的原子问题的解有没有问题,各个原子问题之间有没有联系.

然后再看把原子问题的解合并成原问题的解,合并过程中有没有出问题.