每期一题之5:大力出奇迹

上期题目:矩阵翻转

解答:显然一个必要条件是两个矩阵里的元素必须是一致的,如果不一致可以直接判定不可能了。但这明显还不够。

此类问题通常有一个入手点就是看看经过操作之后不变的是什么。对于本题,一个重要的观察是如果我们把矩阵如国际象棋棋盘一样黑白格交替染色,会发现翻转操作不会改变数字所在的格子颜色,黑格数永远是黑格数,白格数永远是白格数。那么自然的猜测就是,两个矩阵的黑格元素一致且白格元素一致,是不是可以成功的充分必要条件呢?

答案是肯定的,下面说一个不太正式的证明。实际上我们只需要2×2的翻转操作就可以达到目的。每次2×2翻转操作只交换了两个斜对角相邻的元素,我们可以想象把所有黑格拿出来排成一列,使得相邻的两个格子在原矩阵中是斜对角相邻的(对于非1xN或Nx1的矩阵总可以做到)。那么2×2翻转操作就是交换了序列里相邻的两个元素,显然通过这种交换我们可以得到任何一种可能的排列(想一下冒泡排序)。对白格同理。于是只要黑白格的元素分别是一致的,就一定可以达到。注意要特殊判断一下1xN的情况。


本期题目:大力出奇迹

我们都知道台球界有一句格言叫“大力出奇迹”(并没有),只要力气够大,总能把球蒙进去。下面从数学角度研究一下这个问题:在台球桌上放一个球,沿直线击出,球如果没有进洞就永远前进,撞到边后的反弹和光的反射一样入射角严格等于出射​角。球看作一个点,洞也看作点。

问题一:如果球永远不能进洞,那么一定是出现了循环么?(循环指以同一个方向回到以前经过的位置,比如平行于桌边轰一发,那么永远来回反弹)有没有可能不循环但是又永不进洞​?

问题二:假如随机选一个角度轰一发,球最终进洞的概率是多少?循环的概率是多少?

​问题三:存不存在一条永不进洞的线路,能经过桌面上任意一​点?

​问题四:稍微改一下设定,现在洞不是点,而是一个半径非常非常小的圆,当球和洞心的距离小于洞的半径时我们就认为球进洞​了。在这种情况下,随机选一个角度轰一发,球最终进洞的概率是多少?

这个其实是数学题,和算法没啥关系了,不过实在是很有意思所以发出来。来源记得是看到某篇公众号文章说的华尔街交易公司面试题,重新整理了一下。

每期一题之4:矩阵翻转

上期问题 每期一题之3:猜对错

解答:

第一问比较容易,我们可以胡乱猜一个作为基准,比如就猜所有都是T。然后每轮仅把一题的答案改为F,看看新的结果和基准比是大了还是小了,大了说明这题应该是F,否则说明应该是T。这样用N+1轮得到所有结果。

想要仅用kN轮(k<1)有多种方法,基本思路是把题目每m个分成一组,每次用n轮提问确定m个答案(n<m)。下面是官方题解中用两轮确定三个答案的方案:

首先用两轮猜测TTTTTT…T和TFTFTF…TF作为基准一和基准二,设这两个基准值分别为x和y。我们把相邻三个题目作为一组,对于每一组,第一轮猜测时,以基准一为模版,只把对应组里前两题答案改为F,其他全不变。把结果与x值对比,那么有三种情况

1) 如果结果为x-2,说明前两题答案为TT。然后再简单用一轮提问确定第三题

2) 如果结果为x+2,说明前两题答案为FF。同样再用一轮确定第三题

3) 如果结果为x,说明前两题答案为TF或FT。此时,我们发起第二轮提问,这一次是以基准二为模版,把对应三道题的答案反转(不妨设本来是…TFT…,变成…FTF…),然后把结果与y值比较,那么我们会发现有四种情况:

3.1) 结果是y+3,说明这三题答案为FTF

3.2) 结果是y-3,说明这三题答案为TFT

3.3) 结果是y+1,说明前两题改对了,第三题改错了,这三题答案应为FTT

3.4) 结果是y-1,说明前两题改错了,第三题改对了,这三题答案应为TFF

