比冒泡算法还简单的排序算法:看起来满是bug的程序,居然是对的

简介: 比冒泡算法还简单的排序算法:看起来满是bug的程序,居然是对的

程序 bug 也能负负得正吗?


还真可以。


a7cbb56adfa04f5ead26a03c326e8919.png


比如程序员们再熟悉不过的排序算法,通过两个“bug”居然能歪打正着,实在令人匪夷所思。


请看这位程序员写的数组升序排序代码:


for i = 1 to n do
  for j = 1 to n do
    if A[i] < A[j] then
      swap A[i] and A[j]


最近这串代码在 Hacker News 论坛上突然火了起来,引来大批程序员围观。


image.png


乍一看这段代码,你的反应会是什么?会不会觉得这个程序员水平太差了,连基本的冒泡算法都写不好:


不等号方向错了,第二层循环指数 j 的范围也弄错了。


总之,这段代码“绝对不可能正确”。




9c88b9cc88c54510976b1e4aabe58840.gif冒泡算法


但如果你真的运行一下会发现,结果还真的是按照升序排列的。


我们再来看一下正确的冒泡算法代码是怎样的:


for i = 1 to n do
  for j = i + 1 to n do
    if A[i] > A[j] then
      swap A[i] and A[j]


后者不同之处是 j = i + 1 且 A[i] > A[j],两段程序大相径庭。


然而我要告诉你一个不可思议的事实,其实第一串代码是对的,而且可以严格证明。


那么它是如何实现正确排序的?


为何能歪打正着


仔细一想,其实很容易理解。因为该算法比冒泡排序多一半交换操作,正好可以将降序编程升序。


不过,作者还是给出了严格的证明。


我们定义 Pᵢ 是经过 i 次(1 ≤ i ≤ n)外循环后得到的数组。


如果算法正确,那么前 i 项已经是升序排列,即 A[1] ≤ A[2] ≤ . . . ≤ A[i]。


证明该算法正确,实际上就是证明 Pₙ 对于任何 n 都成立。


根据数学归纳法,我们只要证明 P₁ 成立,假设 Pᵢ 成立,接着再证明 Pi+1 也成立,命题即可得证。


P₁ 显然是正确的,而且这一步和普通的冒泡算法降序没有区别,经过第 1 次外循环,A[1] 就是整个数组的最大元素。


接着我们假设 Pᵢ 成立,然后证明 Pi+1 成立。


我们先定义一个序数 k:


首先假设 A[k](k 介于 1~i 之间)满足 A[k]>A[i+1] 最小的一个数,那么 A[k−1]≤A[i+1](k≠1)。


如果 A[i+1]≥A[i],那么这样的 k 不存在,我们就令 k=i+1。


考虑以下三种情况:


1、1 ≤ j ≤ k−1


由于 A[i+1]>A[j],没有任何元素交换发生。


2、 k ≤ j ≤ i (如果 k=i+1,则不存在此步骤)


由于 A[j]>A[i+1],所以每次比较后都会有元素交换发生。


我们使用 A[ ] 和 A′[ ] 来表示交换前和交换后的元素,所以


A′[i+1] = A[k],A′[k]=A[i+1]

经过一系列交换,最大元素最终被放到了 A[i+1] 位置上,原来的 A[i+1] 变成了最大元素,A[k] 被插入了大小介于原来 A[k] 和 A[k-1] 之间的元素。


3、i+1 ≤ j ≤ n


由于最大元素已经交换到前 i+1 个元素中,此过程也没有任何元素交换。


最后,Pₙ 就是升序排序算法执行完以后的结果。


由于内外两组循环没有任何范围差别,因此这可以说是“最简单”的排序算法了。


从代码上来看,它很像冒泡算法,但从证明过程中可以看出,这实际上是一种插入算法。



b04568183fce4cc3ada1e68c67b3bdfa.gif插入算法


算法复杂度


显然,该算法总会进行 n² 次比较,接下来计算算法的交换次数。


可以证明交换其次最多为 I+2(n-1),最少为 n-1。


其中 I 为初始数字的逆序数,最大为 n(n-1)/2


因此整个算法的复杂度为 O(n²)。


从证明过程中可以看出,除了 i=1 的循环以外,其余循环里 j=i-1 之后的部分完全无效,因此可以将这部分省略,得到简化后的算法。


for i = 2 to n do
  for j = 1 to i − 1 do
    if A[i] < A[j] then
      swap A[i] and A[j]


该算法减少了比较和交换次数,不过算法复杂度依然是 O(n²)。


网友:这个算法我以前见过

比最容易理解的冒泡算法还要简单,这个排序算法在 Hacker News 上很快引起了网友的围观。


不少人觉得它“很眼熟”。


有位网友表示,自己曾在奥林匹克数学竞赛中看到一个同学用了一种非常奇怪的排序算法,它可以运行但是效率很低,更像是一种插入排序。


如果我没记错的话,他用的就是这种算法。


1.png


