码迷,mamicode.com
首页 > 其他好文 > 详细

经典算法宝典——贪婪思想(五)(1)

时间:2014-05-11 06:34:39      阅读:1444      评论:0      收藏:0      [点我收藏+]

标签:算法   模型   贪婪法   局部最优   全局最优   

贪婪法(Greedy)又叫登山法,它的根本思想是逐步到达山顶,即逐步获得最优解,是解决最优化问题时的一种简单但适用范围有限的策略。“贪婪”可以理解为以逐步的局部最优,达到最终的全局最优。

贪婪算法没有固定的算法框架,算法设计的关键是贪婪策略的选择。一定要注意,选择的贪婪策略要具有无后向性,即某阶段状态一旦确定以后,不受这个状态以后的决策影响。也就是说某状态以后的过程不会影响以前的状态,只与当前状态有关,也称这种特性为无后效性。因此,适应用贪婪策略解决的问题类型较少,对所采用的贪婪策略一定要仔细分析其是否满足无后效性。

说明:

贪婪算法策略在《数据结构》课程中的算法也有广泛的应用,如霍夫曼树、构造最小生成树的Prim算法和Kruskal算法的决策过程,都是使用的贪婪算法策略。

一、贪婪策略算法设计框架

1、贪心法的基本思路

从问题的某一个初始解出发逐步逼近给定的目标,每一步都作一个不可回溯的决策,尽可能地求得最好的解。当达到某算法中的某一步不需要再继续前进时,算法停止。

2、该算法适用的问题

贪婪策略面对问题只须考虑当前局部信息就要做出决策,也就是说适用贪婪算法的前提是”局部最优策略能导致产生全局最优解“。

3、该策略下的算法框架

从问题的某一初始解出发;
while(能朝给定总目标前进一步)
	利用可行的决策,求出可行解的一个解元素;
由所有解元素组合成问题的一个可行解。
4、贪婪策略选择

贪婪算法的原理是通过局部最优来达到全局最优,采用的是逐步构造最优解的方法。在每个阶段,都作出一个看上去最优的(在一定的标准下),决策一旦作出,就不可再更改。用贪婪算法只能解决通过局部最优的策略能达到全局最优的问题。因此,一定要注意判断问题是否适合采用贪婪算法策略,找到的解是否一定是问题的最优解。

贪婪算法是依靠经验或直觉,确定一个找最优解的决策,逐步决策获得问题的最优解。在一般情况下,选出最优决策标准是使用贪心设计求解问题的核心。要选出最优决策并不是一件容易的事情,因为最优决策对问题的适用范围可能是非常有限的。

因此,贪婪策略一定要精心确定,在使用之前,最好对策略的可行性进行数学证明。这里,不可能告诉大家一个通用的制定最优解的决策的标准,所以贪婪算法要谨慎使用。


二、可绝对贪婪问题

1、设计一个算法,把一个真分数表示为埃及分数之和的形式。所谓埃及分数,是指分子为1的分数。比如7/8 = 1/2 + 1/3 + 1/24。

数学模型:

将一个真分数F表示为A/B;做Bbubuko.com,布布扣A的整除运算,商为D,余数为K(0 < K < A),它们之间的关系及导出关系如下:

B = Abubuko.com,布布扣D + K

B/A = D + K/A < D + 1

A/B > 1/(D + 1)

记C = D + 1,这样就找到了分数F所包含的“最大的”埃及分数就是1/C。进一步计算:

A/B - 1/C = (Abubuko.com,布布扣C - B)/(Bbubuko.com,布布扣C)

也就是说继续要解决的是有关分子为A = Abubuko.com,布布扣C - B,分母为B = Bbubuko.com,布布扣C的子问题。

算法设计

(1)设某个真分数的分子为A(bubuko.com,布布扣),分母为B;

(2)把B除以A的商的整数部分加1后的值作为埃及分数的一个分母C;

(3)输出1/C;

(4)将A乘以C减去B作为新的A;

(5)将B乘以C作为新的B;