综上,我们可以用每两轮搞定三题,外加两轮基准,还可能有最后一组不足三题的特殊处理。总之大约就是n*2/3轮查询。

第三问非常难,理论上说,总的可能性是2^n种,也就是n个比特的信息量。我们每轮询问得到一个数字n,即log(n)比特信息量,那么问题轮数的理论下界为O(n/log(n))次。这个下界是可以达到的,但是题解我看不懂……  题解可以点击下方“原文链接”查看,哪位看懂了可以给我解释一下。


本期题目:矩阵翻转

给定一个M*N的起始矩阵和一个M*N的目标矩阵,我们可以在起始矩阵上进行如下操作:选择一个K*K的子方阵(k<=M, k<=N),把这个方阵的元素沿对角线翻转,注意可以沿两条对角线的任一条。问能否经过若干次操作,把起始矩阵变成目标矩阵。只需判断是否可以,不必输出具体过程。

例如,如果起始矩阵是[[1 2 3] [4 5 6]],目标矩阵是[[1 4 3] [6 5 2]],那么可以经过两次操作得到。先把右边2×2子方阵沿反对角线翻转,再把左边2×2子方阵沿主对角线翻转即可。

\begin{bmatrix}1 & 2 & 3 \\4 & 5 & 6\end{bmatrix}\rightarrow\begin{bmatrix}1 & 6 & 3 \\4 & 5 & 2\end{bmatrix}\rightarrow\begin{bmatrix}1 & 4 & 3 \\6 & 5 & 2\end{bmatrix}

来源:Codechef August Long 2022, Angled Flip

每期一题之3:猜对错

上期题目:每期一题之2:猜数字

如果是均匀分布,直觉上就应该是类似二分搜索的方法,每次一定能把范围缩小一半。比如N=8时,第一轮先问是不是1<=N<=4,如果第一轮答是,第二轮则问是不是1<=N<=2;如果第一轮答否,第二轮问是不是5<=N<=6,依此类推。易知N=8时需要三轮。

当N较小时,有竞赛经验的同学可能容易想到一个“状态压缩动态规划”的算法。简单来说,对于一个集合S,我们可以设ans[S]表示已知结果一定在S之内的情况下,平均需要问多少次。那么我们最终要求的就是ans[{1,2,…N}]。这个东西可以递归来求,要计算ans[S],我们可以枚举S的所有子集T来询问,我们用p[T]表示集合T中所有数的概率之和,那么我们有p[T]/p[S]的概率得到“是”的回答,然后我们递归求ans[T];有1-p[T]/p[S]的概率得到“否”的回答,然后我们递归求ans[S-T]。写成方程式是这样的:

ans[S]=1+\min_{T \subset S}(\frac{p[T]}{p[S]}ans[T] + \frac{p[S-T]}{p[S]}ans[S-T])

边界条件可以是当S中只有一个元素时,结果为0。

在递归的过程中我们可以把每个子集的结果记录下来防止重复计算,这个就是动态规划的思想。然而容易发现一共有2^N个状态,计算每个状态时也要枚举指数级别的子集,所以复杂度还是指数级,只能对比较小的N才可行,1000是绝不可能的。

实际上这题的最优解法是贪心。一个直观的思考方向是,如果一个数出现的概率越大,则应该花较少的步骤确定它,而比较罕见的数则可以用较多的步骤。实际上这个问题等价于Huffman编码问题,即给定一篇文档中每个字母出现的频率,给每个字母设定一个二进制编码,使得没有任何一个字母编码是另一个字母的前缀,要求文档的总编码长度最短。算法是逐次合并,一开始每个字母自己是一个集合,每个集合的权值就是字母频率。每次选出权值最低的两个集合,把它们合并起来,得到的新集合的权值为原来两个的权值和。这样重复操作直到只有一个集合。这个过程中就创建出一个二叉树,每个叶子节点是一个字母。我们给每个点的两条边分别标上0和1,这样每个字母的编码就得到了。

Huffman编码示意图 from Wikipedia

对应到本题,就是从二叉树的根开始,每次问是不是在一个子节点所对应的子集中,根据回答走向不同的分支。

正确性的证明可以用数学归纳法,这里就略过了,网上可以找到很多。算法用堆实现的话,复杂度是O(NlogN)。


本期题目:猜答案