事实上,关于这种算法的讨论已久,从 2014 年开始就不断有人发帖,这次作者将论文上传到 arXiv 后又引起了广泛热议。


image.png


甚至还有乌龙事件发生。


有位网友扫了一眼论文就以为这个算法和自己 10 年前提出的一样。


留言网友的算法:


1.png


乍一看两种算法的代码确实很像,原理上的确有些相似。


都是看起来像冒泡排序,但其实更贴近选择排序。


不过很快有人指出真相:这种算法中 j=i+1 to n,并且是当 A[i] > A[j] 时交换。


而作者提出的算法中 j=1 to n,A[i] < A[j] 时交换。


两种算法相比,网友此前提出的更容易被理解为什么可以运行。


image.png


当然也有歪楼的,有人就调侃自己刚学编程时写过这个算法。


我百分百确定,在我刚开始学编程、并想要找到最短的排序方法时就写过它。


image.png

image.png



不过说到实际应用上,这种算法需要的计算时间太长了。


有人就认为,这种算法此前被发现过很多次,但是那些人根本没打算用它。


image.png


也有人提出:这种排序没有睡眠排序简单。


image.png


睡眠排序就是构造 n 个线程,让线程和排序的 n 个数对应。


例如对于 [4,2,3,5,9] 这样一组数字,就创建 5 个线程,每个线程睡眠 4s,2s,3s,5s,9s。这些线程睡醒之后,就把自己对应的数报出来即可。这样等所有线程都醒来,排序就结束了。


但和作者提出的算法一样,睡眠排序由于多线程的问题,在真正实现上也有困难。


此外,这位网友也表示自己看到过这种算法:


我确定我此前看到过这种算法,它没有名字吗?


很快就有人提议说——


如果它没有名字的话,我建议称之为“面试排序”。


image.png

相关文章
|
3月前
|
存储 算法 C语言
"揭秘C语言中的王者之树——红黑树:一场数据结构与算法的华丽舞蹈,让你的程序效率飙升,直击性能巅峰!"
【8月更文挑战第20天】红黑树是自平衡二叉查找树,通过旋转和重着色保持平衡,确保高效执行插入、删除和查找操作,时间复杂度为O(log n)。本文介绍红黑树的基本属性、存储结构及其C语言实现。红黑树遵循五项基本规则以保持平衡状态。在C语言中,节点包含数据、颜色、父节点和子节点指针。文章提供了一个示例代码框架,用于创建节点、插入节点并执行必要的修复操作以维护红黑树的特性。
101 1
|
1月前
|
机器学习/深度学习 存储 缓存
数据结构与算法学习十:排序算法介绍、时间频度、时间复杂度、常用时间复杂度介绍
文章主要介绍了排序算法的分类、时间复杂度的概念和计算方法,以及常见的时间复杂度级别,并简单提及了空间复杂度。
23 1
数据结构与算法学习十:排序算法介绍、时间频度、时间复杂度、常用时间复杂度介绍
|
16天前
|
算法 测试技术 开发者
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗;代码审查通过检查源代码发现潜在问题,提高代码质量和团队协作效率。本文介绍了一些实用的技巧和工具,帮助开发者提升开发效率。
18 3
|
28天前
|
存储 缓存 算法
如何通过优化算法和代码结构来提升易语言程序的执行效率?
如何通过优化算法和代码结构来提升易语言程序的执行效率?
|
27天前
|
监控 算法 数据安全/隐私保护
基于三帧差算法的运动目标检测系统FPGA实现,包含testbench和MATLAB辅助验证程序
本项目展示了基于FPGA与MATLAB实现的三帧差算法运动目标检测。使用Vivado 2019.2和MATLAB 2022a开发环境,通过对比连续三帧图像的像素值变化,有效识别运动区域。项目包括完整无水印的运行效果预览、详细中文注释的代码及操作步骤视频,适合学习和研究。
|
1月前
|
搜索推荐 算法
数据结构与算法学习十四:常用排序算法总结和对比
关于常用排序算法的总结和对比,包括稳定性、内排序、外排序、时间复杂度和空间复杂度等术语的解释。
19 0
数据结构与算法学习十四:常用排序算法总结和对比
|
21天前
|
缓存 分布式计算 监控
算法优化:提升程序性能的艺术
【10月更文挑战第20天】算法优化:提升程序性能的艺术
|
1月前
|
数据可视化 搜索推荐 Python
Leecode 刷题笔记之可视化六大排序算法:冒泡、快速、归并、插入、选择、桶排序
这篇文章是关于LeetCode刷题笔记,主要介绍了六大排序算法(冒泡、快速、归并、插入、选择、桶排序)的Python实现及其可视化过程。
13 0
|
1月前
|
机器学习/深度学习 搜索推荐 算法
探索数据结构:初入算法之经典排序算法
探索数据结构:初入算法之经典排序算法
|
1月前
|
搜索推荐 算法
排序算法---冒泡&选择&插入总结
排序算法---冒泡&选择&插入总结
14 0