当前位置: 首页 > news >正文

【每天进步一点】毒药和老鼠的研究

之前碰到过毒药和老鼠,鸡蛋和称的问题,每次都拿笔在纸上推敲很久,这类问题今天终于有了完整的解决思路。

基础:

1.整数的二进制表达式

1000的二进制表达式是什么呢?

1000/2=500  --(余)--0
500/2=250   --(余)--0
250/2=125   --(余)--0
125/2=62    --(余)--1
62/2=31     --(余)--0
31/2=15     --(余)--1
15/2=7      --(余)--1
7/2=3       --(余)--1
3/2=1       --(余)--1
1/2=0       --(余)--1

1000的二进制表达式为 1111101000 = 29 + 28 + 27 + 26 + 25 + 23 = 512 + 256 + 128 + 64 +32 + 8

还需要其他的吗?不需要了,有上面的基础足矣。

二进制和上面的题目有什么关系呢?

在计算机基础里面,函数B2Uw表示Binary to Unsigned(长度为w的二进制到无符号整数),它能够被定义为一个映射:

B2Uw{0,1}w  --> {0,1,...,2w-1}

也就是说表达0~2w-1的整数值,只需要w位的二进制即可。

再转换一下:w位的二进制可以表达(2w-1)+1=2w个整数值。

反应在我们的命题中,n个瓶子对应n个可能的结果,转换成二进制之后,只需要x位的0101就可以表达出来了。(n已知,x未知。x的求值参考上面的结论)

命题:8瓶水,其中一瓶有毒,中毒的老鼠会在一个星期之后死亡。给你一个星期,请问最少几只老鼠可以测试出哪瓶有毒?

步骤一:确定需要几只老鼠

8 = (二进制)1000 = 23

即三位的二进制即可表达8种情况。

步骤二:为药品分配准备数据

将三位的二进制所有的情况罗列出来,剔除0的情况

计算机的整数(索引)从0开始,生活中的计数习惯从1开始。而实际的操作中,我们可以拿出一瓶不做测试,当所有老鼠都存活下来时,就说明拿出来的这一瓶是最后的结果。这里可以取巧,将0理解为索引,映射到最后一个数字,在这里就是8;剔除0,也就是剔除了第8瓶水的情况。

001//1010//2011//3100//4101//5110//6111//7

从右往左,第一列对应的是1010101,其中1对应的数即为{1,3,5,7};第二列对应0110011=>{2,3,6,7};第三列对应0001111=>{4,5,6,7}

步骤三:提取上面的数字,得出喂食方案。

第一只老鼠给他喂食(1,3,5,7)瓶水的混合液,第二只老鼠喂食(2,3,6,7)的混合液,第三只老鼠喂食(4,5,6,7)的混合液。

步骤四:根据实验结果推算答案。

如果第一只死了,第二只和第三只活着,我们依旧从右往左写成001(死掉为1,活着为0),转成十进制整数为1。那么结论就是第1瓶水有毒。

如果第一只死了,第三只也死了,第二只活着。二进制为101,转十进制为5。那么结论就是第5瓶水有毒。

如果全部死亡。111,对应7,那么就是第7瓶水有毒。

如果全部存活。000,那就是0瓶水(也就是上面预留的第8瓶水)有毒。

步骤五:验证答案。

101的情况:第二只存活,说明2,3,6,7没有问题,而第一只和第三只,取他们的交集,有可能的是5和7,但是前面已经排除了7,所以答案是5。

再来验证110的情况:第一只存活,二、三只光荣了,说明1,3,5,7没问题,后两只的混合液取交集,有可能是6,7。前面排除了7,答案只能为6。

 

继续思考:

1000瓶水,找出其中有毒的那一瓶水,需要多少小白鼠,实际操作怎么样呢?

29 < 1000 < 210 所以需要的白老鼠个数为10只,也就是10只老鼠才能充分的反应1000种情况。当然,10老鼠最多可以反映多少情况呢,应该是210=1024种。

 

得出了需要十只老鼠,接下来十只老鼠应该怎么操作才能得出结论呢?

0000000001 ------1

0000000010 ------2

0000000011 ------3

0000000100 ------4

0000000101 ------5

...

1111100111 ------999

 

那么第一只老鼠应该喝1,3,5,7,9,11,...,997,999瓶水的混合液

第二只老鼠则是2,3,6,7,...,998,999瓶水的混合液

第三只老鼠则是4,5,6,7,12,13,14,15,...,996,997,998,999瓶水的混合液

...

第十只老鼠则是512,513,514,...,999瓶水的混合液

好吧,每个老鼠的指标都是接近500瓶的混合液,这也是自然的,每个位上有0,1两种可能。这么多,老鼠估计喝都喝死了。还想到一个问题啊,稀释这么严重,能毒死老鼠吗?这年头老鼠的生命力很顽强啊,这得上好的毒药啊......

 

......最后观察老鼠的牺牲情况,从右至左,死掉为1,活着为0,得到最终的二进制表达式,然后转换成十进制,即为有毒瓶子的答案。

注意:这里1000<210的情况,也可以将1111101000 -----1000的情况列出来。最后如果第四、六七八九十只老鼠死了的话,就可以判断是第1000瓶水有毒。但是也可以将1000空出来,不喂食任何老鼠。最后没有老鼠死掉的话,也可以判断是第1000瓶水有毒了。这样可以避免老鼠的牺牲,阿弥陀佛~~~~

 