试卷上面有N道判断题,你每次给出一组答案,我告诉你这次做对了几道,但不会告诉你具体是哪几道。你可以猜很多轮。设计一种方案用尽可能少的轮数做对所有的题。

比如有四道题,真正的答案是TTFF。你如果猜TFFF,我会告诉你对了3道。你如果猜FFTT,我会告诉你对了0道。真正的N会是一个比较大的数,比如几百道,所以乱蒙是效率太低的。

问题1:能否用大约N轮猜出?
问题2:能否用大约k*N轮猜出(k是一个小于1的常数)?
(附加题)问题3:能否用渐近意义上少于线性的轮数猜出?(用小o记号的话就是o(N)次)

题目来源:Codeforces #807 Div 2 Problem F

每期一题之2:猜数字

上期题目:每期一题之1:小朋友开party

题解:(先定义一个概念:图中一个点的(degree),指的是有多少条边与这个点相连。)

显然,如果图中一共有偶数条边,那么直接满足要求,不需删任何点。下面只考虑有奇数条边的情况。

情况一:如果我们删掉一个奇数度的点,那么同时会删掉奇数条边,那么就得到了一个有偶数条边的图。在这种情况下,我们当然是删掉权值最小的那个点。而且容易看出,如果我们删除的点集里包含奇度点,那么只需删除一个奇度点即可,再多的都是多余操作。

情况二:在情况一以外,另外一种可能就是我们只删偶数度的点。这时可以发现,如果我们只删除一个偶度点,或者多个不相连的偶度点,是没有作用的,结果还是奇数条边。只有删除两个相连的偶度点(即删除一条两端都是偶度点的边),才会改变边数的奇偶性。同理,只需删一条这样的边即可,再多都是多余操作。

所以最终算法就非常简单了,我们先统计所有点的度,然后选中最小权值的奇度点,记为v1;再考察所有满足“两个顶点都是偶数度”的边,选中权值最小的那条边(边的权值为两个顶点权值之和),记为v2。v1和v2的较小者即为最终答案。算法时间复杂度为O(E)。


本期题目:猜数字

这是多年以前我毕业时面试国内某司被问的一道题,题目本身非常的不错,虽然未必适合拿来当面试题。当时能做出来我自己也是很高兴的 😀

我们两人玩一个猜数字的游戏。我根据一个给定的概率分布(这个分布我们两人都知道),在1~N里随机选一个数字,你通过问一些问题,猜出这个数字是多少。你的问题是类似“这个数字是奇数吗?”或者是“这个数字是4或者6或者9吗?”,更形式化地说,你每次询问一个集合,我告诉你我选的数是不是在这个集合内。现在要求是设计一个算法,对于一个给定的概率分布,得到一个最优的猜数方案,使得问题轮数的期望(aka.平均要问多少个问题)最小。

例子:为了简单起见,假设只有3个数,概率分布是[p1=0.1, p2=0.1, p3=0.8]。那么你的最优策略应该是先问,这个数是不是3。我有80%的概率说是,直接结束;有20%的概率说不是,这样你要再花一次机会。所以总的问题轮次的期望为 0.8 * 1 + 0.2 * 2 = 1.2

问题(1):若N=8,且8个数字是均匀分布,每个都是1/8的概率,怎么做?

问题(2):若N<=10,概率分布任意,怎么做?

问题(3):若N<=1000,怎么做?

FLP不可能性定理

说起分布式系统里的不可能性,大家第一反应应该是CAP理论。但是我们这篇不谈CAP,而是另一个关于共识算法的FLP定理。以后有机会的话再写写CAP以及这两个的关系。

FLP是三位作者的名字首字母。这个定理的内容是,在异步网络下,如果有一台机器可能出错,则没有任何确定性共识算法保证在有限时间内结束。这是一个相当强的结论,比起更著名的CAP理论,FLP不可能性定理实际上更不显然,也更有意思。

