退火算法 贪婪算法_算法第1部分:贪婪算法

退火算法 贪婪算法

If someone asked me to describe what a software engineer does in a single word, my answer would be optimization. The fact is most of the time we don’t look for the best solution and we don’t look for the cheapest either, but we look for the optimum one. Sacrificing your time for the best solution is a noble cause, but there may come times where the best solution is overkill and you don’t always have the resources (time, money, etc.) to go after your ideals. In fact, you don’t always need the best solution either, just a fairly good one might suffice. I hereby welcome you to the first chapter of the series: greedy algorithms…

如果有人要我用一个词描述一个软件工程师的工作,那么我的答案就是优化 。 事实是,大多数时候我们都不是寻找最佳解决方案,也不是寻找最便宜的解决方案,但是我们寻找的是最佳解决方案。 牺牲时间来寻求最佳解决方案是一个崇高的事业,但有时最好的解决方案是过度杀伤力,并且您并不总是拥有追求理想的资源(时间,金钱等)。 实际上,您也不总是需要最好的解决方案,只要一个相当好的解决方案就足够了。 在此,我欢迎您进入本系列的第一章:贪婪算法...

What is a greedy algorithm? The book definition says a greedy algorithm makes the optimal choice at each step in order to find the globally optimal solution to the problem. What this British old man pretentiously tells you while smoking his pipe (that’s how they are in my twisted imagination) is actually a very simple idea. At any given point on your algorithm, instead of searching through all possible solutions and picking the best one, just pick the one that seems the best. Then on the next step, pick the one that seems the best again. Rinse and repeat until you reach the end of the road and just hope that your locally optimal choices lead you to the global optimum.

什么是贪心算法? 该书定义说,贪婪算法会在每个步骤中做出最优选择,以便找到针对该问题的全局最优解决方案。 这个英国老人在抽烟斗时自夸地告诉你(这就是我的想象力),这实际上是一个非常简单的想法。 在算法的任何给定点上,而不是搜索所有可能的解决方案并选择最佳解决方案,而只是选择看起来最好的解决方案。 然后在下一步中,选择看起来最好的那个。 漂洗并重复直到您到达路的尽头,并希望您的局部最优选择能使您达到全局最优。

Everything up to this point was quite intangible but it gives the idea behind greedy algorithms. Soak it in and let’s dive into a real-life example.

到目前为止,所有事情都是无形的,但它给出了贪婪算法背后的想法。 浸泡一下,让我们进入一个真实的例子。

活动选择问题 (Activity Selection Problem)

This is a very well-known problem, almost a cliche. You are given a series of activities, each having a determined start and finish time. They share a common resource (your time maybe?) and some of them obviously overlap. Your task is to select the activity subset with the maximum cardinality, in which there are no overlaps.

这是一个非常著名的问题,几乎是陈词滥调。 您将获得一系列活动,每个活动都有确定的开始和结束时间。 他们共享一个共同的资源(也许是您的时间?),其中一些显然重叠。 您的任务是选择基数最大的活动子集,其中不存在重叠。

Image for post

There are given ten activities with start and finish times in the table above. Subset {2, 4, 8} does not contain any overlaps while subset {5, 6} does. We need to make greedy choices to find the biggest feasible subset. Here are some possible greedy approaches:

上表中提供了十项活动的开始和结束时间。 子集{2,4,8}不包含任何重叠,而子集{5,6}包含。 我们需要做出贪婪的选择,以找到最大的可行子集。 以下是一些可能的贪婪方法:

  • Choose the earliest starting activity first

    首先选择最早的开始活动
  • Choose the earliest finishing activity first

    首先选择最早的整理活动
  • Choose the shortest activity first

    首先选择最短的活动

We defined three very simple greedy approaches to the problem. Possible approaches to a problem usually require nothing more than a good intuition. Some are much harder than others but the idea is the same.

我们为该问题定义了三种非常简单的贪婪方法。 解决问题的可能方法通常只需要良好的直觉。 有些难度比其他难度大得多,但想法是相同的。

