文章出自https://bbs.csdn.net/topics/340004129,算法叫做“平衡二叉树”,用matlab软件编的拿来学习下。
这个问题的模型就是用多少二进制位可以表示1000内的一个数字.回答这个模型就简单了,1000表示为二进制表示1111101000.最大的1000用10位就中了,那么检验这1000瓶水要10个小白鼠.
那么要怎么样检验呢.经过我在床上转来转去的思考,终于想到了一个方法.1000瓶水太多了,我们用8瓶水说明情况(要用3个小白鼠做实验哦).好像下面的图叫做平衡二叉树来的吧.名词忘记了,能用就中.
所有小白鼠只喝右分支的水(要是左的话水编号要从右到左增加). 也就是
鼠1喝水4,5,6,7的混合液体
鼠2喝水2,3,6,7的混合液体
鼠3喝水 1,3,5,7的混合液体
貌似水0没有老鼠喝,但愿一个白鼠都没有死就是你水0有毒了.
这样小白鼠死亡表示1,不死亡表示0。鼠1,鼠2,鼠3组成个三位二进制
000对应水0有毒
001对应水1有毒
…
111对应水7有毒
1000瓶水和这个原理一样用十个小白鼠就找到那瓶水有毒了.
感觉恍然大悟,所以编了下程序,不过问题已经不是求小白鼠数目了,而是对整个检测的过程进行了模拟,并且给出判断结果。
给1000个瓶分别标上如下标签(10位长度):
0000000001 (第1瓶)
0000000010 (第2瓶)
0000000011 (第3瓶)
......
1111101000 (第1000瓶)
从编号最后1位是1的所有的瓶子里面取出1滴混在一起(比如从第一瓶,第三瓶,里分别取出一滴混在一起)并标上记号为1。以此类推,从编号第一位是1的所有的瓶子里面取出1滴混在一起并标上记号为10。现在得到有10个编号的混合液,小白鼠排排站,分别标上10,9,1号,并分别给它们灌上对应号码的混合液。24小时过去了,过来验尸吧:
从左到右,死了的小白鼠贴上标签1,没死的贴上0,最后得到一个序号,把这个序号换成10进制的数字,就是有毒的那瓶水的编号。
检验一下:假如第一瓶有毒,按照0000000001 (第1瓶),说明第1号混合液有毒,因此小白鼠的生死符为0000000001(编号为1的小白鼠挂了),0000000001二进制标签转换成十进制=1号瓶有毒;假如第三瓶有毒,0000000011 (第3瓶),第1号和第2号混合液有毒,因此小白鼠的生死符为00000011(编号为1,2的鼠兄弟挂了),0000000011二进制标签转换成十进制=3号瓶有毒。
main.m
================================================================
- clc
- clear
- N=8;%总瓶数
- num=log2(N);%需要小鼠数
- pos=round(1+rand*(N-1));%毒药在第几瓶 从1开始
- solu=zeros(num,N/2);%方案
- for ii=1:num
- solu(ii,:)=tiqu(N,ii);%每只小鼠喝哪几瓶 方案
- end
- result=zeros(num,1);%活 死
- for ii=1:num
- result(ii)=ismember(pos,solu(ii,:));%0 活 1 死
- end
- code=result';%编码
- tdpos=bin2dec(num2str(code))+1;%推断的位置
- disp('真实位置(毒药)');
- disp(pos);
- disp('推断位置(毒药)');
- disp(tdpos);
- disp('小鼠状态(0-活,1-死)')
- disp(result);
================================================================
- function a=tiqu(A,no)
- % A 瓶子总数目
- % no 第no只小鼠
- % a 第no只小鼠应该喝哪些瓶子里的东西
- if no>log2(A)
- error('超出最大数目');
- end
- fenduan=2^no;%分段数
- lenduan=A/fenduan;%段长度
- AA=zeros(fenduan,lenduan);%每段内容 包含的序号
- Aa=zeros(fenduan/2,lenduan);%第no只小鼠喝的序号的矩阵
- for ii=1:fenduan
- AA(ii,:)=[(ii-1)*lenduan+1:1:ii*lenduan];
- end
- Aa=AA(2:2:end,:);%Aa取为AA的偶数行的值
- Aa=Aa';
- a=Aa(:);
- a=a';
- end