奇妙的算法【2】- 韩信点兵问题优化

 1,问题介绍

  ①淮安民间传说着一则故事——“韩信点兵”,其次有成语“韩信点兵,多多益善”。

韩信带1500名兵士打仗,战死四五百人,站3人一排,多出2人;站5人一排,多出4人;站7人一排,多出6人。韩信很快说出人数:104。

  ②在一千多年前的《孙子算经》中,有这样一道算术题:“今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问物几何?”按照今天的话来说:一个数除以3余2,除以5余3,除以7余2,求这个数。这样的问题,也有人称为“韩信点兵”。它形成了一类问题,也就是初等数论中的解同余式。

  ③问题:一个数除以3余2,除以5余3,除以7余2,求符合条件的最小数。

2,结题思路

思路1:【暴力求解法-遍历,效率低】

直接从0一直计算到很大的数值来进行判断

    public static int dealSingle1(int index0,int index1,int index2){//暴力求解
        boolean a,b,c;
        for(int i=0;i<Integer.MAX_VALUE;i++){
            a=(i%3==index0);
            b=(i%5==index1);
            c=(i%7==index2);
            if(a&&b&&c){
                return i;
            }
        }
        return -1;
    }

 

思路2:【利用数学的知识进行运算,进一步优化,应该是速度最快的】

  先列出除以3余2的数:2,5,8,11,14,17,20,23,26……

  再列出除以5余3的数:3,8,13,18,23,28……
  这两列数中,首先出现的公共数是8。3与5的最小公倍数是15。两个条件合并成一个就是8+15×整数,列出这一串数是8,23,38,……,再列出除以7余2的数2,9,16,23,30……就得出符合题目条件的最小数是23。
  事实上,我们已把题目中三个条件合并成一个:被105除余23。
    public static int dealSingle2(int index0,int index1,int index2) {//2,数学方法求解
        long startTime =  System.currentTimeMillis();
        int num=0,num1=0;
        for(int i=0;i<Integer.MAX_VALUE;i++){
            num=5*i+index1;//修改优化,直接使用较大大数进行取值,进一步加快速度
            if(num%3==index0){
                break;
            }
        }
        for(int i=0;i<Integer.MAX_VALUE/15;i++){
            num1=num+15*i;
            if(num1%7==index2){
                long endTime =  System.currentTimeMillis();
                System.out.println("方法2:数学优化,耗时:"+(endTime-startTime));
                return num1;
            }
        }
        return -1;
    }

思路3:【由大数主导数值运算,速度也很快】

    public static int dealSingle3(int index0,int index1,int index2){//处理单个数据
        long startTime =  System.currentTimeMillis();
        int num=-1;
        boolean a,b;
        for(int i=0;i<Integer.MAX_VALUE/7;i++){
            num=i*7+index2;
            a=(num%3==index0);
            b=(num%5==index1);
            if(a&&b){
                long endTime =  System.currentTimeMillis();
                System.out.println("方法3:数学优化,耗时:"+(endTime-startTime));
                return num;
            }
        }
        return num;
    }

 3,问题引申

  计算出韩信点兵的所有结果【3,5,7全部为素数,那么在0~(3*5*7-1)这个范围内的所有数据都是其中的一种情况】

    public static int[][][] dealAll(){//每次只计算一次,直到所有数据
        int[][][] arr=new int[3][5][7];
        for(int i=0;i<(3*5*7);i++){
            arr[i%3][i%5][i%7]=i;
        }
        return arr;
    }

posted on 2019-08-25 21:12  周健康  阅读(1145)  评论(0编辑  收藏  举报

导航