吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 903|回复: 5
收起左侧

[讨论] 【逻辑题】经典的“12枚硬币称量问题”

[复制链接]
jgdfc 发表于 2022-8-12 18:31
本帖最后由 jgdfc 于 2022-8-12 18:36 编辑

问题描述:
12枚硬币,其中11枚真币1枚假币,现有一架天平,最少称多少次可以找出这枚假币并且知道假币和真币的相对重量。

先求最少次数
总共12枚硬币,假币有俩种可能:轻或重,所以共12*2=24可能
每次称重有3种可能,左轻,平衡,右轻,3次称量可以获得的信息是3^3=27
所以理论上3次可行

本质是利用信息熵去解决,每次称量尽量造成信息熵减少(类似于动态规划)。
注:可获得信息大于信息熵时是理论上可行,实际中有不可行的情况,比如需要借一个正常硬币才能称量出来。

第一种方法解法
  • 每个方框表示当前的状态,即哪些硬币可能会重,哪些硬币可能会轻。
  • 每个菱形框表示所做的判断,即将哪些硬币用于称量。
此图版权所有:littlefall



这种方法如何转换成程序呢?
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Demo {
    Integer yingBiSize = 12;
    //硬币
    List<Integer> yingBi = new ArrayList<>();
    //结果可能性
    List<List<Integer>> result = new ArrayList<>();
    //称重次数
    int limit = 1;

    //[0]集合
    List<Integer> kn1 = new ArrayList<>();
    //[-1,0]集合
    List<Integer> kn2Minus = new ArrayList<>();
    //[1,0]集合
    List<Integer> kn2Positive = new ArrayList<>();
    //[-1,0,1]集合
    List<Integer> kn3 = new ArrayList<>();

    /**
     * 设置第number个硬币的质量偏差为deviation
     * number从0起,范围[0-11], deviation范围[-1,0,1]
     */
    public Demo(int number, int deviation) {
        for (int i = 0; i < yingBiSize; i++) {
            yingBi.add(number == i?deviation:0);
            result.add(new ArrayList<Integer>(Arrays.asList(-1,0,1)));
            kn3.add(i);
        }

    }

    /**
     * 比较重量 -1 左轻 ; 0 平衡 ; 1 右轻
     * home.php?mod=space&uid=952169 left
     * @param right
     * @return
     */
    public Integer compare(List<Integer> left,List<Integer> right){
        int leftSum = left.stream().mapToInt(x -> yingBi.get(x)).sum();
        int rightSum = right.stream().mapToInt(x -> yingBi.get(x)).sum();
        int i = leftSum - rightSum;
        System.out.println("第"+(limit++)+"次测量,左为:"+left+"右为"+right+"结果为"+i);
        return i;
    }

    /**
     * 计算这么摆放硬币
     * @return
     */
    public List<List<Integer>>  grouping(){
        List<List<Integer>> paixu = new ArrayList<>();
        //左侧
        List<Integer> left = new ArrayList<>();
        //闲置
        List<Integer> middle = new ArrayList<>();
        //右侧
        List<Integer> right = new ArrayList<>();

        if (kn3.size()>0){
            List<Integer> kn = new ArrayList<>();
            kn.addAll(kn3);
            kn.addAll(kn1);
            for (int i = 0; i < kn.size(); i++) {
                if (i%3 ==0){
                    left.add(kn.get(i));
                }else if(i%3 ==1){
                    right.add(kn.get(i));
                }else {
                    middle.add(kn.get(i));
                }
            }
        }else if(kn2Positive.size() >0 || kn2Minus.size() >0){

            List<Integer> kn = new ArrayList<>();
            if (kn2Positive.size() > kn2Minus.size()) {
                kn.addAll(kn2Positive);
                kn.addAll(kn2Minus);
            } else {
                kn.addAll(kn2Minus);
                kn.addAll(kn2Positive);
            }
            kn.addAll(kn1);
            int kn2Size = kn2Positive.size()+kn2Minus.size();
            int middleSize = kn2Size / 3 ;
            if ( kn2Size % 3 != 0){
                middleSize++;
                middle.addAll(kn.stream().limit(middleSize).collect(Collectors.toList()));
                left.addAll(kn.stream().skip(middleSize).limit(middleSize).collect(Collectors.toList()));
                right.addAll(kn.stream().skip(middleSize* 2L).limit(middleSize).collect(Collectors.toList()));
            }else {
                left.addAll(kn.stream().limit(middleSize).collect(Collectors.toList()));
                right.addAll(kn.stream().skip(middleSize).limit(middleSize).collect(Collectors.toList()));
                middle.addAll(kn.stream().skip(middleSize* 2L).limit(middleSize).collect(Collectors.toList()));
            }
        }
        paixu.add(left);
        paixu.add(middle);
        paixu.add(right);
        return paixu;
    }

    /**
     * 根据结果排除可能性
     * @param left
     * @param right
     * @param compare
     * @return
     */
    public void checkResult(List<Integer> left,List<Integer> middle,List<Integer> right,Integer compare){
        if (compare==0){
            left.forEach(x ->result.get(x).removeIf(s -> !s.equals(0)));
            right.forEach(x ->result.get(x).removeIf(s -> !s.equals(0)));
        }else {
            middle.forEach(x ->result.get(x).removeIf(s -> !s.equals(0)));
            left.forEach(x ->result.get(x).removeIf(s -> s.equals((-1) * compare)));
            right.forEach(x ->result.get(x).removeIf(s -> s.equals(compare)));
        }

    }

    /**
     * 分析结果
     */
    public void interpretationResult(){
        kn1 = new ArrayList<>();
        kn2Minus = new ArrayList<>();
        kn2Positive= new ArrayList<>();
        kn3 = new ArrayList<>();
        for (int i = 0; i < result.size(); i++) {
            if (result.get(i).size()==1){
                kn1.add(i);
            }
            if (result.get(i).size()==2){
                if (result.get(i).get(0).equals(0)){
                    kn2Positive.add(i);
                }else {
                    kn2Minus.add(i);
                }
            }
            if (result.get(i).size()==3){
                kn3.add(i);
            }
        }
//        System.out.println("排除"+kn1+"号硬币");
//        System.out.println("偏轻"+kn2Minus+"号硬币");
//        System.out.println("偏重"+kn2Positive+"号硬币");
//        System.out.println("未知"+kn3+"号硬币");
        if (kn1.size()==yingBiSize-1){
            if (kn2Minus.size()==1){
                System.out.println("第"+kn2Minus.get(0)+"硬币质量轻");
            }else if(kn2Positive.size()==1) {
                System.out.println("第"+kn2Positive.get(0)+"硬币质量重");
            }
        }
        if (kn1.size()==yingBiSize){
            System.out.println("没有假币");
        }
//        System.out.println(result);
    }

    public static void main(String[] args) {
        for (int i = 0; i < 12; i++) {
            Demo demo = new Demo(i, 1);
            while (demo.limit<=3){
                List<List<Integer>> grouping = demo.grouping();
                Integer compare = demo.compare(grouping.get(0), grouping.get(2));
                demo.checkResult(grouping.get(0), grouping.get(1),grouping.get(2),compare);
                demo.interpretationResult();

            }
            System.out.println("---------------");
        }

    }

}

