比 O(nlog(n)) 做得更好 — 4.局限性和当前发展

只要范围的大小不大于大约2/3 * nlog(n),这个方法就会更快。

长按关注《Python学研大本营》,加入读者群,分享更多精彩 扫码关注《Python学研大本营》,加入读者群,分享更多精彩

第 4 部分:局限性和当前发展

限制——范围太大

虽然使用范围是这种方法的关键,但它也引入了一些硬限制,以限制该方法何时使用得更快。如果要排序的数组的范围明显大于数组中元素的数量,则在范围内循环将比使用nlog(n)时间进行排序花费更长的时间。理论上,如果范围大于len_arr * log(len_arr),最好只执行其中一种经典的排序方法。但是,并非所有操作的成本都相同,而且我正在执行的操作(例如在字典中创建键)在计算上比经典排序算法中使用的操作更昂贵。因此,实际上,只要范围的大小不大于大约2/3 * nlog(n),我的方法就会更快。

在第 3 部分中,我们看到 mergeSort 需要大约 50 秒来对一个 1000 万长的数组进行排序,该数组的范围为 1000 万。

比 O(nlog(n)) 做得更好 — 4.局限性和当前发展_第1张图片

如果我们将范围扩大到大约2/3 * nlog(n),我们就有大约 1.5 亿的范围。从下面的代码中可以看出,我的方法需要大约 52 秒来对一个范围更广的数组进行排序。因此,通过实验,我们确认了范围可以有多大的上限阈值,并且仍然不比 mergeSort 慢。

比 O(nlog(n)) 做得更好 — 4.局限性和当前发展_第2张图片

限制——使用浮点数

另一个主要限制是使用范围似乎会排除对浮点数进行排序,因为任何两个浮点数之间的范围是无限的。然而,当改变观点时,处理浮动的方法就会出现。

当前的发展——将方法扩展到浮点数

处理浮点数们再次改变我们正在解决的问题。在这种情况下,我从尝试使用范围对浮点数进行排序(这是不可能的)更改为使用范围来解决两个整数排序问题。我通过将整数和小数部分视为两个单独的排序问题来做到这一点。

首先,我们创建一个包含数字整数部分的字典,就像我们之前所做的那样。但是,这次我们确实使用了一个列表,而不是保存该键的实例数的键,该列表是与给定整数部分匹配的每个小数部分。换句话说,我们创建了一个新级别的分组。每个唯一整数现在都是顶级组。

比 O(nlog(n)) 做得更好 — 4.局限性和当前发展_第3张图片

但是,在尝试获取小数部分时会出现浮点错误问题,因此必须将小数部分转换为字符串。

比 O(nlog(n)) 做得更好 — 4.局限性和当前发展_第4张图片

从这里,可以对每个整数组的小数部分进行排序,然后是用于对整数组进行排序的范围。

比 O(nlog(n)) 做得更好 — 4.局限性和当前发展_第5张图片

但是,这里仍有一些问题需要解决,特别是处理前导零和尾随零。在我的方法中,小数部分先转换成字符串,然后再转换成整数。将字符串版本的小数转换为整数后,原来的 0.9 会变成 '09',然后变成 9——它最终会小于 0.11,最终会变成 11。同样,.00001 将变为 1,使其与 0.1 一样大(因为它也变为 1)——所以,有一些问题需要解决。

当前的发展——许多小数位 = 大范围

使用浮点数的另一个问题是,在处理小数部分时,每个小数点都会将小数部分的范围增加一个数量级。即使整数部分的范围很窄,很多小数位也会导致我的方法处理得很慢。

我意识到我可以通过将小数部分保留为字符串并将这些字符串分成“上半部分”和“下半部分”来缓解这个问题。然后,就像整数是具有匹配小数部分的顶级分组一样,上半部分可以具有匹配下半部分的实例。例如,假设我有一个小数部分“12345678”——如果我保持原样,那就是 1000 万的范围。但是,如果我把它分成一个上半部分和一个匹配的下半部分,它就变成了:'1234': ['5678']——那么我在排序下半部分时只有 10k 的范围,而在排序时只有 10k 的范围对上半部分进行排序。我仍然需要对 10k 个下半部分的范围 * 我拥有的上半部分的数量进行排序,所以我查看的范围总共仍然是 1000 万,但是分解后的排序过程更快。

当前发展——按位数对整数进行分组

然后我突然意识到我可以对整数部分做类似的事情来缓解大范围的问题。一开始,我可以根据整数的位数创建存储桶,而不是创建所有唯一元素及其实例的字典。然后,因为每个桶都有相同的位数,我可以把它们变成字符串,把它们切成两半,然后做我对小数做的事情。当我这样做时,它加快了那些我的范围大于要排序的元素数量的情况的排序过程,并将我的方法可以使用的范围的大小扩大了大约 4 倍。所以现在,我可以使用大约2/3nlog(n)的范围,而不是限制在其中n是要排序的数组的大小2.75nlog(n)大小。

