一般性假币称重鉴别问题
问题重述
设有nn枚硬币,其中仅有一枚假币,在已知或未知假币与真币之间重量关系两种情况下,通过无砝码天平称重的方法鉴别假币,求所需的最少称重次数。
问题分析
此问题是经典的信息论算法问题,许多大公司都曾以此作为面试、笔试题来考核员工。从信息论角度看,“有nn枚硬币,其中仅有一枚假币”发生概率为P=1nP = \frac{1}{n},“假币与真币之间重量关系未知”发生概率为P=12P = \frac{1}{2},为了确定哪一枚假币,即要消除上述事件的联合不确定性。
又因为两事件独立,因此有I1=logn+log2=log2n{I_1} = \log n + \log 2 = \log 2n比特;用天平称重,有三种可能:平衡、左倾、右倾,三者等概率,为P=13P = \frac{1}{3},因此天平称重一次消除的不确定性为I2=log3{I_2} = \log 3比特,所以称重次数至少为I1I2=log2nlog3\frac{{{I_1}}}{{{I_2}}} = \frac{{\log 2n}}{{\log 3}}次。解题思路
【问题一】当n=12n = 12 时
将12个硬币编号为:1,2,3,4,5,6,7,8,9,10,11,12
称重安排如下表:
称重结果表示为:0:平衡 1:左倾 2:右倾
可以得到如下表结果:
同理可用矩阵表示3次称重的安排,矩阵上方为硬币序号,矩阵的行为3次称重时矩阵的放置位置,1表示放到左盘,2表示放到右盘,0表示不参与称重。123456789101112⎛⎝⎜110101102100221211212210002021022020⎞⎠⎟\begin{array}{l}{\kern 10pt}1{\kern 11pt} 2{\kern 10pt} 3{\kern 10pt} 4{\kern 10pt} 5{\kern 10pt}6{\kern 10pt}7{\kern 11pt}8{\kern 10pt}9{\kern 7pt} 10{\kern 6pt} 11{\kern 5pt} 12\\ \left( {\begin{array}{*{20}{l}} 1&1&1&1&2&2&2&2&0&0&0&0\\ 1&0&0&0&2&1&1&1&0&2&2&2\\ 0&1&2&0&1&1&2&0&2&1&2&0 \end{array}} \right) \end{array}
由表格与矩阵,发现:如果检测结果与矩阵的某列符合,则对应序号的硬币即为假币,且重量较重;如果检测结果不在上述矩阵的列中,将1、2互换,得到假币对应序号,重量较轻。例如,若称重结果为110,则1号为假币,且重量较重;若称重结果为201,将1与2互换,得到102,则3号为假币,且重量较轻。【问题二】当n=39n{\rm{ = }}39时
查阅资料可得,kk次称重最多可以在3k−32\frac{{{3^k} - 3}}{2}个硬币中找到不同的硬币,并判断其轻重。已知硬币数量为39,可求得需要称量的次数k=5k{\rm{ = }}5- 编码
以称量次数为编码长度,使用0、1、2排列组合进行编码,再去掉全为0、全为1和全为2,可知一共有3k−3{3^k} - 3个编码。在一个编码中,第一处相邻数字不同的情况是01、12或20,则我们称它为正序码,如11010; 否则为逆序码,如11210;在长度为 的编码中,正序码和逆序码的数量相等,为3k−32\frac{{{3^k} - 3}}{2}个。 - 赋值
如果把一个正序码的0换成1,1换成2,2换成0,则它认为正序码。由此,将正序码每3个分为一组,例如11010、22121、00202。7将正序码的0与2互换,即可得到一个逆序码,因此每枚硬币均有一个正序码一个逆序码。 - 称重
第一次,将正序码第一位为1的硬币放在左侧,为2的硬币放在右侧,其余不参与称重,如果天平平衡记为0,左倾记为1,右倾记为2;
第二次,将正序码第二位为1的硬币放在左侧,为2的硬币放在右侧,其余不参与称重;
每轮如此,重复 次,结果得到一个 位编码。如果此编码为某个硬币的正序码,则这个硬币比其余硬币重;如果此编码为某个硬币的逆序码,则这个硬币比其余硬币轻。
- 编码
可视化演示
以n=12n=12为例
(1)编号(假设6号硬币为假币)
(2)赋值123456789101112⎛⎝⎜110101102100221211212210002021022020⎞⎠⎟\begin{array}{l}{\kern 10pt}1{\kern 11pt} 2{\kern 10pt} 3{\kern 10pt} 4{\kern 10pt} 5{\kern 10pt}6{\kern 10pt}7{\kern 11pt}8{\kern 10pt}9{\kern 7pt} 10{\kern 6pt} 11{\kern 5pt} 12\\ \left( {\begin{array}{*{20}{l}} 1&1&1&1&2&2&2&2&0&0&0&0\\ 1&0&0&0&2&1&1&1&0&2&2&2\\ 0&1&2&0&1&1&2&0&2&1&2&0 \end{array}} \right) \end{array}
(3)称重
将正序码第一位为1的硬币放在天平的左侧,为2的放在右侧,为0的放在旁边:
将正序码第二位为1的硬币放在天平的左侧,为2的放在右侧,为0的放在旁边:
将正序码第三位为1的硬币放在天平的左侧,为2的放在右侧,为0的放在旁边:
结果与事先挑选的6号硬币一致附录
称球通解问题的证明
摘自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=1k=1。3个半确定的球,一定至少有两个属于同一类,比如说疑重球,将这两个上天平,重的那个就是次品,如果平衡,外边的那个就是次品,而且从它的类别知道这次品是较重还是较轻。验证正确。
假设结论对k−1k-1次正确。将不多于3k3^k个半确定的球三等分,如果不能够等分,除天平两边要等数外,三方都不多于3(k−1)3^(k-1)个球,且使得两边共有偶数个疑重球,记为2a2a个。这总是可以做到的。因为我们可以把天平上“不齐整”的球和外面异类的球对调。这样天平左右各有aa个疑重球和3k−1−a3^{k-1}-a个疑轻球。这一般有多种可能的aa值满足要求,任取一个都行。这时如果左边重,左边的aa个疑重球和右边的3k−1−a3^{k-1}-a个疑轻球,共3k−13^{k-1}个半确定球有嫌疑,其他都是正品。如果右边重,同理将嫌疑缩小到3k−13^{k-1}个半确定球。如果平衡,嫌疑在外面的3k−13^{k-1}个半确定球中。如果这嫌疑是1个或2个半确定球,可以用一个正品与其中一个称一次解决,其他情况我们已知用k−1k-1次可以解决不多于3k−13^{k-1}个半确定的球。证毕。
引理2:已知次品在其中,加一个已知的正品球称k次,可以并最多在3k+12\frac{3^{k+1}}{2}个球中找出次品,但有且仅有一种情况不知其次品的轻重。
在k=1k=1情况,有2个球,取一个与正品球上天平,如果平衡,次品在外面,但不知它比正品轻还是重,注意这是归纳证明中仅有的情况。如果只有1个球,它就是次品了,称一次可以知道比正品轻了还是重。
假设结论对k−1k-1次正确。考虑第一次天平称量,一边取3k−1−12\frac{3^{k-1}-1}{2}个加上一个正品球,另一边取3k−1−12\frac{3^{k-1}-1}{2}个球。我们知道这次称量以后,如果天平平衡,那么嫌疑在外面。余下k−1k-1次可以解决这里的不超过3k−1−12\frac{3^{k-1}-1}{2}个球,有且仅有一种情况不知其次品的轻重。如果天平不平衡,天平的两边都是半确定的球。由引理1知道,余下k−1k-1次可以解决这里的3k−13^{k-1}个球。因为这个数是奇数,所以我们必须在第一次天平称量时再加上一个已知的正品球。因此称kk次,可以并最多解决3k+12\frac{{{3^{k + 1}}}}{2}个球。证毕。
定理:在一堆等重球中有一个重量不同的次品球,用天平称kk次找出来,这堆球最多且可以是3k−12\frac{3^{k-1}}{2}个球。
在第一次称量我们最多可以将3k−1−13^{k-1}-1个球两等分放在天平上,如果不平衡,由引理1,可以再称k−1k-1次解决。如果平衡,天平这里都是已知球,由引理2,可以再称k−1k-1次解决外面的3k−1+12\frac{3^{k-1}+1}{2}个球。所以总共可以解决3k−12\frac{3^{k-1}}{2}个球。证毕。
- 程序代码
package com.yrwan.findCoin;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) { //求解 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; j<status.bls.size(); j++) { Balance bl = status.bls.get(j); bl.weight(); if (root.succeed()) { return; } if (bl.out1.isUnknown()) newlist.add(bl.out1); if (bl.out2.isUnknown()) newlist.add(bl.out2); if (bl.out3.isUnknown()) newlist.add(bl.out3); } } round++; run(root, newlist); } public static void print(Status st, int depth) { //输出结果 String indent=""; for (int i=0; i<depth-1; i++) indent = indent+"t"; Balance bl=null; for (int i=0; i<st.bls.size(); i++) if (st.bls.get(i).unresolved==0) bl=st.bls.get(i); if (bl!=null) { if (depth>maxSteps) maxSteps=depth; System.out.println(indent + "第" + depth + "步称重: " + bl + "rn"); System.out.println(indent + "如果一样重: " + bl.out1 + (bl.out1.getConclusion()==Status.RESOLVED?" *解决*":(bl.out1.getConclusion()==Status.REDICULOUS?" ×不可能×":"")) + "rn"); print(bl.out1, depth+1); System.out.println(indent + "如果左边重: " + bl.out2 + (bl.out2.getConclusion()==Status.RESOLVED?" *解决*":(bl.out2.getConclusion()==Status.REDICULOUS?" ×不可能×":"")) + "rn"); print(bl.out2, depth+1); System.out.println(indent + "如果右边重: " + bl.out3 + (bl.out3.getConclusion()==Status.RESOLVED?" *解决*":(bl.out3.getConclusion()==Status.REDICULOUS?" ×不可能×":"")) + "rn"); print(bl.out3, depth+1); } } public static void main(String[] args) {Scanner sc = new Scanner(System.in); System.out.println("请输入硬币个数:"); int n = sc.nextInt(); sc.close();Status root = new Status(n); ArrayList<Status> list = new ArrayList<Status>(); list.add(root); run(root, list); System.out.println("***** 求解步骤*****"); maxSteps = 0; print(root, 1); System.out.println("***** 最少" + maxSteps + "步可找出假币*****"); }
}
package com.yrwan.findCoin;public class Balance {public int[] data; public Status in,out1,out2,out3; public int unresolved = 3; public Balance(int[] data) { this.data = data.clone(); } public void weight() {//称重量,推理出三种可能的结果 int[] temp; // 一样重 temp = in.data.clone(); for (int i=1; i<4; i++) { //所有参与称重的硬币都移入正常硬币集合 temp[0] = temp[0] + data[i] + data[i+4]; temp[i] = temp[i] - data[i] - data[i+4]; } out1 = new Status(temp); out1.addParent(this); //左边重 temp = in.data.clone(); for (int i=1; i<4; i++) { temp[0] = temp[0] + temp[i] - data[i] - data[i+4]; //未参与称重的硬币 -->> 正常硬币 } temp[0] += data[3] + data[6]; //左边的疑似轻硬币、右边的疑似重硬币 -->> 正常硬币 temp[1] = 0; temp[2] = data[1] + data[2]; //左边的不明轻重硬币移入疑似重硬币集合 temp[3] = data[5] + data[7]; //右边的不明轻重硬币移入疑似轻硬币集合 out2 = new Status(temp); out2.addParent(this); //右边重 temp = in.data.clone(); for (int i=1; i<4; i++) { temp[0] = temp[0] + temp[i] - data[i] - data[i+4]; //未参与称重的硬币 -->> 正常硬币 } temp[0] += data[2] + data[7]; //左边的疑似重硬币、右边的疑似轻硬币 -->> 正常硬币 temp[1] = 0; temp[2] = data[5] + data[6]; //右边的不明轻重硬币移入疑似重硬币集合 temp[3] = data[1] + data[3]; //左边的不明轻重硬币移入疑似轻硬币集合 out3 = new Status(temp); out3.addParent(this); } public String toString(){ return "(" + (data[0]>0?"正常硬币×"+data[0]+"个 ":"") + (data[1]>0?"不明硬币×"+data[1]+"个 ":"") +(data[2]>0?"疑似重硬币×"+data[2]+"个 ":"") + (data[3]>0?"疑似轻硬币×"+data[3]+"个 ":"") + ") --天平-- (" + (data[4]>0?"正常硬币×"+data[4]+"个 ":"") + (data[5]>0?"不明硬币×"+data[5]+"个 ":"") +(data[6]>0?"疑似重硬币×"+data[6]+"个 ":"") + (data[7]>0?"疑似轻硬币×"+data[7]+"个 ":"") + ")"; } public void prop() { if (unresolved <= 0) return; unresolved--; if (unresolved == 0) in.setConclusion(Status.RESOLVABLE); }
}
package com.yrwan.findCoin;import java.util.ArrayList;
import java.util.List;public class Status {public static int RESOLVED=1, UNKNOWN=2, REDICULOUS=3, RESOLVABLE=4; public int count=0; public int[] data; public List<Balance> parents = new ArrayList<Balance>(); public List<Balance> bls = new ArrayList<Balance>(); private int conclusion; public Status(int c) { count = c; int[] data1 = {0,c,0,0}; data = data1; int conc = data[0]<count-1?UNKNOWN:(data[0]==count-1?RESOLVED:REDICULOUS); setConclusion(conc); } public Status(int[] is) { data = is; for (int i=0; i<is.length; i++) count+=is[i]; int conc = data[0]<count-1?UNKNOWN:(data[0]==count-1?RESOLVED:REDICULOUS); setConclusion(conc); } public void addParent(Balance bl) { parents.add(bl); if (conclusion==RESOLVED || conclusion==RESOLVABLE || conclusion==REDICULOUS) bl.prop(); } public String toString() { return "正常" + data[0] + "、不明" + data[1] + "、或重" + data[2] + "、或轻" + data[3]; } public void setConclusion(int conc) { if (conclusion == conc) return; conclusion = conc; if (conclusion==RESOLVED || conclusion==RESOLVABLE || conclusion==REDICULOUS) for (int i=0; i<parents.size(); i++) parents.get(i).prop(); } public int getConclusion() {return conclusion;} public boolean succeed() {return conclusion==RESOLVED || conclusion==RESOLVABLE;} public boolean isUnknown(){return conclusion==UNKNOWN;} public void produceBalances() {//得到当前状况下所有可能的称重方案 List<int[]> bldata = getBalanceDataArray(data); bls = new ArrayList<Balance>(); for (int i=0; i<bldata.size(); i++) { Balance bl = new Balance(bldata.get(i)); bl.in = this; bls.add(bl); } } private List<int[]> getBalanceDataArray(int[] src) { List<int[]> list = new ArrayList<int[]>(); list.add(new int[src.length*2]); return getBalanceDataArray(src,0,list); } private List<int[]> getBalanceDataArray(int[] src, int id, List<int[]> list) { int total=0,left,right; if (id>=src.length) { for (int i=list.size()-1; i>=0; i--) { int[] is = list.get(i); left=0; right=0; for (int j=0; j<src.length; j++) left+=is[j]; for (int j=src.length; j<src.length*2; j++) right+=is[j]; if (left!=right || left==0 || is[0]>0&&is[is.length/2]>0) list.remove(i); } return list; } List<int[]> r = new ArrayList<int[]>(); for (int i=0; i<src.length; i++) total += src[i]; int half = total/2; for (int i=0; i<list.size(); i++) { int[] is = list.get(i); left=0; right=0; for (int j=0; j<src.length; j++) left+=is[j]; for (int j=src.length; j<src.length*2; j++) right+=is[j]; for (int j=0; j<=Math.min(half-left, src[id]); j++) { for (int k=0; k<=Math.min(half-right, src[id]-j); k++) { int[] iis = list.get(i).clone(); iis[id] = j; iis[id+src.length] = k; r.add(iis); } } } return getBalanceDataArray(src,id+1,r); }
}
一般性假币称重鉴别问题相关推荐
- 信息熵--硬币称重问题-详解
在补<信息熵基础>,此书理论清晰,后面还有一堆要命的习题,加深理解和应用,实乃佳作.其中一些题目对我来说确实不易,记录巩固一下,也和大家交流一下. 顺便补一下,一份完整的参考资料或者书籍, ...
- 12枚硬币称重问题(面试)
问题描述: 12枚硬币,其中11枚真币1枚假币,现有一架天平,最少称多少次可以找出这枚假币并且知道假币和真币的相对重量. 答案是三次,称重过程描述如下. 第一步:分组,分三组,1 2 3 4为一组,5 ...
- 关于stm32f407wifi模块的设置_料粉定量称重模块,罐子称重传感器
料粉定量称重模块,罐子称重传感器 上海恒刚生产销售的称重模块采用了的传感器元件,其特点是结构紧凑,不需安装其他配件,自稳定的传感器承压头使计量,重复性好:高速简便的安装,节约安装和停机维修时间,称重模 ...
- 局域网通讯工具_自动称重带无线通讯WIFI传输功能设备
自动称重带无线通讯WIFI传输功能设备详情内容/ Content details 支持: 远程数据库功能,支持OPCUA协议,能实时上传各种称重信息:通过SQLServer等实现称重数据与记录的上传到 ...
- pandas重命名列名称、数据列名称重命名(Rename Column Names): rename、set_axis、df.columns
pandas重命名列名称.数据列名称重命名(Rename Column Names): rename.set_axis.df.columns 目录 pandas重命名列名称.数据列名称重命名(Rena ...
- 电子称重管理计算机,称重管理系统
霄博称重软件主要是针对企业在原料采购.成品销售及厂内物资倒运.物资调拨过程进行的计量管理:霄博称重单机软件适用于单台磅,管理相对简单的企业的计量管理. 1.计量功能 霄博单机版称重软件的计量过程可以按 ...
- 做弱电机房工程的时候,如何解决机房称重问题?
我们在做一些机房工程的时候经常遇到机房承重的问题,尤其在一些比较高的楼层上面做机房是不是合适呢?机房称重符合要求吗?今天我们一起来探讨一下. 一,什么是机房承重? 众所周知,机房是电子设备运行的场所, ...
- 判断文件是否损坏_称重传感器好坏的判断方法,看完秒懂!
完整的称重系统通常由称重传感器和称重显示器(即称重仪器)组成,下面将对其进行详细介绍. 一:称重传感器有四线制和六线制称重传感器.四线制: EXC +,EXC-:电源线,即仪器到传感器的直流电源. S ...
- 地磅称重软件源码_【漯河衡器】浅谈地磅称重的发展趋势
发展,是人类物质社会的一个永恒的话题.每一个个体,集团,企业都需要不断地发展.才能应对社会的整体发展.对于地磅称重行业也是如此.今天衡安软件的小编就来讲一讲地磅称重的发展以及发展趋势. 地磅称重 一. ...
最新文章
- CSP认证201412-1	门禁系统[C++题解]:哈希表
- GooglePR说明
- 微服务架构设计模式~根据业务能力进行服务拆分
- java 合并对象中属性_Java2个对象形集合按某一个属性合并
- 阶段3 3.SpringMVC·_04.SpringMVC返回值类型及响应数据类型_8 响应json数据之响应json格式数据...
- 阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_01 File类_8_File类遍历(文件夹)目录功能...
- java 工作流 实例_Activiti工作流的应用示例
- 教你编写第一个人工智能程序
- 万字长文丨大白话带你由浅入深Python编程语言
- 图像小波去噪matlab程序,小波去噪程序(用matlab实现)
- 海外侨胞代表建议广东各市抱团出海开设名优特产品实体店
- 双十一,稳!2小时1000亿,阿里的技术露大脸了
- 问题 D: 上帝视角
- 最新小程序授权+php后端(附demo源码)
- MS08-067远程代码执行漏洞(CVE-2008-4250) | Windows Server服务RPC请求缓冲区溢出漏洞复现
- 山东如意路嘉纳高级定制西装品牌惊艳亮相intertextile面料展 - 服装资讯中心 - 华衣网...
- cocos tween
- 8月TIOBE编程语言排行榜出炉:它稳了!
- Python爬虫学习笔记-第十一课(selenium下)
- 按头安利 好看又实用的公文包 文件包3d模型素材看这里
热门文章
- Git之多人协同开发
- stream.map 和 stream.foreach 的区别
- 联想Idea Pad Y430 开启VT
- Winform知识扩展-------右键菜单与InputBox弹出式输入框
- 2021年R1快开门式压力容器操作最新解析及R1快开门式压力容器操作操作证考试
- 电视剧也能训练人工智能?爱丁堡大学对此进行了实验 | 研究
- Ubuntu下android-4.0.3_r1源码下载,阅读工具安装配置,源码编译详解
- multiple 属性,上传多个文件或图片
- 2011 模拟 c语言 本科(含答案)(第二届“国信蓝点杯”全国软件专业人才设计与开发大赛)...
- 卡耐基-----人性的弱点