程序结果如下

第1次测量,左为:[0, 3, 6, 9]右为[1, 4, 7, 10]结果为1
第2次测量,左为:[10, 0, 3]右为[6, 9, 2]结果为1
第3次测量,左为:[3]右为[1]结果为0
第0硬币质量重
---------------
第1次测量,左为:[0, 3, 6, 9]右为[1, 4, 7, 10]结果为-1
第2次测量,左为:[9, 1, 4]右为[7, 10, 2]结果为1
第3次测量,左为:[4]右为[0]结果为0
第1硬币质量重
---------------
第1次测量,左为:[0, 3, 6, 9]右为[1, 4, 7, 10]结果为0
第2次测量,左为:[2, 11, 3, 7]右为[5, 0, 4, 9]结果为1
第3次测量,左为:[2]右为[11]结果为1
第2硬币质量重
---------------
第1次测量,左为:[0, 3, 6, 9]右为[1, 4, 7, 10]结果为1
第2次测量,左为:[10, 0, 3]右为[6, 9, 2]结果为1
第3次测量,左为:[3]右为[1]结果为1
第3硬币质量重
---------------
第1次测量,左为:[0, 3, 6, 9]右为[1, 4, 7, 10]结果为-1
第2次测量,左为:[9, 1, 4]右为[7, 10, 2]结果为1
第3次测量,左为:[4]右为[0]结果为1
第4硬币质量重
---------------
第1次测量,左为:[0, 3, 6, 9]右为[1, 4, 7, 10]结果为0
第2次测量,左为:[2, 11, 3, 7]右为[5, 0, 4, 9]结果为-1
第3次测量,左为:[2]右为[11]结果为0
第5硬币质量重
---------------
第1次测量,左为:[0, 3, 6, 9]右为[1, 4, 7, 10]结果为1
第2次测量,左为:[10, 0, 3]右为[6, 9, 2]结果为-1
第3次测量,左为:[6]右为[9]结果为1
第6硬币质量重
---------------
第1次测量,左为:[0, 3, 6, 9]右为[1, 4, 7, 10]结果为-1
第2次测量,左为:[9, 1, 4]右为[7, 10, 2]结果为-1
第3次测量,左为:[7]右为[10]结果为1
第7硬币质量重
---------------
第1次测量,左为:[0, 3, 6, 9]右为[1, 4, 7, 10]结果为0
第2次测量,左为:[2, 11, 3, 7]右为[5, 0, 4, 9]结果为0
第3次测量,左为:[8, 2, 5, 9]右为[0, 3, 6, 10]结果为1
第8硬币质量重
---------------
第1次测量,左为:[0, 3, 6, 9]右为[1, 4, 7, 10]结果为1
第2次测量,左为:[10, 0, 3]右为[6, 9, 2]结果为-1
第3次测量,左为:[6]右为[9]结果为-1
第9硬币质量重
---------------
第1次测量,左为:[0, 3, 6, 9]右为[1, 4, 7, 10]结果为-1
第2次测量,左为:[9, 1, 4]右为[7, 10, 2]结果为-1
第3次测量,左为:[7]右为[10]结果为-1
第10硬币质量重
---------------
第1次测量,左为:[0, 3, 6, 9]右为[1, 4, 7, 10]结果为0
第2次测量,左为:[2, 11, 3, 7]右为[5, 0, 4, 9]结果为1
第3次测量,左为:[2]右为[11]结果为-1
第11硬币质量重
---------------


