小球称重问题:用天平称量几次才能找到有问题的球并判断轻重?

在n个外观完全相同的小球中,有一个与其它球重量不同。如何只用一架天平找到这个球并判断它比其它球轻还是重?最少需要称几次?

这是一个流传极广的经典问题,在网上随便一搜就会发现无数人都提出了自己的见解和算法并争论不休。最常见的是12个小球,至于更多球的计算,就不是人力能及的了。

实际上,这个问题确实是有准确答案的:n次称量最多可以在个球中找到不同的球,并判断它的轻重。
理论证明可参考The Problem of the Pennies, F. J. Dyson, The Mathematical Gazette , Vol. 30, No. 291 (Oct., 1946), pp. 231-234。
不过这里不打算讲那么多理论,而是直接演示怎么称量并找到这个小球。

算法十分精巧,我尽可能简单地描述了一下。如果还是觉得太抽象,没关系,直接点击这里或者拉到下面看演示就可以了。

  1.  编码
    知道了球数,就能算出需要称量几次;
    以这个次数作为长度,使用0、1、2排列组合进行编码,如001021、212022等等,再去掉全0、全1和全2,可知一共有个编码;
    如果在一个编码中,第一处相邻数字不同的情况是01、12或20,则我们称它为正序码,如1120021;
    否则为逆序码,如2221012;
    在长度为n的编码中,正序码和逆序码的数量相等,均为个。
  2. 赋值
    如果把一个正序码中的0换成1,1换成2,2换成0,则它仍然是正序码;
    根据这个原理,我们把所有正序码按3个3个进行分组,如12001、20112、01220这3个就是一组;
    把正序码一组一组地分配给小球,每球一个,直到分完;
    然后把每个正序码的0换成2,2换成0,它就变成了一个逆序码,如12001变成10221;
    这样,每个小球就有了两个编码,一个正序,一个逆序,而且所有球都不重复。
  3. 称重
    第一轮,我们把所有正序码第一位为0的小球放在天平左侧,为2的小球放在右侧,其它的放在旁边;
    如果天平左倾,记为0;右倾,记为2;平衡,记为1;
    然后是第二轮,把第二位为0的小球放在左侧,为2的放在右侧,同样记下称量结果;
    每一轮都按这个顺序进行,一共要称n次,最终结果是个n位的编码;
    如果编码等于某个小球的正序码,则这个小球比其它球重;
    如果编码等于某个小球的逆序码,则这个小球比其它球轻。

下面就是这个算法的完整演示:

首先输入球数,电脑会自动给所有球编码;然后手工设置某个特殊球是轻还是重;接下来就会自动分组称量,最终找到特殊球,看看是否和你所设置的那个一致。
在本演示中,绿色代表轻;红色代表重。

补充说明:

  1. 如果小球的数量是3的倍数,则上面的算法完全没有问题,但如果不是3的倍数,则要做如下处理:
    小球数量除以3,余1,则给多出来的这个球分配以1开头的编号;
    小球数量除以3,余2,则给多出来的这两个球分配一组编号中以0和2开头的两个。
    这样的话,第一轮称重的天平左右两侧球数必然相同。
    而且每一轮称重后,都会有1/3或2/3的球被证明是重量正常的。所以如果后面出现天平左右球数不等,就找一个正常球补到少的一边,或者从多的一边取走一个正常球即可。
  2. 此算法适用于大于3的任何球数,限于浏览器性能,最多可输入999个;
  3. 本演示程序参考了知乎问题有999个乒乓球其中有一个和其他的质量不同,问你用天平最少几次一定能称出来?答案中洪涛提供的文章陳浩的算法解释,在此特别致谢。
27+

20 comments

    • 苏莉安 says:

      3次这个问题是chromium浏览器内核的bug,明明是整除结果出了小数位。我加一层过滤之后就解决了。
      源码已经有了,这么多年居然一直都是混淆过的版本,没注意。

      0
  1. 求知er says:

    作者你的表述要严格一点呀,大标题是找到有问题的球,但正文里面却是找到有问题的球并判断轻重。这两个是有实际影响的,比如不考虑特殊球的轻重,两次称重最大可以在四个球中找到特殊的那个;要是考虑轻重,则两次称重最大只能在三个球中找到特殊的那个(就是那个公式)。

    0
    • 求知er says:

      那个文献不太好找,因此也就只能默认了。
      不过问题是,如果不考虑那个特殊球具体是轻是重,称n次最大可以从多少个球中找到特殊的那个呢?(看网上的视频,据说在不考虑轻重的情况下,可以在13个球中称三次找到特殊那个)

      1+
  2. Test says:

    公式错了,n次最多可以从(3^n-1)/2个球中找出质量不同的小球,n>2。
    譬如13个小球,3次就可以找出质量不同的那个球。

    0
  3. 曾加 says:

    这个方法很通用,但我想了想,实际操作性可能不是太强,如果现实中有 很多个球,如果不依赖计算机,光三进制编号就能把人弄晕了。

    感觉这类问题更适合用动态规划,这样每次称球的数量可以减少,到最后一步就 1 vs 1 ,这样也更加符合普通人的思维方式。

    利用原论文的证明,我在这篇文章的最后给出了动态规划的方法,可能更加直观一点:https://zhuanlan.zhihu.com/p/368050985

    0
    • 苏莉安 says:

      编号方法很不错。但我觉得现实中最常见的12个球问题,可以直接把顺序给背下来唬人用……如果真的太多还是上计算机为好

      0
  4. 后楚 says:

    博主你好,https://freemind.pluskid.org/machine-learning/a-compressed-sense-of-compressive-sensing-ii/ 这里提到的用矩阵方法来解决。通过矩阵的方法,似乎更为直观地找出有问题的硬币。我归纳了一下,这个求矩阵的问题:输入x(x是大于等于3的整数),得到一个n行x列的矩阵,n=⌈(2x+3)^1/3⌉ (向上取整,比如x=12,n=3;x=38,n=4;x=66,n=5)。这个矩阵满足:
    1、不能出现全零的列;
    2、任意两列不同;
    3、任意两列的和不为零向量;
    4、元素只能是-1,1或0;
    5、每一行元素的和为0。
    我是编程小白,请问博主,你能实现这个么?

    0

曾加进行回复 取消回复

电子邮件地址不会被公开。 必填项已用*标注