To me, it is obvious to always choose the activity that leaves the resource available for as many other activities as possible. Then, let’s start with the third approach. First, you need to sort the activities by their execution times. Then iterate through the activities and if the current activity starts after the previous one finishes, add it into the result set. Here is the C++ code, assuming the activities are already sorted.

对我来说,很明显总是选择使资源尽可能多地用于其他活动的活动。 然后,让我们从第三种方法开始。 首先,您需要按活动的执行时间对活动进行排序。 然后遍历这些活动,如果当前活动在上一个活动完成之后开始,则将其添加到结果集中。 这是C ++代码,假设活动已经排序。

vector<int> findMaximumSubset(vector<Activity> activities) {
vector<int> result;
result.push_back(0); for (size_t i = 1; i < activities.size(); i++) {
if (activities[i].start >= activities[i - 1].finish) {
result.push_back(i);
}
} return result;
}

Note that the earliest activity comes first when two activities have the same execution time. This is a very simple algorithm and running it produces the subset {2, 4, 8} for the given table. But wait, the optimal solution is subset {1, 4, 7, 10} and our algorithm was not able to find it!

请注意,当两个活动具有相同的执行时间时,最早的活动首先出现。 这是一个非常简单的算法,运行它会为给定的表生成子集{2,4,8}。 但是,等等, 最佳解决方案是子集{1、4、7、10},我们的算法无法找到它!

Worry not, let’s try another approach. This time we will choose the earliest finishing activity first. The source code for the second approach is exactly the same as the previous one and the only thing that changes is the sorting step. This time, we sort the activities by their finish times and execute the same algorithm. For the activities with the same finish times, the earliest starting activity comes first.

不用担心,让我们尝试另一种方法。 这次我们将首先选择最早的整理活动。 第二种方法的源代码与前一种方法完全相同,唯一改变的是排序步骤。 这次,我们根据活动的完成时间对其进行排序,并执行相同的算法。 对于完成时间相同的活动,最早的开始活动排在第一位。

When you execute the code, you will see that it actually produces the subset {1, 4, 7, 10}, which incidentally is the optimal solution. Obviously, both approaches were greedy using greedy algorithms but the first one didn’t give us the optimal solution. There might be times it is okay not to find the optimal solution but why do that, when you can reach the optimal one so easily?

执行代码时,您会看到它实际上产生了子集{1、4、7、10},这是最佳解决方案 。 显然,两种方法都使用贪婪算法贪婪,但是第一种方法并没有给我们提供最佳解决方案。 有时候可能没有找到最佳解决方案是可以的,但是为什么当您如此轻松地达到最佳解决方案时又为什么呢?

We can actually prove that the earliest finishing activity first approach always produces the best result. I will try not to use the complex proof language and make it as simple as possible but if you are still not interested in proofs, just skip the next paragraph.

我们实际上可以证明,最早的完成活动优先方法始终可以产生最佳结果。 我将尽量不使用复杂的证明语言,并使其尽可能简单,但是如果您仍然对证明不感兴趣,请跳过下一段。

Assume you have found a feasible subset of activities and they are sorted in the earliest activity first manner so that each activity starts after all the previous ones end. For any given activity in the subset, if there exists another activity that also starts after all the previous activities are finished and it also finishes before the given activity in your current subset, you can replace it with the given activity in your subset. When you do that, the size of the subset stays the same at worst and it has a possibility to increase.

假设您找到了一个可行的活动子集,并且按照最早的活动优先方式对它们进行了排序,以便每个活动都在所有先前的活动结束之后开始。 对于子集中的任何给定活动,如果存在另一个活动,该活动也在所有先前的活动完成之后开始,并且也在当前子集中的给定活动之前完成,则可以将其替换为子集中的给定活动。 当您这样做时,子集的大小在最坏的情况下保持不变,并且有可能增加。

That’s it for the activity selection problem. But before we are done with this post, I want to discuss another problem.

这就是活动选择问题。 但是在完成这篇文章之前,我想讨论另一个问题。

