try ai
科普
编辑
分享
反馈
  • 不稳定排序

不稳定排序

SciencePedia玻尔百科
核心要点
  • 稳定排序算法保留键值相等项的原始相对顺序,而不稳定排序则不提供此类保证。
  • 通过按键重要性的逆序应用一系列稳定排序,可以优雅地实现多级(字典序)排序。
  • 选择使用不稳定排序会引入非确定性,导致排名中的公平性问题、数据管道中的数据完整性问题,以及图形学中如“Z-fighting”(深度冲突)等可见的瑕疵。
  • 通过使用“装饰-排序-去装饰”模式,可以强制为不稳定算法赋予稳定性,该模式涉及为项增加其原始索引作为决胜条件。

引言

排序是计算中最基本的操作之一,这个任务表面上看起来很简单:将一个项目集合按特定顺序排列。然而,当我们遇到根据排序标准被视为“相等”的项目时,一个关键的微妙之处便浮现出来。它们最终的排列顺序应该是什么?这个问题揭示了算法世界中的一个关键区别:稳定排序与不稳定排序之间的差异。这两种方法之间的选择不仅仅是一个技术细节,它对系统的可预测性、正确性,乃至其感知的公平性都有着深远的影响。

本文深入探讨排序稳定性的核心概念。它旨在弥合仅仅排序数据与理解算法对数据最终顺序所提供保证之间的知识鸿沟。在接下来的章节中,您将对这一原则有深入的理解。首先,“原理与机制”部分将剖析稳定性的定义,展示它如何实现像多级排序这样的强大技术,并揭示使算法能够维持顺序的内部逻辑。接下来,“应用与跨学科联系”部分将探讨这一选择在现实世界中的后果,从确保招生公平、防止计算机图形学中的视觉错误,到其在现代区块链系统的安全与经济学中的作用。

原理与机制

相等项的问题:什么是稳定性?

对一个项目列表进行排序是计算中最基本的任务之一,在日常生活中亦是如此。我们按邮政编码整理邮件,按作者整理书籍,按艺术家整理音乐播放列表。目标似乎很简单:将事物按顺序排列。但一个极其微妙的问题潜藏其下。当两个项目“相等”时,我们该怎么做?

假设你的电脑上有一系列文件,每个文件都有创建日期和文件类型(例如“文档”、“图像”)。如果你按类型对这些文件进行排序,所有的“文档”将被归为一组,所有的“图像”也将被归为一组。但是在“文档”组内部,它们将以何种顺序出现?是最早的文档排在前面?还是最新的?或者会是某种任意的、不可预测的混乱状态?

这就引出了​​稳定​​排序算法与​​不稳定​​排序算法之间的关键区别。

​​稳定排序算法​​做出一个承诺:如果两个项目具有相等的键(你排序所依据的属性),它们在输出中的相对顺序将与它们在输入中的相对顺序保持一致。如果你使用稳定排序按类型对文件进行排序,并且你的文件列表原本已按时间顺序排列,那么排序后,每种类型内的文件仍然会保持时间顺序。原始顺序在处理等值项时得以保留。

​​不稳定排序算法​​则不作此承诺。当它遇到具有相等键的项目时,它没有义务维持它们原始的相对顺序。它可能会保留,可能会反转,也可能会完全打乱。如果你对文件使用不稳定排序,那么每种文件类型内部的时间顺序很可能会被破坏。

想象一个处理天体记录的天文勘测数据管道。每条记录都有一个时间戳和一个类别(“星系”、“恒星”)。数据首先按时间戳排序。如果下一步是按类别排序,稳定排序将确保在“星系”组内,天体仍然按其观测时间排序。然而,如果使用不稳定排序,我们可能会看到一条来自 20230512 的观测记录出现在一条来自 20230508 的记录之前,尽管它们都是星系。这种重新排序的出现将是所用排序算法不稳定的明确证据。

这个属性看似一个微不足道的细节,但正如我们将看到的,它是解锁惊人强大且优雅的计算技术的关键。

电子表格的秘密:使用稳定排序实现多级排序

你是否曾在电子表格中先按一列排序,再按另一列排序?例如,先按州再按市对客户列表进行排序。目标是得到一个列表,其中城市在每个州内分组,并且州和城市都按字母顺序排列。这一常见功能背后的魔力就是稳定排序。

