小球称重问题

  • 问题重述

    给定n枚硬币,其中有一枚假币,它与真币重量不同,但不知道是轻还是重。现有一个无砝码的天平,问要找出假币,并确认假币至少要称几次,并给出可视化编程。
  • 问题分析

    此问题是经典的信息论算法问题,许多大公司都曾以此作为面试、笔试题来考核员工。结合信息论的观点来看,每一次的称量,都会带来三种可能的结果,左边重、右边重、一样重。给我们将带来log_2⁡3,大约是1.58bit。n个球有一个不一样,那么总可能有n×2种结果(1号硬币轻,1号硬币重,2号硬币轻,2号硬币重••••••n号硬币轻,n号硬币重)。可以得知需要的信息量是log_2⁡2n,所以需要的次数大概就是log_2⁡2n⁄log_2⁡3 =log_3⁡n次。
    此分析是在理想状态下的称量状态,在实际中可能由于操作性的局限难以以此称量次数得到结果。下面,我将给出一些特殊情况的分析。

    • 特殊性分析
      考虑一般问题,把硬币分三堆,放两堆等数的球上天平,如果平衡,次品当然在外面这一堆。如果不平衡,比如说左边重,我们只知道外边的肯定是正品,但次品不知道是在天平的哪一边。重的那一边的硬币定义为“疑重硬币”,如果在这边,那次品一定是较重。轻的那一边定义为“疑轻球”,若是在这边则次品较轻。所以天平上的这些球经过一次称量之后已经被消除掉一部分的不确定性,称为半确定的硬币。半确定的球集合中只有两类的硬币,疑重硬币或疑轻硬币,集合中可以混合着这两类硬币,但知道每个硬币属于哪一类。
      我们来分析n=6的情况。

      1. 将硬币等分为3组,分别编号为A1,A2,B1,B2,C1,C2
      2. 将A组和B组放到天平两侧称量(第一次称量),将有两种可能情况
        i. 天平平衡,那么问题硬币必然在C组。则将AB中任取一组与C做称量(第二次称量),可以得知假币是比正常硬币轻还是重。再将C1,C2放入天平两侧称量(第三次称量),根据第二次称量的结果便可找出问题硬币。
        ii. 天平不平衡,则可以确定C组全部为正常硬币。将A,B两组中任取一组与C进行称量(第二次称量),便可确定问题硬币在哪一组,并且可知问题硬币是轻还是重。再将问题硬币组中两枚硬币做称量(第三次称量),则根据前两次称量结果可确定问题硬币是哪枚。
        这仅仅是六枚硬币便有如此复杂的称量方式,当硬币个数上升时,称量过程中出现的情况将指数增长,下面给出此问题的一般性推广分析。
    • 一般性推广
      通过数学的方法我们可以证明在一堆等重的硬币中有一个重量不同的假币,用天平称k次找出来,这堆球最多有(3^n-3)/2个球[附录一]。
      对如此多的球我们难以直接进行计算,因此我们做一下处理来得出结果
      编码
      知道了球数,就能算出需要称量几次;
      以这个次数作为长度,使用0、1、2排列组合进行编码,如001021、212022等等,再去掉全0、全1和全2,可知一共有3^n-3个编码;
      如果在一个编码中,第一处相邻数字不同的情况是01、12或20,则我们称它为正序码,如1120021;
      否则为逆序码,如2221012;
      在长度为n的编码中,正序码和逆序码的数量相等,均为(3^n-3)/2个。
      赋值
      如果把一个正序码中的0换成1,1换成2,2换成0,则它仍然是正序码;
      根据这个原理,我们把所有正序码按3个3个进行分组,如12001、20112、01220这3个就是一组;
      把正序码一组一组地分配给小球,每球一个,直到分完;
      然后把每个正序码的0换成2,2换成0,它就变成了一个逆序码,如12001变成10221;
      这样,每个小球就有了两个编码,一个正序,一个逆序,而且所有球都不重复。
      称重
      第一轮,我们把所有正序码第一位为0的小球放在天平左侧,为2的小球放在右侧,其它的放在旁边;
      如果天平左倾,记为0;右倾,记为2;平衡,记为1;
      然后是第二轮,把第二位为0的小球放在左侧,为2的放在右侧,同样记下称量结果;
      每一轮都按这个顺序进行,一共要称n次,最终结果是个n位的编码;
      如果编码等于某个小球的正序码,则这个小球比其它球重;
      如果编码等于某个小球的逆序码,则这个小球比其它球轻。

  • 问题演示

    这个问题的算法比较,我将把Java语言实现此算法并将代码附在附录,由于在找资料的过程中看到有一位前辈以JavaScript实现了此程序的可视化演示,自认为自己的演示的精彩程度将不及其千分之一,因此附上他的演示地址http://www.funnyjs.com/ballweight/#demo
  • 参考资料

    1. The Problem of the Pennies, F. J. Dyson, The Mathematical Gazette , Vol. 30, No. 291 (Oct., 1946), pp. 231-234
    2. http://www.funnyjs.com/ballweight/#demo
  • 附录

    1. 称球通解问题的证明
      摘自The Problem of the Pennies, F. J. Dyson, The Mathematical Gazette , Vol. 30, No. 291 (Oct., 1946), pp. 231-234
      • 引理1:在多于2个的一堆球,已知次品在其中,称k次可以并最多在3**k个半确定的球中找出次品,并且知道其轻重。
        用数学归纳法,k=1。3个半确定的球,一定至少有两个属于同一类,比如说疑重球,将这两个上天平,重的那个就是次品,如果平衡,外边的那个就是次品,而且从它的类别知道这次品是较重还是较轻。验证正确。
        假设结论对k-1次正确。将不多于3**k个半确定的球三等分,如果不能够等分,除天平两边要等数外,三方都不多于3**(k-1)个球,且使得两边共有偶数个疑重球,记为2a个。这总是可以做到的。因为我们可以把天平上“不齐整”的球和外面异类的球对调。这样天平左右各有a个疑重球和3**(k-1)-a个疑轻球。这一般有多种可能的a值满足要求,任取一个都行。这时如果左边重,左边的a个疑重球和右边的3**(k-1)-a个疑轻球,共3**(k-1)个半确定球有嫌疑,其他都是正品。如果右边重,同理将嫌疑缩小到3**(k-1)个半确定球。如果平衡,嫌疑在外面的3**(k-1)个半确定球中。如果这嫌疑是1个或2个半确定球,可以用一个正品与其中一个称一次解决,其他情况我们已知用k-1次可以解决不多于3**(k-1)个半确定的球。证毕。
      • 引理2:已知次品在其中,加一个已知的正品球称k次,可以并最多在(3**k+1)/2个球中找出次品,但有且仅有一种情况不知其次品的轻重。
        在k=1情况,有2个球,取一个与正品球上天平,如果平衡,次品在外面,但不知它比正品轻还是重,注意这是归纳证明中仅有的情况。如果只有1个球,它就是次品了,称一次可以知道比正品轻了还是重。
        假设结论对k-1次正确。考虑第一次天平称量,一边取(3**(k-1)-1)/2个加上一个正品球,另一边取(3**(k-1)+1)/2个球。我们知道这次称量以后,如果天平平衡,那么嫌疑在外面。余下k-1次可以解决这里的不超过(3**(k-1)+1)/2个球,有且仅有一种情况不知其次品的轻重。如果天平不平衡,天平的两边都是半确定的球。由引理1知道,余下k-1次可以解决这里的3**(k-1)个球。因为这个数是奇数,所以我们必须在第一次天平称量时再加上一个已知的正品球。因此称k次,可以并最多解决3**(k-1)+(3**(k-1)+1)/2=(3**k+1)/2个球。证毕。
        定理:在一堆等重球中有一个重量不同的次品球,用天平称k次找出来,这堆球最多且可以是(3**k-1)/2个球。
        在第一次称量我们最多可以将3**(k-1)-1个球两等分放在天平上,如果不平衡,由引理1,可以再称k-1次解决。如果平衡,天平这里都是已知球,由引理2,可以再称k-1次解决外面的(3**(k-1)+1)/2个球。所以总共可以解决(3**k-1)/2个球。证毕。
  • 称球问题java实现
import java.util.ArrayList;  
import java.util.List;  
import java.util.Scanner;  
public class Main {
    static int round = 1;  
    static int maxSteps;  

    public static void run(Status root, List<Status> list) { //求解  
        long time = System.currentTimeMillis();  
        List<Status> newlist = new ArrayList<Status>();   
        for (int i=0; i<list.size(); i++) {  
            Status status = list.get(i);  
            status.produceBalances();  
            for (int j=0
  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值