背包问题 (Knapsack Problem)

In this section, we will discuss the knapsack problem. More specifically the fractional knapsack problem. In the knapsack problem, you are a thief with a bag that can carry at most 10 kg (all hail to the metric system) and you are going to shoplift. For some weird reason, you are stealing flour, sugar and salt - maybe you want to slowly kill yourself by consuming unhealthy food for a long time. Your purpose is to make your bag as valuable as possible.

在本节中,我们将讨论背包问题。 更具体地说,分数背包问题。 在背包问题中,您是一个小偷,带的袋子最多可以承载10公斤(所有冰雹均以公制为单位),并且您打算去购物。 由于某些奇怪的原因,您正在偷面粉,糖和盐-也许您想长时间食用不健康的食物来慢慢杀死自己。 您的目的是使您的包包尽可能有价值。

I believe the greedy nature of this problem is very obvious. Steal the most valuable ingredient first until there is no more left and move on to the next until your bag is full. Thus, if the corresponding prices for flour, sugar and salt is 10, 8 and 6 dollars per kg (I have no idea about the actual prices) and each of them has 4 kg of supply available, you would but 4 kg of flour, then 4 kg of sugar and finally 2 kg of salt.

我相信这个问题的贪婪性质非常明显。 首先偷走最有价值的食材,直到剩无几,再移到下一个食材,直到装满袋子。 因此,如果面粉,糖和盐的相应价格为每公斤10、8和6美元(我不知道实际价格),并且每一种都有4公斤的可用供应量,那么您只能得到4公斤的面粉,然后加入4公斤糖,最后加入2公斤盐。

Let’s change the problem a little bit. After a few years of shoplifting, you now want to play in the big kids league and in order to prove yourself, you will participate in a bank job! Oblivious of the unfortunate events awaiting, you have entered the vault of the bank with your faithful 10 kg bag. In the vault, there is one gold, one silver and one platinum ingot with 4 kg, 6 kg and 2 kg of weights correspondingly. Each gold ingot worth 100 dollars, silver worth 120 dollars and platinum worth 60 dollars. So platinum is the most valuable, then gold and then silver when their price calculated per kg.

让我们稍微改变一下问题。 经过几年的入店行窃,您现在想参加大型儿童联赛,并且为了证明自己,您将参加银行工作! 无视即将发生的不幸事件,您已经带着10公斤忠实的袋子进入了银行的保险库。 在金库中,有一个金,一个银和一个铂锭,分别具有4公斤,6公斤和2公斤的重量。 每个金锭价值100美元,白银价值120美元,白金价值60美元。 因此,按每公斤价格计算时,铂金是最有价值的,其次是黄金,然后是银。

If we apply the same strategy we were using back in the shoplifting days, we need to take platinum ingot first since it is the most valuable per kg. Then we take the gold ingot, which gives us 160 dollars in total for 6 kg of our storage capacity. We cannot take the silver because we only have 4 kg capacity left in our bag. For the optimum solution, we should have taken gold and silver ingots, which would worth 220 dollars in total for 10 kg of bag capacity.

如果我们采用在入店行窃时使用的相同策略,则需要首先摄取铂金锭,因为它是每公斤最有价值的。 然后我们拿走金锭,这使我们总共得到了160美元,可容纳6公斤的存储量。 我们不能拿走白银,因为我们的包里只剩下4公斤的重量。 为了获得最佳解决方案,我们应该选择金锭和银锭,每10公斤的装袋容量,总价值为220美元。

You can try different approaches but you won’t be able to find a greedy algorithm that yields the optimal solution for this problem, which leads us to the next topic in the series: Dynamic Programming

您可以尝试不同的方法,但找不到能够为该问题提供最佳解决方案的贪婪算法,这将导致我们进入本系列的下一个主题: 动态编程

翻译自: https://medium.com/@canbayar91/algorithms-revisited-part-1-greedy-algorithms-d8f10c7c0c36

退火算法 贪婪算法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值