其中在计算这么摆放硬币最优grouping()方法,感觉很难写,而且当硬币达到39时,目前程序会出错
大家有什么更好的写发没,还有遇到这种逻辑题怎么用代码解决

注: 参考了b站up主Solara570的视频 论坛littlefall作者的博客

免费评分

参与人数 1热心值 +1 收起 理由
teawithmilk + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

hs248613 发表于 2022-8-13 08:24
数学不好,读了三分钟的题目和解析才看懂楼主意思,给和我一样数学不好的人啰嗦说明下:
1、假币可能比真币重,或比真币轻,2种可能,合起来就是楼主说的12*2=24可能
2、我们遍历24种可能就一定知道答案
3、称一次有有3种可能,左轻,平衡,右轻,称3次就是3^3=27种可能,27种可能涵盖了24种可能,再少就是称两次,3^2=9种可能比24小。再多就是称4次,3^4=81种可能比24大,但太多。
4、我们求最少称多少次,3次最佳,所以理论上3次可行
iawyxkdn8 发表于 2022-8-13 08:55
hs248613 发表于 2022-8-13 09:33
看看B站这位UP做的视频,把问题数学化了
https://www.bilibili.com/video/BV1Fy4y1T7mL?spm_id_from=333.999.0.0&vd_source=61a104c80d3fb90797dd9c7a15f4ba59
 楼主| jgdfc 发表于 2022-8-13 12:09
hs248613 发表于 2022-8-13 09:33
看看B站这位UP做的视频,把问题数学化了
https://www.bilibili.com/video/BV1Fy4y1T7mL?spm_id_from=333.9 ...

看过了,但我想用程序来描述这个过程
CXC303 发表于 2022-8-13 13:22
感谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止回复与主题无关非技术内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-29 04:59

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表