让我们来揭开它的神秘面纱。假设我们想按主键 AAA 和次键 BBB 对记录列表进行排序。一个常见的需求是让列表按 AAA 排序,并且对于所有 AAA 值相同的记录,它们应该按 BBB 排序。这被称为对偶 (A,B)(A, B)(A,B) 的​​字典序​​。

你可能会认为,直观的方法是先按主键 AAA 排序,然后按次键 BBB 排序。让我们试试看。第一次按 AAA 排序可以正确地将所有记录分组。但是第二次按 BBB 排序会完全重新排列列表,使所有内容按 BBB 的顺序排列,从而破坏了按 AAA 的主要分组!一条 (A=2,B=3)(A=2, B=3)(A=2,B=3) 的记录会移动到一条 (A=1,B=5)(A=1, B=5)(A=1,B=5) 的记录之前,因为 353 535。这不是我们想要的。

正确的方法是按重要性的逆序进行排序。

  1. 首先,对次键 BBB 执行​​稳定排序​​。
  2. 然后,对主键 AAA 执行​​稳定排序​​。

让我们看看为什么这能行。第二次排序根据键 AAA 排列整个列表。这是我们的主要目标。那么,当它遇到两个具有相同 AAA 值的记录时会发生什么?因为这次排序是稳定的,它承诺保持它们在此步骤之前的相对顺序。而那个顺序是什么?它正是第一步产生的顺序——一个按键 BBB 排序的列表!

因此,第二次排序的稳定性保留了第一次排序的顺序,但仅限于主键相等的分组内部。结果是一个完全按 AAA 排序,然后以 BBB 作为决胜条件的列表。这种优雅的两遍稳定排序技术是无数应用中多级排序的主力。

有趣的是,深入探究会发现,只有最后一遍排序必须是稳定的。第一遍排序(对次键 BBB)可以是不稳定的,而不会影响最终的字典序。为什么?因为它的唯一任务是按键 BBB 对项目进行分组。随后对 AAA 的稳定排序将利用这个 BBB 的顺序来为相等的 AAA 值决胜,而不管这个 BBB 的顺序是如何实现的。然而,如果最后对主键 AAA 的排序是不稳定的,它就可以随意打乱具有相同 AAA 值的项目,从而破坏第一遍排序建立的优美的次级顺序。

通往完美秩序的两条路径

顺序稳定排序方法不是实现字典序排序列表的唯一途径。还有一种更直接的方法。我们可以为每条记录定义一个单一的​​复合键​​。对于一条具有键 (A,B)(A, B)(A,B) 的记录,我们可以将整个键对视为一个单一的比较键。

比较规则将是:记录1在记录2之前,如果 A1A2A_1 A_2A1​A2​,或者如果 A1=A2A_1 = A_2A1​=A2​ 且 B1B2B_1 B_2B1​B2​。

通过使用这种复合比较规则进行单遍排序,我们可以获得完全相同的字典序排序结果。事实上,如果用于这单遍排序的算法也是稳定的,它甚至可以保留那些 AAA 和 BBB 都相同的记录的原始顺序。顺序稳定排序法和单一复合键法都是实现所需多键排序的有效策略。

机器之魂:如何保持稳定性

我们已经看到稳定排序能创造奇迹,但它们是如何做到的呢?是什么内部机制,即“机器之魂”,支撑着稳定性的承诺?

答案在于算法是如何构建和证明其正确性的。为了正式证明一个算法有效,计算机科学家经常使用一个叫做​​循环不变量​​的概念。循环不变量是在循环的每次迭代开始时都为真的条件。如果我们能建立这样一个不变量,并证明当循环终止时它在逻辑上能导出最终期望的结果,我们就证明了算法的正确性。

对于一个构建数组有序前缀的简单(可能不稳定)排序算法,其不变量可能是:“经过 kkk 次迭代后,数组的前 kkk 个元素是有序的。”

要证明稳定性,这个不变量是不够的。我们必须加强它。一个稳定排序算法的不变量必须是:“经过 kkk 次迭代后,前 kkk 个元素是有序的,​​并且​​在这个前缀内任何两个键相等的项目,它们的相对顺序与它们的原始相对顺序相同。”。

