前言:
这段时间晚上都在做实习的工作,所以学习的进度有点慢了下来。不过基础是非常重要的,所以一定不能把基础给拉下,平时挤时间出来也得好好看书学习。
这本书现在到了第十章,还有两章。本来打算9月底一定要弄完这本书的,不过发生了太多的事情,但是还是要尽全力往前赶。
我的github:
我实现的代码全部贴在我的github中,欢迎大家去参观。
https://github.com/YinWenAtBIT
介绍:
一、贪婪算法:
一、定义:
贪婪算法分阶段的工作,每一个阶段,都选择当前能选择的最好的选择,不考虑将来的后果。每次的选择都是局部最优,期望最后的结果能达到全局最优,它的策略就是它名字的来由。
二、贪婪算法的应用:
1. Dijkstra算法,在图论中使用的最短路径优先算法就是贪婪算法,每次选择当前可见的路径中具有最低加权的边。即是使用了贪婪策略。
2.Prim算法,图论中最小生成树。每次选择与原有树相连的具有最小加权的边,同样是贪婪策略。
3. Kruskal算法,同样是求最小生成树,使用贪婪策略。
这几个算法我已经在之前的学习中学习过了,并且全部实现出来了。接下来是这一章中提及的新的算法
4.调度问题;
这个问题是要得到最短的平均完成时间,在单处理器情况下,优先调用耗时最短的任务即可。
在多处理器中,采用同样的策略可以得到最短的平均完成时间,但是如果要是的最后完成时间最小,这就会变成一个NP完全问题,这个问题就不再是可以简单处理的问题了。这个问题,难的太难,简单的太简单,没有什么编码实现的必要,直接略过了。
5.Huffman编码
这个Huffman编码说起来可就真的有历史了,本科的数据结构课程就学习过了,不过当时我只会背诵这个算法,对于如何真正去实现它一筹莫展,直到上个学期,我学习图像处理课程,其中也要求使用matlab来完成Huffman编码,当时我也是不明白怎么做,看了一篇别人的博客,发现用链表实现特别好,并且突然发现链表原来是这么用的。。最后那个作业还是抄的别人的,因为我不知道怎么在matlab里面写一个链表。
今天再来看这个算法,只不过是生成一颗树而已,先把所有的节点都加入优先队列,每次取出两个来组成一个新的节点再加入队列,时间复杂度O(N*logN),N代表需要编码的元素。或者直接使用链表来写,O(N^2)的复杂度。书上没有给出伪代码,我也觉得没有必要。通过这么一段时间的训练,我感觉这样的算法再写出来也基本是浪费时间而已了。直接手写,使用以前编写好的优先队列或者是链表,估计20分钟就完成了。不过现在连20分钟都不愿意花在这上面了。感觉自己这半年来进步已经非常的大了。
6.装箱问题
这个问题主要的难点在于联机算法与脱机算上。
联机算法代表一次只能知道一个物品大小的的信息,处理完一个物品的装箱,才知道另外一个物品的信息。所以在这种情况下,最优解是很难达到的,此时能到到的最优解为4/3倍与最优解的箱子数。不过达到这个限度的算法并没有。
使用贪婪策略的两个算法:首次适合法和最佳适合法,都可以达到17/10最优箱子的解。实现这个算法可以使用链表,或者是优先队列。
脱机时,就是知道所有的物品大小信息,然后进行装箱,能达到11/9最优。这里只进行了证明,并没有给出可行的算法。
三、编码实现:
这一块需要进行新的编码的地方基本没有,huffman编码现在对我来说已经太简单,不用再浪费时间了。
二、分治算法:
一、定义:
分治算法由两部分组成:分:递归解决较小的问题
治:从子问题的解构建原问题的解
传统上,在正文至少含有两个递归调用的例程叫做分治算法,我们一般坚持子问题是不相交的(即基本不重叠)。
二、分治算法的应用:
1.最大子序列,这个问题可以把序列分成两部分,最大的序列出现在左边,或者右边,或者中间。时间复杂度O(N*logN)
2.归并排序与快速排序,都是使用分治思想,算法复杂度O(N*logN)
3.最近点问题,同样是使用分治算法,可以根据x坐标将排好序的点分成左右两部分,然后最近点位于左边,右边,或者中间。
三、分治算法的运行时间:
对于方程的:
T(N) =aT(N/b)+O(N^k)
的解为:
T(N) = O(N^(lga/lgb)) a>b^k时
= O(N^(N^k*logN)) a=b^k时
= O(N^(N^k)) a
这里原方程代表处理算法的复杂度方程,其中a为划分之后的子问题个数,b是子问题相对于原问题的大小的倒数,最后一项是每次递归需要做的额外工作。
接下来将详细讲解如何求二维最近点问题:
三、最近点问题:
1问题模型:
给定平面上n个点,找其中的一对点,使得在n个点的所有点对中,该点对的距离最小。严格地说,最接近点对可能多于1对。为了简单起见,这里只限于找其中的一对。
2.一维最近点问题:对于一维的最近点问题,可以采用分治法,做法是排序之后,从中间分开,最近点在左边,右边,或者横跨左右两部分。一维的分治法很好实现,算反复杂度O(N*logN)。实际上,在头脑中调用这个递归过程,实际上很容易发现,实际上就是对相邻的两个点求距离,然后找到最近的那个。我们这里就可以利用动态规划的思想,直接对排好序的点,求相邻点的距离,排序的复杂度O(N*logN)。复杂度相同。
3.二维最近点问题:
当维度升高之后,就不能像之前那样直接求取相邻点的距离了,因为此时如果按照x坐标排序,那么y此时便不是按顺序排列的了。那么就没有相邻点的意义在这了。
那么此时,如何化简这个问题呢,做法还是利用分治法,先按照x排序,然后分开,左边,右边,中间。这样先把问题给化简。然后对于每一个最简的x区域,对这个区域内的所有点的距离进行计算,所以调用到最简时,计算的复杂度为O(N)。但是这时有一个缺陷。如果说所有的点都是均匀分布的,那么这个算法复杂度为O(N*logN),但是如果所有的点都落在了同一带内,那么这时候这个算法就失效了。如果此时能够把y也按照顺序排列,比较的时候就只用比较y“相邻”的点了。实际上是比较y之差小于epsilon的点。
算法过程:
预处理:把所有点先对x坐标进行归并排序,放入数组X中,再对X数组中的数据按照y排序,放入数组Y中
1.对输入的X数组的数据从中间分开,以中间点为界限,将Y数组按照中间的x分成左右两部分,放入数组Z中,左边部分x小于中间点的x坐标,右边相反。
2.递归求左右数组的最短距离;
3. 取左右数组最小距离,合并Z中的数组,然后对在左右最小距离里的点求取距离。
更加具体的解释,大家可以参考:http://blog.csdn.net/liufeng_king/article/details/8484284
4.编码实现二维最近点问题:
首先是类定义:
class PointX
{
public:
PointX(){}
bool operator <(PointX &P2){return x
预处理:
预处理之后直接调用了真正的处理函数,直接返回了处理的结果
void getNearDis(PointX X[], int N, PointX &a, PointX &b, float &d)
{
MergeSort(X, N);
PointY *Y = new PointY[N];
for(int i=0; i
分治处理核心:
这一部分处理代码比较长,也不太好懂,需要反复思考问题的模型之后才能比较明白。
void getClosest(PointX X[], PointY Y[], PointY Z[], int left, int right, PointX &a, PointX &b, float &d)
{
/*剩下两个点*/
if(right -left ==1)
{
d = getDis(X[left], X[right]);
a = X[left];
b = X[right];
return;
}
/*剩下三个点*/
if(right - left ==2)
{
float d1, d2, d3;
d1 = getDis(X[left], X[right-1]);
d2 = getDis(X[left], X[right]);
d3 = getDis(X[left+1], X[right]);
if(d1<=d2 && d1<=d3)
{
a=X[left];
b=X[right -1];
d=d1;
return;
}
if(d2<=d3)
{
a=X[left];
b=X[right];
d=d2;
}
else {
a=X[left+1];
b=X[right];
d=d3;
}
return;
}
int center = (left + right)/2;
int k = left;
int g= center +1;
/*将已经按Y排序的点,根据中线,分别放入中线左边和右边*/
for(int i = left; i <=right; i++)
{
if(Y[i].xID <= center)
Z[k++] = Y[i];
else
Z[g++] = Y[i];
}
PointX atmp, btmp;
float dtmp;
getClosest(X, Z, Y, left, center, a, b, d);
getClosest(X, Z, Y, center+1, right, atmp, btmp, dtmp);
if(dtmp< d)
{
a = atmp;
b = btmp;
d = dtmp;
}
Merge(Z, Y, left, center+1, right);
int f = left;
/*距离中心线距离在d内的点放入Z中,并且这些点是按照y坐标从小到大排序的 */
for(int i =left; i<=right; i++)
{
if(ABS(X[center].x - Y[i].x)
测试结果
这里只对二维最近点问题进行了编码,测试也只测试这个代码:
生成1000个随机的x,y坐标,算法很快就得到了答案。
总结:
这一次对于二维最近点问题的学习与编码,基本上让我对于分治算法有了更深入的理解了。想要想出一个好的分治算法,一定要先想清楚整个分与治的流程。想明白之后,再进行编码就容易多了。