这里面有很多定义要首先明确:

  • 异步网络。很不幸“异步”又是一个被滥用的词,在分布式系统理论中,异步网络指的是,系统中每个节点的执行速度不必一致,消息从一个节点传播到另一个节点的时间不确定,没有一个全局可见的准确时钟。在本论文的设定里,消息不会重复,不会丢失,只是可能会(有限)延迟。
  • 出错。常见的出错类型有fail-stop, fail-recovery以及拜占庭错误。fail-stop指的是一台机器一旦出错就卡住不动了,fail-recovery指的是机器出错后会重启,从头开始执行,拜占庭错误指的是机器出错后进入混乱,可能乱给别人发消息,甚至是故意发误导性的消息(比如被黑客入侵了)。本论文里假定是fail-stop。
  • 共识算法。本论文里讨论的是一个相当精简的共识问题:每个节点有一个初始值为0或1,经过一系列互相发消息沟通之后,每个节点都会做出一个决定,输出一个0或者一个1,一旦做出决定就不能再改。一个正确的共识算法要满足如下要求:
    • 不同节点不能做出不一致的决定
    • 需要在有限步数内完成
    • 共识算法需要既有可能决定为0,也有可能决定为1。
      (最后这一条是为了避免一种耍赖的方法,比如不管三七二十一通通决定0值,这样算法就没有意义。我后文把这一条称作“非trivial性”要求。)

下面我们把整个框架形式化一下,这个对于后面的证明是非常必要的:

我们的系统里有N个进程(N>=2),每个进程有一个1-bit的初始输入寄存器,有一个输出寄存器,取值为{b,0,1},一开始的时候值为b,表示尚未做出决定,在执行过程中这个输出值只可以写一次,写成0或1,表示相应的决定。每个进程还有自己的内部状态(aka内存)。

进程之间通过发消息交互。一条消息是一个(p,m)对,p表示消息的目标进程,m是消息内容。消息会放到一个统一的池子里,于是有两种操作:
– Send(p,m): 把消息(p,m)放到池子里
– Receive(p): 从池子里取一条发给p的消息,返回m给进程p,并把这条消息从池子里删除;或者返回一个空消息给进程p,注意即使这时池子里有给p的消息,也有可能不马上把这个消息给p,即模拟消息出现延迟的情况。但如果Receive(p)重复执行无限多次,所有给p的消息都会被p收到。

一个格局(原文是configuration,我瞎翻译的)指的是某一时刻所有进程的状态和消息池的状态的汇总。初始格局是每个进程只有输入寄存器非空,而所有进程内部状态和消息池都为空。算法执行过程就是连续从一个格局到另一个格局的转移,每次称为一。每一步只涉及某单个进程p,先是执行Receive(p)得到一条发给p的消息或者空消息,然后进程p根据得到的消息和自己的内部状态,决定如何操作,包括改变自己的内部状态,放消息到消息池,和/或作出0/1决定。

注意我们讨论的是确定性共识算法,就是说一个格局C转变成什么新格局,是由从消息池取到什么消息唯一决定的。所以我们也把收到消息(p,m)称为一个事件e,用e(C)表示格局C经过事件e以后变成的新格局。我们把一系列连续的事件称为一个事件序列(原文是schedule,我又瞎翻的),常用\sigma表示。

先说一个比较显然的引理:如果两个事件序列\sigma_1, \sigma_2涉及的进程没有交集,那么从一个格局C先经过\sigma_1再经过\sigma_2,和先经过\sigma_2再经过\sigma_1,到达的是终点格局是相同的。这一点比较好理解,因为这两个序列涉及的是互不相关的进程,异步网络里不同进程收到消息的先后顺序本来就是不确定的,所以谁先谁后不应该对结果有任何影响。后文我把这个称做“交换律”。

交换律示意图

下面开始真正的证明。我们用反证法,假设这样一个共识算法P存在,然后推导出矛盾。

对于一个格局C,如果因为之后发生的事件序列的不同,既有可能决定为0,也有可能决定为1,我们就称C为双值(bivalent)的。否则,就是不管后面事件序列怎样,都只能决定为0,就称为0-单值;如果只能决定为1,就称为1-单值。

下面我们简单证明,对于任何一个算法P来说,所有可能的初始格局里面一定存在双值格局。也就是说,总有某些初始格局你不能仅根据进程的初始输入就做出决定(比如在一个事先计算好的对应表里查,是不行的),而是要在算法的运行过程中根据消息的顺序不同而达到不同的最终决定。