这是按位数创建存储桶的代码。注意 — 稍后将所有这些桶压缩到最终排序的数组中时需要 max_length 变量。

比 O(nlog(n)) 做得更好 — 4.局限性和当前发展_第6张图片

这种方法的完整过程是:

  1. 根据整数的位数将所有整数分组。

  2. 对于每个数字长度组,将每个元素转换为字符串并将此字符串切割成上半部分和下半部分,创建一个上半部分和下半部分匹配的字典。

  3. 对于每个数字长度组,对于每个上半部分,对其匹配的下半部分进行排序,并将两半重新组成一个字符串。

  4. 对于每个数字长度组,对上半部分进行排序(在第 3 步之后与下半部分重新连接)。

  5. 将数字桶的范围从最少数字到最多数字,将它们的排序元素连接到最终的排序数组中。

注意——虽然这里的逻辑有很多嵌套操作,但在任何步骤中总共发生的操作永远不会超过n 个( n是原始未排序输入数组中的元素数)。因此,步骤的O()都是O(n)。

步骤 2-5 的顶级逻辑:

比 O(nlog(n)) 做得更好 — 4.局限性和当前发展_第7张图片

第 2 步详细信息:

比 O(nlog(n)) 做得更好 — 4.局限性和当前发展_第8张图片

第 3 步详细信息:

比 O(nlog(n)) 做得更好 — 4.局限性和当前发展_第9张图片

比 O(nlog(n)) 做得更好 — 4.局限性和当前发展_第10张图片

第 4 步详细信息:

比 O(nlog(n)) 做得更好 — 4.局限性和当前发展_第11张图片

使用数字长度存储桶提高速度

将这种方法与使用 mergeSort 以及具有 1000 万个元素和 1 亿范围的数组的基本 groupSort 进行比较。

使用数字桶分组排序:

比 O(nlog(n)) 做得更好 — 4.局限性和当前发展_第12张图片

使用基本组排序:

比 O(nlog(n)) 做得更好 — 4.局限性和当前发展_第13张图片

使用合并排序:

比 O(nlog(n)) 做得更好 — 4.局限性和当前发展_第14张图片

验证所有结果均已排序且等效:

进一步的思考——个位数的水平

我也意识到,如果它可以把事情分成两半,我不妨尽可能地把它们分解。换句话说,我可以为每个数字 place创建存储桶,而不是根据整数中的位数创建存储桶,并将后续位置嵌套为较低级别。因此,数字 [1234, 1345] 将变为:

比 O(nlog(n)) 做得更好 — 4.局限性和当前发展_第15张图片

然后每个级别将在 0-9 的范围内进行排序,压缩直到最终我们有一个排序数组。

https://medium.com/@ajchristian86/doing-better-than-o-nlog-n-part-4-eaec180f8397

推荐书单

《Pandas1.x实例精解》

比 O(nlog(n)) 做得更好 — 4.局限性和当前发展_第16张图片

本书详细阐述了与Pandas相关的基本解决方案,主要包括Pandas基础,DataFrame基本操作,创建和保留DataFrame,开始数据分析,探索性数据分析,选择数据子集,过滤行,对齐索引,分组以进行聚合、过滤和转换,将数据重组为规整形式,组合Pandas对象,时间序列分析,使用Matplotlib、Pandas和Seaborn进行可视化,调试和测试等内容。此外,本书还提供了相应的示例、代码,以帮助读者进一步理解相关方案的实现过程。 本书适合作为高等院校计算机及相关专业的教材和教学参考书,也可作为相关开发人员的自学用书和参考手册。

链接:https://u.jd.com/UKjx4et

精彩回顾

《Pandas1.x实例精解》新书抢先看!

【第1篇】利用Pandas操作DataFrame的列与行

【第2篇】Pandas如何对DataFrame排序和统计

【第3篇】Pandas如何使用DataFrame方法链

【第4篇】Pandas如何比较缺失值以及转置方向?

【第5篇】DataFrame如何玩转多样性数据

【第6篇】如何进行探索性数据分析?

【第7篇】使用Pandas处理分类数据

【第8篇】使用Pandas处理连续数据

【第9篇】使用Pandas比较连续值和连续列

【第10篇】如何比较分类值以及使用Pandas分析库

长按关注《Python学研大本营》

长按二维码,加入Python读者群

扫码关注《Python学研大本营》,加入读者群,分享更多精彩

你可能感兴趣的:(算法,python,java)