算法总结:

根据园友Wossoneri的提示,仔细看了汉明码的校验方法,看到如下说明:

可发现一个比较直观的规律:第i个检验位是第2i-1位,从该位开始,检验2i-1位,跳过2i-1位……依次类推。例如上表中第3个检验位p4从第23-1=4位开始,检验4、5、6、7共4位,然后跳过8、9、10、11共4位,再检验12、13、14、15共4位……

由上面的思路写了下面的代码

计算小白鼠数量:

 Console.WriteLine("请输入总瓶数:");
 int poisonNum = int.Parse(Console.ReadLine());
 var x = Math.Log(poisonNum, 2);
 var mouseNum = (int)x + (x % 1 > 0 ? 1 : 0);
 Console.WriteLine("需要小白鼠数量:" + mouseNum);
View Code

老鼠喂食方案:

Console.WriteLine("老鼠喂食方案:");
List<int>[] solution = new List<int>[mouseNum];
for (int i = 0; i < mouseNum; i++)
{
    solution[i] = new List<int>();
    int segment = 1 << i;
    int idx = segment;
    while (idx < poisonNum)
    {
        solution[i].AddRange(Enumerable.Range(idx, segment));
        idx += 2 * segment;
    }
    solution[i].RemoveAll(aa => aa >= poisonNum);
}
for (int i = 0; i < mouseNum; i++)
    Console.WriteLine("第{0}只老鼠的喂食方案:{1}", i + 1, string.Join(",", solution[i]));
View Code

根据实验结果得出结论:

Console.WriteLine("假设有毒瓶数为:");
int poisonOne = int.Parse(Console.ReadLine());
Console.WriteLine("死掉的老鼠有:" + string.Join(",", solution.Select((aa, idx) => aa.Contains(poisonOne) ? idx + 1 : -1).Where(idx => idx != -1)));
var resBinary = new string(solution.Select(aa => aa.Contains(poisonOne) ? '1' : '0').Reverse().ToArray());
Console.WriteLine("二进制表达式为:" + resBinary);
var res = Convert.ToInt32(resBinary, 2);
if (res == 0) res = poisonNum;
Console.WriteLine("测试结果为:第{0}瓶有毒", res);
View Code

 

发散的问题:6个质量相等的小球和1个质量稍重的球,不能用天平,只能用称,设计一种方法,只能量三次就找出稍重的球。

类似的问题,这里数量变成了7个。测试数据也从死掉变成了重和轻,不过一样可以转换成0和1。

如果你看懂了上面,不妨想想这个的答案。

转载于:https://www.cnblogs.com/icyJ/p/Mouse_Poison.html

相关文章:

  • linux下安装Python-2.7.9
  • 一条简单的sql在11g和12c中的不同
  • 关于mongodb在mac下的手动安装,非homwbrew安装(小白请进)
  • EcShop二次开发学习方法
  • 国庆后的特训
  • 梦游记-梦中游记
  • PHP-内核学习(一、变量)
  • 【cs229-Lecture18】线性二次型调节控制
  • 转:windows 下 netsh 实现 端口映射(端口转发)
  • assign, retain, weak, strong, copy,unsafe_unretain
  • java 反射
  • MSF溢出实战教程
  • 虚拟机的使用和Linux的一些基础
  • 了解IP子网划分的那些事
  • 海量数据备份归档技术及系统
  • ECMAScript 6 学习之路 ( 四 ) String 字符串扩展
  • export和import的用法总结
  • js面向对象
  • k8s 面向应用开发者的基础命令
  • nodejs:开发并发布一个nodejs包
  • Sass 快速入门教程
  • 多线程 start 和 run 方法到底有什么区别?
  • 分享自己折腾多时的一套 vue 组件 --we-vue
  • 搞机器学习要哪些技能
  • 讲清楚之javascript作用域
  • 近期前端发展计划
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 区块链共识机制优缺点对比都是什么
  • 优秀架构师必须掌握的架构思维
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • ​一文看懂数据清洗:缺失值、异常值和重复值的处理
  • !$boo在php中什么意思,php前戏
  • ( 10 )MySQL中的外键
  • (1)bark-ml
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (搬运以学习)flask 上下文的实现
  • (第27天)Oracle 数据泵转换分区表
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (顺序)容器的好伴侣 --- 容器适配器
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • (一)kafka实战——kafka源码编译启动
  • (一)u-boot-nand.bin的下载
  • (转)我也是一只IT小小鸟
  • ******之网络***——物理***
  • .apk文件,IIS不支持下载解决
  • .mat 文件的加载与创建 矩阵变图像? ∈ Matlab 使用笔记
  • .Net - 类的介绍
  • .NET Core Web APi类库如何内嵌运行?
  • .NET Core WebAPI中使用Log4net 日志级别分类并记录到数据库
  • .Net Web项目创建比较不错的参考文章
  • .NET 中 GetProcess 相关方法的性能
  • .Net的C#语言取月份数值对应的MonthName值
  • [ C++ ] STL---string类的模拟实现
  • [ 蓝桥杯Web真题 ]-Markdown 文档解析