我们用反证法,假设所有初始格局都是单值格局。因为非trivial性的要求,初始格局里需要即有0-单值格局又有1-单值格局。不妨设一共有N个进程,每个进程的初始输入为0或1,则初始格局共有2^N种。我们可以把这2^N个初始格局排成一列,使得相邻两个格局之间有且仅有一个进程的初始状态不同。这个东西叫做格雷码,总是可以构造出来的(详见Wiki)。比如三个进程的情况下,我们可以把8个初始格局排成000, 001, 011, 010, 110, 111, 101, 100。因为两种单值格局都有,那么序列里必然能找到相邻的两个初始格局属于不同的单值格局。而这相邻的两个格局只有一个进程的初始输入不同。如果刚好这个进程一开始就挂掉了没有任何响应,那么我们的共识算法没有任何办法区分这两种初始格局,也就不可能达成两种不同的最终决定。于是原引理成立。

下面我们证明最重要的部分:设C是一个双值格局,事件e=(p,m)是一个可以发生在C上的事件。我们令ℂ表示从C格局出发不经过事件e可以达到的所有可能格局的集合,令D表示ℂ里面的每个格局都经过事件e得到的格局的集合,则D里一定仍包含双值格局。

这个描述有点绕,我们先看看如果它成立,能达成什么效果。前面引理已经证明了初始格局里一定有双值格局,我们拿一个这样的格局出来,再找一个接下来要发生的事件e,那么我们总可以通过把事件e推后到某个合适的时间发生,使得之后得到的格局仍然可能是双值格局(i.e.未能做出决定的格局)。从这个新双值格局继续同样操作,那么就可以永远留在双值格局,不做出最终决定。这样这个共识算法就不能终止了。

还是用反证法,假设D里不包含双值格局,我们试图推出矛盾。

若D不包含双值格局,我们可以得出D里必须两种单值格局都存在。(1)

这一步的证明是这样的:因为初始格局C是双值的,那么一个存在一个0-单值格局E0和一个1-单值格局E1可以从C到达。我们先看E0,一种可能是E0属于ℂ集合,那么F0=e(E0)就属于D集合,显然F0也是0-单值的;另一种可能是E0不属于ℂ集合,这时一定在D里存在一个F0使得从F0可以到达E0,因为D里只有单值格局,那么F0一定是0-单值的。综合两种情况,D集合里必然有0-单值格局。同样的逻辑对E1也成立,所以D集合里也必然有1-单值格局。故(1)得证。

简化一点来说就是,对于E0,我们一定能在D里找到一个单值格局F0,使得要么能从E0到F0,要么能从F0到E0。下面这个盗来的图形象描绘了这一部分证明:

来源:https://www.the-paper-trail.org/post/2008-08-13-a-brief-tour-of-flp-impossibility/

现在我们知道D里面两种单值格局都有了,因为D里的每个格局都是从ℂ里的某个格局经过一步e得到的,那么我们一定能在ℂ里找到相邻的两个格局C0,C1,使得D0=e(C0)是0-单值格局,而D1=e(C1)是1-单值格局。不妨设是C0经过一步e’得到C1。(若则C1经过一步到C0下面的讨论也同样成立)

此时对e’=(p’,m’)有两种情况,

情况一:若p’不等于p,也就是说e和e’是作用在不同的节点上的。那么根据交换律,交换e和e’的顺序结果应该不变。我们已经知道了C0通过e’到达C1再通过e到达D1。还知道C0通过e到达D0,此时如果让D0接受事件e’,因为满足交换律我们应该也得到D1。但这是不可能的因为D0已经是单值格局不能变了,所以得出矛盾。见下图:

来源:https://www.the-paper-trail.org/post/2008-08-13-a-brief-tour-of-flp-impossibility/

情况二:若p’等于p,此时从C0出发必存在一个不涉及节点p的事件序列R,能达到一个单值格局A。(因为我们的算法要在有一个节点挂了的时候仍能正常运行,现在就好比是p点挂了)现在我们让D0接到序列R,设结果为E0,也就是说E0=R(e(C0))。因为R不涉及p,根据交换律,我们让A接收事件e后,也应该得到E0,也就是e(A)=e(R(C0))=R(e(c0))=E0。类似地,我们让D1接受序列得到E1,也就是说E1=R(e(e'(C0))),同样根据交换律,A接收事件e和e’后也应该得到E1,e(e'(A)) = e(e'(R(C0))=R(e(e'(C0)))=E1。但是,A已经是单值格局了,不可能既能到E0又能到E1,得出矛盾。