像插入排序这样的算法自然地维持了这个更强的不变量。当它考虑第 (k+1)(k+1)(k+1) 个元素时,它会向后扫描有序前缀以找到它的位置,将元素向前移动,但绝不会将一个元素与另一个键相等的元素交换位置。相比之下,像一个朴素的选择排序算法可能会在未排序部分找到最小的元素并将其交换到位。如果它与另一个键相等但在原始数组中出现得更早的元素交换,它就破坏了稳定性不变量。

这揭示了一个深刻的真理:稳定性不是一个偶然的副产品。它是一个必须在算法的每一步都有意识地保持的属性。

从数学上讲,我们可以这样想:一组具有重复键的项目定义了一个“严格弱序”。稳定排序通过使用原始位置作为决胜条件,有效地将其转换为“全序”。使用稳定排序对键 kkk 进行排序,等同于使用任何正确的排序算法对复合键 (k,i)(k, i)(k,i) 进行排序,其中 iii 是原始索引。稳定性是从模糊性走向单一、确定且有用的秩序的桥梁。

内存的代价:利用信息强制实现稳定性

如果我们只有一个不稳定的排序算法,但又迫切需要稳定性,该怎么办?我们能强制它实现吗?是的,如果我们愿意付出一点“内存的代价”。

这个策略是一种经典的计算机科学模式,称为​​装饰-排序-去装饰​​。

  1. ​​装饰​​:在排序之前,我们遍历列表,并通过附加每个项目的原始索引(其从 000 到 n−1n-1n−1 的位置)来“装饰”它。
  2. ​​排序​​:然后我们使用我们的不稳定算法对装饰过的项目进行排序,但采用新的比较规则:首先比较主键。如果主键相等,则比较附加的原始索引作为决胜条件。
  3. ​​去装饰​​:排序后,我们只需剥离掉索引,即可得到最终的稳定列表。

通过使用原始索引作为决胜条件,我们从比较器的视角使每个项目都变得独一无二。不稳定排序再也找不到“相等”的项目来错误处理了。

这就提出了一个有趣的问题:为了保证稳定性,我们需要为每个项目附加的绝对最小信息量是多少?为了唯一标识 nnn 个可能的原始位置,我们需要足够多的比特来表示 nnn 个不同的数字。表示 nnn 个不同状态所需的最小比特数 bbb 由公式 2b≥n2^b \ge n2b≥n 给出。解出 bbb 得 b≥log⁡2(n)b \ge \log_2(n)b≥log2​(n)。由于比特是不可分割的,最小数量是满足此条件的最小整数:⌈log⁡2(n)⌉\lceil \log_2(n) \rceil⌈log2​(n)⌉。

这是一个优美的结果。稳定性的抽象要求可以通过每个项目仅增加 ⌈log⁡2(n)⌉\lceil \log_2(n) \rceil⌈log2​(n)⌉ 个额外比特的内存来物理实现。这是记住过去的根本“信息成本”。

从确定性到概率性:在现实世界中构建稳定性

“装饰-排序-去装饰”模式功能强大,但如果用完整的索引来装饰在空间或复杂性上成本太高怎么办?我们能用一种捷径,比如用索引的​​加密哈希​​作为决胜条件吗?

这将我们从确定性保证的世界带入了概率和工程权衡的领域。使用哈希作为决胜条件有两种潜在的失败模式:

  1. ​​顺序反转​​:哈希函数不保留顺序。对于两个原始索引 iji jij,完全有可能得到 hash(i)>hash(j)hash(i) > hash(j)hash(i)>hash(j),这将导致决胜条件强制执行错误的相对顺序。
  2. ​​碰撞​​:两个不同的索引 iii 和 jjj 产生相同哈希值的可能性虽然很小,但确实存在:hash(i)=hash(j)hash(i) = hash(j)hash(i)=hash(j)。如果主键也相等,那么复合键就变得相同了。我们的不稳定排序算法现在可以自由地重新排序它们,从而破坏稳定性。

至少发生一次碰撞的概率由著名的“生日问题”决定。对于 nnn 个项目和一个 bbb 比特的哈希(有 2b2^b2b 个可能的输出),至少发生一次碰撞的概率大约与 n2/2bn^2 / 2^bn2/2b 成正比。如果我们使用一个大的哈希(例如,b=64b=64b=64)并且项目数量适中,这个概率是极其微小的。