(6)如果A大于1且能整除B,则最后一个埃及分数的分母为B/A;

(7)如果A = 1,则最后一个分母为B;否则转到步骤(2)。

例子思想:

C = 8/7 + 1 = 2                   // 说明7/8 > 1/2

打印1/2,

A = 7bubuko.com,布布扣2 - 8 = 6                   // 在计算7/8 - 1/2 = (7bubuko.com,布布扣2 - 8)/(7bubuko.com,布布扣2) = 6/16 = A/B

B = Bbubuko.com,布布扣C = 16

C = 16/6 + 1 = 3                // 说明16/6 > 1/3

打印1/3,

A = 6bubuko.com,布布扣3 - 16 = 2,             // 在计算6/16 - 1/3 = (6bubuko.com,布布扣3 - 16)/(16bubuko.com,布布扣3) = 2/48 = A/B

B = Bbubuko.com,布布扣C = 16bubuko.com,布布扣3 = 48

A > 1但B/A为整数24,打印1/24结束。

算法实现:

#include<stdio.h>

void main()
{
	int a, b, c;
	printf("input element:\n");
	scanf("%d", &a);
	printf("input denominator:\n");
	scanf("%d", &b);
	if(a >= b)
		printf("input error");
	else if((a == 1) || (b % a) == 0)
		printf("%d/%d = 1/%d", a, b, b/a);
	else
		while(a != 1)
		{
			c = b/a + 1;
			a = a*c -b;
			b = b*c;
			printf("1/%d", c);
			if(a > 1)
				printf(" + ");
			if(b % a == 0 || a == 1)
			{
				printf("1/%d", b/a);
				a = 1;
			}
		}

		printf("\n");
}
结果输出:

bubuko.com,布布扣


三、相对或近似贪婪问题

1、币种统计问题

某单位给每个职工发工资(精确到元)。为了保证不要临时兑换零钱,且取款的张数最少,取工资前要统计出所有职工的工资所需各种币值(100,50,20,10,5,2,1元共7种)的张数。

算法设计:

(1)合理的情况每个的工资应该由文件读入,为了突出算法思想,还是从键盘输入每个的工资。

(2)为了能达到取款的张数最少,且保证不要临时兑换零钱,应该对每一个人的工资,用“贪婪”的思想,先尽量多地取大面额的币种,由大面额到小面额币种逐渐统计。

(3)7中面额无规律可循,而统计每个职工的工资的计算都要用到这7种币值,为了能构造出循环不变式,将7种币值存储在数组B。这样,7种币值就可表示为B[i],i = 1, 2, 3, 4, 5, 6, 7。为了能实现贪婪策略,7种币值应该从大面额的币种到小面额的币种依次存储。

(4)算法需要统计7种面额的数量,设置一个有7个元素的累加器数组S,这样操作就可以通过循环顺利完成了。

算法实现:

#include<stdio.h>

void main()
{
	int i, j, n, GZ, A, B[8] = {0, 100, 50, 20, 10, 5, 2, 1}, S[8] = {0, 0, 0, 0, 0, 0, 0, 0};
	scanf("%d", &n);
	for(i = 1; i <= n; i++)
	{
		scanf("%d", &GZ);
		for(j = 1; j <= 7; j++)
		{
			A = GZ/B[j];
			S[j] = S[j] + A;
			GZ = GZ - A * B[j];
		}
	}
	for(i = 1; i <= 7; i++)
		printf("%d: %d\n", B[i], S[i]);
}
结果输出:

bubuko.com,布布扣

说明:

(1)每求出一种面额所需的张数后,要把这部分金额减去“GZ = GZ - A * B[j];”,否则将会重复计算。

(2)在用贪婪算法策略时,最好能用数学方法证明每一步的策略是否能保证得到最优解。

经典算法宝典——贪婪思想(五)(1),布布扣,bubuko.com

经典算法宝典——贪婪思想(五)(1)

标签:算法   模型   贪婪法   局部最优   全局最优   

原文地址:http://blog.csdn.net/ssw_1990/article/details/25491891

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!