来源:https://www.the-paper-trail.org/post/2008-08-13-a-brief-tour-of-flp-impossibility/

综合以上所有这些矛盾,说明我们一开始的假设“D里不包含双值格局”是不正确的。D里必定包含双值格局。所以我们前面说的这个构造无限序列的方法就成立了:

前面引理已经证明了初始格局里一定有双值格局,我们拿一个这样的格局出来,再找一个接下来要发生的事件e,那么我们总可以通过把事件e推后到某个合适的时间发生,使得之后得到的格局仍然可能是双值格局(i.e.未能做出决定的格局)。从这个新双值格局继续同样操作,那么就可以永远留在双值格局,不做出最终决定。这样这个共识算法就不能终止了。

所以原始的命题得证:在异步网络中,只要有一个节点有可能挂,理论上就不存在一定能在有限时间内结束的共识算法。

总结

这个结论是出乎意料的强,所以这篇论文获得了广泛的赞誉。给人的第一感觉是,这么一点点小错误都不让发生,分布式系统还能有个啥卵用。 实际上也并不需要那么悲观,因为真正的网络不可能是完全异步的,这种陷入无限循环可能性微乎其微。后续的很多研究是把“完全异步”这一条件放宽一点点,然后就有很多可以做到的事情了。如果以后有时间我可以写写后续。

论文原文:https://groups.csail.mit.edu/tds/papers/Lynch/jacm85.pdf
参考资料:https://www.the-paper-trail.org/post/2008-08-13-a-brief-tour-of-flp-impossibility/

每期一题之1:小朋友开party

本系列打算选取一些在各种地方遇到的有意思的题目,每次发一道,然后下期发布题解和新题目。本来想叫“每周一题”,但是觉得我不一定能保证频率……题目大多是接近编程算法面试/算法竞赛的思考方式,但我会尽量避免需要太多算法背景知识的题目。难度的话,大概是作为竞赛题是中等偏简单,作为面试题是相当难的,实际上这些应该都不太适合作为面试题,不全是因为难度,主要因为考察的重点不太一样。

废话不多说,小镇做题家GoGoGo——

全班有N个小朋友,你打算邀请全部或一部分小朋友来参加一个party。小朋友两两之间有可能有好朋友关系,如果一对好朋友都出现在party上,他们就要吃一个蛋糕。注意是每对关系对应一个蛋糕,比如ABCD四个人,两两之间都是好朋友且四个人都被邀请了,那么就需要6个蛋糕(AB,AC,AD,BC,BD,CD)。由于某种神秘原因,你只能准备出偶数个蛋糕,所以你可能不得不放弃邀请某些小朋友。每个小朋友有一个正数权重数值,表示如果他没被邀请的话,他的不开心程度是多少。现在要找一个方案使得所有缺席小朋友的不开心程度之和最小。

用图论的语言来说:给定一个无向图,图里的每个点有一个正权值。图里没有重边(任何两个点之间最多有一条边)没有自环(每条边的两个顶点不是相同的)。现在要求删掉图中的若干个点(当然,同时也删去与这些点相连的边),使得刚好剩下偶数条边。要求删掉的点的权值之和最小。

样例1:三个人权重分别为1,2,3,权重2和3的两个人是好朋友。如果全邀请的话需要一个蛋糕,不满足要求。此处最优解是邀请权重1和3的两人,这样不需要蛋糕,不开心值是2。

样例1

样例2:五个人权重分别为1,2,3,4,5,朋友关系如图所示。最优解是邀请3,4,5,不需要蛋糕,不开心值是3。

样例2

样例3:五个人权重是1,2,3,4,100,朋友关系如图所示。一种最优解是邀请3,4,100,需要两个蛋糕,不开心值是3。另一种最优解是只放弃3,需要两个蛋糕,不开心值也是3。另外一种得到偶数蛋糕的方法是只放弃100,这样需要四个蛋糕,但不开心值太高了。

样例3