这提出了一个经典的工程选择。使用完整索引的方法简单、确定,并保证正确。基于哈希的方法可能计算更快或占用更小的数据结构,但它带有微小但非零的失败风险。

在许多系统中,这种风险是完全可以接受的。但在其他系统中,正确性至关重要。想象一个系统,其中需要稳定性来确保公平,比如处理在同一毫秒到达的用户请求。不稳定的排序可能会违反先进先出(FIFO)的保证。在这种场景下,当一个系统不变量依赖于它时,概率性方法是不可接受的。我们需要只有真正的稳定排序或确定性装饰的排序才能提供的数学确定性。

稳定性的概念,初看起来像一个微不足道的技术细节,因此揭示了它是一个与信息、秩序、正确性乃至公平性交织在一起的深刻原则。理解它使我们能够构建更健壮、可预测和强大的系统。

应用与跨学科联系

在理解了排序算法的机械核心——即保留原始顺序的稳定过程与可能不保留原始顺序的不稳定过程之间的区别——之后,我们可能会想把这仅仅当作一个技术细节,一个供纯粹主义者玩味的精微之处。但是,自然界以及我们为模拟它而构建的系统,很少如此简单。正是在那些“平局”的时刻,在那些看似等价的瞬间,最有趣和最具影响力的现象才会出现。稳定与不稳定之间的选择不仅仅是算法的选择;这一选择会回响在数据管道、伦理框架,乃至经济系统的基石之中。这个决定可能意味着一个系统是可预测和公平的,还是一个被随机性的幽灵所困扰的系统。

排序的艺术:构建复杂排名

让我们从最直接的应用开始。我们生活在一个热爱排名的世界,但很少是在单一维度上排名。一个体育联盟不只关心胜场数;胜场数相同通常会用净胜分来决胜负。数字图书馆的搜索结果不仅应显示最相关的项目,而且对于相关性相同的项目,还应以合理的次要顺序(如按标题字母顺序)呈现。一所大学可能会根据已修学分来为学生安排课程注册的优先顺序,但对于学分相同的学生,注册更早的学生应该获得优先权。

我们如何实现这种多级排序?一种方法是设计一个复杂的比较器,一个单一的规则,知道如何根据主键、然后是次键等来比较项目。这行得通,但还有一种更优雅且出奇地反直觉的方法,它揭示了稳定性的力量。诀窍是执行一系列排序,但要按重要性的逆序进行。

想象一下我们的体育联盟积分榜。为了先按降序排列的胜场数 (WWW),再按降序排列的净胜分 (PPP) 来给球队排名,我们首先按次键 PPP 对整个列表进行排序。结果是一个纯粹按净胜分排序的列表。现在,我们拿这个列表进行第二次​​稳定​​排序,这次是按主键 WWW。稳定性的魔力在于:当第二次排序遇到两支胜场数相同的球队时,它不会重新排序它们。它会说:“我看到你们俩在这个键上是相等的,所以我会尊重你们进入时的顺序。”而那个顺序是什么?正是我们在第一遍排序中精心建立的顺序——按净胜分排列的顺序!最终结果是一个完美的字典序排名,不是通过一个复杂的规则,而是通过两个简单的规则实现的,而稳定性是其中必不可少的粘合剂。

这个原则是数据操作的基石。有时,宇宙会给我们一点帮助。考虑一个社交媒体信息流,它必须按降序的参与度分数显示帖子,但要通过显示最新的帖子来打破平局。如果帖子从服务器传来时已经是按时间倒序排列的(一种非常自然的情况),我们甚至不需要两次排序。一次按参与度分数的​​稳定排序​​就足够了。对于分数相同的帖子,它们预先存在的 chronological 顺序将被优雅地保留下来。算法的稳定性利用了输入数据的固有顺序,免费完成了一半的工作。

机器中的幽灵:确定性、公平性与正确性

当我们放弃稳定性时会发生什么?当我们使用一个将具有相等键的项目视为真正无法区分、可以任意打乱的不稳定算法时会发生什么?我们把一个幽灵请进了我们的机器:非确定性。而这个幽灵会引发非常现实的麻烦。

