这个标题的一个名字叫做『Boyer-Moore Majority问题极其扩展形式』。但是这也许会让人觉得不那么有趣。
以囚徒的形式来描述这个趣题的方法来自顾森的《浴缸里的惊叹》
典狱长给100名囚犯每个人发了张纸条,纸条上写了一个1到100以内的数,然后告知囚犯有一个数超过了一半。然后每名囚犯要求依次进入一个房间,房间里有两个旋钮,每个旋钮100档。囚犯可以任意转动旋钮,然后离开房间。一开始这两个旋钮都指向1档。如果最后一名走出房间的囚犯正确说出了超过一半的那个数,则100名囚犯都被释放。一旦游戏开始,囚犯间就不许进行任何交流。囚犯在游戏前可以商量一个对策,他们有办法获得胜利么?
这个问题的思路在于如果有一个超过一半的数,则每次去掉两个不同的数,这个数仍然超过一半。例如100个数中,x有51个,如果去掉一个x和一个y,则x有50个,其他数有48个,x仍然超过半数;如果去掉一个y和一个z,则x有51个,其他数有47个,这个结论也成立。囚犯们要做的,就是每次去掉两个不同的数。
记两个旋钮为A和B:
第一个囚犯走进房间,将旋钮A转动到他手里纸条上的那个数,同时将旋钮B转动到第2档;
以后每名囚犯走进房间,都先检查旋钮A的档位和自己手里的数是否相同,如果相同,就将旋钮B再转动增加一档;否则,如果不同,就将旋钮B减少一档,如果发现旋钮B已经是1档了,就将旋钮A转动成自己手里这个数的档位,然后将旋钮B转动到2档。
最后一名囚犯走出房间后,旋钮A上的数字就是超过了一半的数。
请读者思考,为什么这个方法相当于囚犯每次去掉了两个不同的数呢?
到此并没有结束,在上次100个囚犯释放后,典狱长决心改变一下游戏,他又叫来了100名囚犯,发给了写有数字的纸条,但是这次他宣布,有一个数超过了1/3,并且房间里有4个旋钮。囚犯们先依次进入房间一轮,然后再依次进入房间一轮,最后走出房间的囚犯要说出这个数。囚犯们这次要怎么做呢?
一般地说,如果给出n个数,希望找出一个出现次数超过1/k的数,如何只用2k-2个变量,仅仅检查两遍就找出来呢?(为什么要两遍?)
解决的思路是每次去掉k个不同的数,因此可以用k-1个变量记录可能的候选数,另用k-1个变量记录每个候选的个数。
每检查一个数的时候,先检查它是否是k-1个候选数中的一个,是的话,就把那个候选数对应的个数增加1。否则,说明这是一个不同的数,我们将k-1个计数都减少1。这实际上相当于去掉了k个不同的数。如果某个候选的个数已经是0了,就把这个新数记录下来。
最后,我们会得到k个候选数。需要再检查一遍这n个数,以决定这k个候选中,哪个才是超过1/k的一个。
【参考】
https://en.wikipedia.org/wiki/Boyer–Moore_majority_vote_algorithm
以囚徒的形式来描述这个趣题的方法来自顾森的《浴缸里的惊叹》
典狱长给100名囚犯每个人发了张纸条,纸条上写了一个1到100以内的数,然后告知囚犯有一个数超过了一半。然后每名囚犯要求依次进入一个房间,房间里有两个旋钮,每个旋钮100档。囚犯可以任意转动旋钮,然后离开房间。一开始这两个旋钮都指向1档。如果最后一名走出房间的囚犯正确说出了超过一半的那个数,则100名囚犯都被释放。一旦游戏开始,囚犯间就不许进行任何交流。囚犯在游戏前可以商量一个对策,他们有办法获得胜利么?
这个问题的思路在于如果有一个超过一半的数,则每次去掉两个不同的数,这个数仍然超过一半。例如100个数中,x有51个,如果去掉一个x和一个y,则x有50个,其他数有48个,x仍然超过半数;如果去掉一个y和一个z,则x有51个,其他数有47个,这个结论也成立。囚犯们要做的,就是每次去掉两个不同的数。
记两个旋钮为A和B:
第一个囚犯走进房间,将旋钮A转动到他手里纸条上的那个数,同时将旋钮B转动到第2档;
以后每名囚犯走进房间,都先检查旋钮A的档位和自己手里的数是否相同,如果相同,就将旋钮B再转动增加一档;否则,如果不同,就将旋钮B减少一档,如果发现旋钮B已经是1档了,就将旋钮A转动成自己手里这个数的档位,然后将旋钮B转动到2档。
最后一名囚犯走出房间后,旋钮A上的数字就是超过了一半的数。
请读者思考,为什么这个方法相当于囚犯每次去掉了两个不同的数呢?
到此并没有结束,在上次100个囚犯释放后,典狱长决心改变一下游戏,他又叫来了100名囚犯,发给了写有数字的纸条,但是这次他宣布,有一个数超过了1/3,并且房间里有4个旋钮。囚犯们先依次进入房间一轮,然后再依次进入房间一轮,最后走出房间的囚犯要说出这个数。囚犯们这次要怎么做呢?
一般地说,如果给出n个数,希望找出一个出现次数超过1/k的数,如何只用2k-2个变量,仅仅检查两遍就找出来呢?(为什么要两遍?)
解决的思路是每次去掉k个不同的数,因此可以用k-1个变量记录可能的候选数,另用k-1个变量记录每个候选的个数。
每检查一个数的时候,先检查它是否是k-1个候选数中的一个,是的话,就把那个候选数对应的个数增加1。否则,说明这是一个不同的数,我们将k-1个计数都减少1。这实际上相当于去掉了k个不同的数。如果某个候选的个数已经是0了,就把这个新数记录下来。
最后,我们会得到k个候选数。需要再检查一遍这n个数,以决定这k个候选中,哪个才是超过1/k的一个。
【参考】
https://en.wikipedia.org/wiki/Boyer–Moore_majority_vote_algorithm