来源:Codeforces 810 Div2 Problem B

不可能完美的选举 – Arrow不可能定理

这是我以前blog里的一篇老文,觉得有必要保留下来。不要紧张,本文无关政治,是数学。

下面要说的是一个选举的问题,我们规定一些看上去完全合情合理的条件,结果却发现没有任何一种选举制度能够满足。从这个意义上讲,任何一种选举制度都是有缺陷的。

下面把问题形式化一下,有N个投票人,K个候选人。每个投票人根据自己的喜好,把这K个人排个序,设这K!种可能的序列组成的集合为L(K)。一个选举制度,就是N个L(K)的笛卡尔积到一个L(K)的映射。前面这句话是吓人用的,说白了就是,选举制度就是一个算法,输入是N个{1..K}的排列,设为R1,R2…Rn,每个排列表示一个投票人的观点,算法的输出是一个{1..K}的排列,表示结合所有的观点计算出一个最终的投票结果。我们要证明的是,不管你用什么算法在某种意义上都是有缺陷的。

我们觉得这选举算法应该满足如下3个条件:

(1)一致性或帕雷托最优性(unanimity): 如果对于全部N个排列,候选人a都排在b的前面,则最终结果a也应该排在b的前面

(2)非独裁性(non-dictatorship): 不存在这样一个i (1 <= i <= n),使得无论输入是什么,总的结果总和Ri相同。

(3)无关因素独立性(independence of irrelevant alternatives, 以下简作IIA): 对于两组可能不同的输入R1,R2…Rn和S1,S2…Sn,如果对于每个i,候选人a和b的相对顺序在Ri和Si里都一致,则由R1,R2…Rn得出的最终结果和由S1,S2…Sn得出的最终结果中,a和b的相对顺序是一样的。

条件(3)的另一种理解方式为,如果我们只关心K个候选人的一个子集,假设说是M个候选人,那么把投票人的排列里我们不关心的人都划去,剩下的仍保持原来顺序,就好像只有这M个候选人一样。然后用同样的选举算法计算,最终这M个人的顺序和原来考虑全部K个人时这M个人的相对顺序是一样的。

这三个条件看上去似乎都很合理,然而我们可以证明,没有任何一种选举算法能同时满足三条。下面我们就证明,满足(1)和(3)的选举算法,必然不满足(2),也就是说,满足一定条件的民主就变成了独裁…… -_-

证明可以分为三部分:

第一部分:存在关键的投票人

我们考虑N个投票人和三个候选人A,B,C。如果所有投票人都把B排在最后,根据一致性,显然在总排名里B排在最后。(我们把这种状态叫做状态1)同理,如果所有人都把B排在最前,总排名里B就在最前。下面我们考虑从状态1开始,编号从1到N的N个投票人,逐个把B从最后改成最前,每当一个投票人改变一次排列,就重新计算一次总的排名。这样的话,一开始B总排名垫底,到最后总排名最高,这中间必定有个变化的过程。我们会发现,这个变化是直接“跳”上去的,也就是说,在中间的某个投票人改变自己的排列后,B的总排名会突然从垫底跳到最高,没有中间过程。 

我们用反证法,假设存在一个中间过程,也就是说,在某个投票人(不妨设为第n个)改变主意后,B升到了A和C的中间。不妨设此时总排名是C>B>A(A>B>C同理),现在我们把每个投票人的排列里的A都改到C的前面,由一致性可知,总排名里A也应该在C前面。又因为在投票人的排列里B不是在最前就是在最后,我们改变A,C的顺序对B的相对位置没有影响,由IIA知,因为BA, BC的相对顺序都没变,故总排名里BA,BC的相对顺序也都不变。但此时就出现了矛盾,要想让A改在C前面,BA,BC的相对顺序不可能不变。故假设不成立,第一部分得证。

第二部分:存在决定A与C相对顺序的独裁者

继续第一部分的讨论,我们把B的排名发生跳跃之前的状态,也就是前n-1个人把B排在最前,后面的人都把B排在最后的状态,称为状态2,此时总的排名里B在最后。把B的排名发生跳跃之后的状态,也就是前n个人把B排在最前,后面的人都把B排在最后的状态,称为状态3,此时总的排名里B在最前。