考虑一个数据去重管道,其设计目的是处理一串记录,并为每个键只保留第一个唯一的条目。如果我们使用​​稳定​​算法按键对数据进行排序,任何给定键的记录都将保持其原始的到达顺序。排序后分组中的“第一个”记录保证是输入流中“第一个”出现的记录。这个过程是确定性的:在相同的输入上运行一百万次,你会得到相同的输出。但如果我们使用​​不稳定​​排序,每个等值键组内的记录可能会在每次运行时被不同地打乱。“哪个”记录是“第一个”就变成了掷骰子。管道的输出不再可预测,这在数据工程中是头等大罪,因为可复现性至关重要。同样的问题也困扰着像会话化这样的任务,其中将用户的日志条目分组为“会话”需要一致的、按时间顺序的排序。对用户ID进行不稳定排序会打乱时间戳,使最终的会话数据变得毫无意义。

这种“先到先得”的概念不仅仅是一个技术要求;它通常也是一个公平原则。想象一个招生办公室,申请材料按分数排名。一项道德政策可能会规定,对于分数相同的申请者,申请较早的人应该排名更高。这项政策就是稳定排序的定义。使用不稳定算法将直接违反这项政策,导致我们可能称之为“排名波动”——对同样合格的候选人进行任意重新排序。系统的感知公平性被打破了。我们甚至可以量化这种“错误”。在分布式系统中,事件通常带有时间戳记录下来。如果两个事件具有完全相同的时间戳,它们的接收顺序提供了一个因果关系的决胜条件。对时间戳进行不稳定排序会打乱这个因果链,我们可以计算出这引入的成对“因果关系错误”的期望数量。不稳定性有其可衡量的成本。

有时,后果是立即可见的。在计算机图形学中,一种称为画家算法的经典技术通过按对象的深度(zzz 坐标)排序并从后向前绘制它们来绘制3D场景。对于那些共面、共享相同 zzz 坐标的对象会发生什么?稳定排序会保留它们的初始顺序,从而得到一致的描绘。然而,不稳定排序可能会在不同帧之间打乱它们的绘制顺序。结果是一种分散注意力的视觉瑕疵,称为“Z-fighting”(深度冲突)或闪烁,其中两个表面似乎在闪烁并争夺可见性。在这里,不稳定性不仅仅是一个抽象的错误;它是最终产品中一个刺眼的缺陷。

排序的前沿:高风险应用

排序稳定性的触角延伸到计算机科学的最深角落,并深入到现代经济系统的核心。

在算法的理论分析中,考虑用于在图中寻找最小生成树(MST)的 Kruskal 算法。该算法按权重对所有边进行排序,如果边不形成环路,则将其添加到树中。如果一个图有多条权重相同的边,不稳定排序可能会在不同的运行中以不同的顺序处理它们。虽然任何最终生成的树仍将具有相同的最小总权重(一个优美且基本的定理),但树中的*边集*可能会改变。在同一个图上运行两次相同的算法可能会产生两个不同的最小生成树!此外,算法使用的数据结构的内部状态,如并查集结构中的父指针,最终可能完全不同。这揭示了一个深刻的真理:获得相同的“答案”并不意味着底层的计算过程是相同的。

也许这场辩论最现代、风险最高的舞台是在区块链技术中。在构建一个新区块时,“区块构建者”必须从一个等待池(“内存池”)中选择并排序交易。主要的排序键通常是交易费,费用越高的优先级越高。但对于费用相同的交易怎么办?如果排序算法是​​不稳定​​的,构建者就可以随意重新排序这些费用相同的交易。这种自由允许他们进行抢先交易或三明治攻击,从用户那里榨取价值,这种做法被称为最大可提取价值(MEV)。然而,如果协议强制要求​​稳定​​排序——例如,保留它们从内存池中到达的顺序——这种重新排序的权力将被消除。一个简单的算法选择变成了一个经济政策工具,能够使系统对每个人都更公平、更可预测。

从一个简单的数字列表到我们数字经济的整体架构,排序稳定性的问题远非一个微不足道的细节。它是关于我们所构建系统特性的一个根本选择。它迫使我们去问:当事物相等时,我们珍视什么?我们是珍视历史的原始顺序吗?我们珍视可预测性吗?我们珍视公平吗?还是我们将结果交给偶然,交给机器中那个任意的幽灵?稳定排序这个简单而优雅的概念,为那些选择前者的人提供了一个强大的工具。