常见算法思维
常见算法思维
贪婪算法
- 如何处理不可能完成的任务:没有快速算法的问题(NP完全问题)
- 识别NP完全问题,以免浪费时间去寻找解决它们的快速算法。
- 学习近似算法,使用它们可快速找到NP完全问题的近似解
- 学习贪婪策略——一种非常简单的问题解决策略。
贪婪算法很简单:每步都采取最优的做法。
每步都选择局部最优解,最终得到的就是全局最优解。
动态规划
这是一种解决棘手问题的方法,它将问题分成小问题,并先着手解决这些小问题。
如何设计问题的动态规划解决方案。
动态规划先解决子问题,再逐步解决大问题。
K最近邻算法
使用K最近邻算法创建分类系统。
特征抽取。
回归,即预测数值,如明天的股价或用户对某部电影的喜欢程度。
反向索引
搜索引擎的工作原理。假设你有三个网页,内容如下。
根据这些内容创建一个散列表。这个散列表的键为单词,值为包含指定单词的页面。现在假设有用户搜索hi,在这种情况下,搜索引擎需要检查哪些页面包含hi。
搜索引擎发现页面A和B包含hi,因此将这些页面作为搜索结果呈现给用户。现在假设用户搜索there。你知道,页面A和C包含它。
这是一种很有用的数据结构:一个散列表,将单词映射到包含它的页面。这种数据结构被称为反向索引(inverted index),常用于创建搜索引擎。
傅里叶变换
给它一杯冰沙,它能告诉你其中包含哪些成分。换言之,给定一首歌曲,傅里叶变换能够将其中的各种频率分离出来。
如果能够将歌曲分解为不同的频率,就可强化你关心的部分,如强化低音并隐藏高音。傅里叶变换非常适合用于处理信号,可使用它来压缩音乐。为此,首先需要将音频文件分解为音符。傅里叶变换能够准确地指出各个音符对整个歌曲的贡献,让你能够将不重要的音符删除。这就是MP3格式的工作原理!
傅里叶变换还被用来地震预测和DNA分析。使用傅里叶变换可创建类似于Shazam这样的音乐识别软件。傅里叶变换的用途极其广泛,你遇到它的可能性极高!
并行算法
为提高算法的速度,你需要让它们能够在多个内核中并行地执行!
在最佳情况下,排序算法的速度大致为O(n log n)。众所周知,对数组进行排序时,除非使用并行算法,否则运行时间不可能为O(n)!对数组进行排序时,快速排序的并行版本所需的时间为O(n)。
并行算法设计起来很难,要确保它们能够正确地工作并实现期望的速度提升也很难。有一点是确定的,那就是速度的提升并非线性的,因此即便你的笔记本电脑装备了两个而不是一个内核,算法的速度也不可能提高一倍,其中的原因有两个。
❑ 并行性管理开销。假设你要对一个包含1000个元素的数组进行排序,如何在两个内核之间分配这项任务呢?如果让每个内核对其中500个元素进行排序,再将两个排好序的数组合并成一个有序数组,那么合并也是需要时间的。
❑ 负载均衡。假设你需要完成10个任务,因此你给每个内核都分配5个任务。但分配给内核A的任务都很容易,10秒钟就完成了,而分配给内核B的任务都很难,1分钟才完成。这意味着有那么50秒,内核B在忙死忙活,而内核A却闲得很!你如何均匀地分配工作,让两个内核都一样忙呢?
MapReduce
有一种特殊的并行算法正越来越流行,它就是分布式算法。在并行算法只需两到四个内核时,完全可以在笔记本电脑上运行它,但如果需要数百个内核呢?在这种情况下,可让算法在多台计算机上运行。MapReduce是一种流行的分布式算法,你可通过流行的开源工具Apache Hadoop来使用它。
分布式算法为何很有用
假设你有一个数据库表,包含数十亿乃至数万亿行,需要对其执行复杂的SQL查询。在这种情况下,你不能使用MySQL,因为数据表的行数超过数十亿后,它处理起来将很吃力。相反,你需要通过Hadoop来使用MapReduce!又假设你需要处理一个很长的清单,其中包含100万个职位,而每个职位处理起来需要10秒。如果使用一台计算机来处理,将耗时数月!如果使用100台计算机来处理,可能几天就能完工。分布式算法非常适合用于在短时间内完成海量工作,其中的MapReduce基于两个简单的理念:映射(map)函数和归并(reduce)函数。
映射函数
映射函数很简单,它接受一个数组,并对其中的每个元素执行同样的处理。
如果有100台计算机,而map能够自动将工作分配给这些计算机去完成就好了。这样就可同时下载100个页面,下载速度将快得多!这就是MapReduce中“映射”部分基于的理念。
归并函数
归并函数可能令人迷惑,其理念是将很多项归并为一项。映射是将一个数组转换为另一个数组。而归并是将一个数组转换为一个元素。
布隆过滤器和HyperLogLog
假设你管理着网站Reddit。每当有人发布链接时,你都要检查它以前是否发布过,因为之前未发布过的故事更有价值。
又假设你在Google负责搜集网页,但只想搜集新出现的网页,因此需要判断网页是否搜集过。在假设你管理着提供网址缩短服务的bit.ly,要避免将用户重定向到恶意网站。
你有一个清单,其中记录了恶意网站的URL。你需要确定要将用户重定向到的URL是否在这个清单中。这些都是同一种类型的问题,涉及庞大的集合。
给定一个元素,你需要判断它是否包含在这个集合中。为快速做出这种判断,可使用散列表。例如,Google可能有一个庞大的散列表,其中的键是已搜集的网页。
布隆过滤器
布隆过滤器是一种概率型数据结构,它提供的答案有可能不对,但很可能是正确的。为判断网页以前是否已搜集,可不使用散列表,而使用布隆过滤器。使用散列表时,答案绝对可靠,而使用布隆过滤器时,答案却是很可能是正确的。
❑ 可能出现错报的情况,即Google可能指出“这个网站已搜集”,但实际上并没有搜集。
❑ 不可能出现漏报的情况,即如果布隆过滤器说“这个网站未搜集”,就肯定未搜集。
布隆过滤器的优点在于占用的存储空间很少。使用散列表时,必须存储Google搜集过的所有URL,但使用布隆过滤器时不用这样做。
布隆过滤器非常适合用于不要求答案绝对准确的情况,前面所有的示例都是这样的。对bit.ly而言,这样说完全可行:“我们认为这个网站可能是恶意的,请倍加小心。”
HyperLogLog
HyperLogLog是一种类似于布隆过滤器的算法。
如果Google要计算用户执行的不同搜索的数量,或者Amazon要计算当天用户浏览的不同商品的数量,要回答这些问题,需要耗用大量的空间!
对Google来说,必须有一个日志,其中包含用户执行的不同搜索。有用户执行搜索时,Google必须判断该搜索是否包含在日志中:如果答案是否定的,就必须将其加入到日志中。即便只记录一天的搜索,这种日志也大得不得了!
HyperLogLog近似地计算集合中不同的元素数,与布隆过滤器一样,它不能给出准确的答案,但也八九不离十,而占用的内存空间却少得多。面临海量数据且只要求答案八九不离十时,可考虑使用概率型算法!