下面我们要证的是,这第n个人就是决定A与C相对顺序的独裁者,他说谁在前面谁就在前面。

设第n个人的投票是A>B>C,此时如果我们只考虑A和B,把C去掉,我们会发现A,B的相对顺序和状态2里是相同的(前n-1个人A<B,后面的人A>B),于是根据IIA,A-B的最终相对顺序也与状态2相同,也就是说,A应该在B前面(因为状态2里B最终在最后)。同理,如果我们只考虑B和C,我们发现B,C的相对顺序和状态3里是相同的(前n个人B>C,后面的人B<C),于是B-C的最终顺序与状态3相同,B应该在C前面(因为状态3里B最终在最前)。于是最终顺序就确定了,是A>B>C。

若设第n个人的投票是C>B>A,同理可得最终顺序是C>B>A。

现在我们只考虑A-C相对顺序,把B完全忽略掉,可以发现A-C的相对顺序是由这第n个人决定的。

注意这里是我认为整个证明里最难理解的一点:根据IIA,在我们决定A-C相对顺序时,B是无关紧要的。尽管我们的证明里出现了B,但最终的结论里没有B的事,引入B只是为了证明独裁者的存在性。在这里我猜很多人都会有的一个疑问是:我们是在如此特殊的状态下才证明了最终结果取决于第n个人,为什么可以得出在所有状态下都成立?如果我不是让前n-1人都把B排最前而后面的人都把B排最后,而是随便乱给一个输入状态呢?(如果你没有这个疑问,说明你的智商远高于我,这是一件概率非常大的事,那样就可以跳过下面的这段解释了)

解释是,我们确实可以乱给一个输入,根本不是前面状态1状态2状态3的样子,但因为现在只考虑A-C相对顺序,我们可以从这个乱给的输入里把其它的候选人都拿掉,结果是不变的,再把B按照状态1的情况插进去,结果还是不变,再逐个地把B从最低提到最高,结果还是不变,但是当总排名里B出现跳跃的时候,独裁者(那第n个人)就找到了。如果我们把此人对A,C的排名交换位置,则总排名里A,C也交换位置。这时你可以再把所有的B都改回一开始乱给的那种输入下的位置,由IIA知B的位置是不影响A-C顺序的。这样我们又回到了原来的输入,只是交换了第n个人对A,C的排名,结果总结果里就跟着变化了。所以第n个人确实是A-C顺序的独裁者。

第三部分:独裁者只有一个

这一部分的证明最简短了。第二部分我们只证明了决定A-C顺序的独裁者存在,当然同理决定B-C顺序和A-B顺序的独裁者也存在,现在的问题是这些独裁者是不是同一个人?

假设A-B独裁者x和B-C独裁者y不是同一个人,则由这两个独裁者的意见就可能决定了A-C的顺序。比如x投票是A<B,y投票是B<C,则必有A<C。但我们知道还有一个A-C独裁者存在(可能是x,y中的一个,也可能是另外一个人),如果他的投票是C<A呢?由这个矛盾可知,三个独裁者只能是同一个人。

最后我们把问题扩展到大于三个候选者,由IIA,前面的所有讨论对任意一个包含三个候选者的子集都仍然成立,于是每个三元组都有一个独裁者。由类似第三部分的证法易知,所有这些独裁者都是同一个。于是总的命题得证。

后记:

这其实是博弈论里的一个叫做Arrow不可能性定理的东西,我只是把这个Wiki页面翻译了一下,稍微加入一点自己的理解。关于这个定理网上能搜到很多介绍,不过大都没有涉及具体的证明过程。我觉得这个证明还是颇为锻炼逻辑思维能力的,而且不需要高深的数学,每一步都只是简单的逻辑推理,最终得到了神奇的结论。

这个定理能说明一切选举都无效么?显然不是的。实际上现有的选举制度都是对前面的三个条件作出了妥协的结果,但是他们有很多都工作得很好。三个条件里限制最强的就是IIA,我们也发现了在上面的证明中IIA无处不在,而现有的选举制度大都不满足IIA的要求。关于此问题的后续研究很多都致力于探索如何适当放宽IIA的要求来更准确地评价选举制度的好坏。感兴趣的可以深入看看wiki里的参考文献,我也不懂就不多说了。