奇妙的算法【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; }
探究未知是最大乐趣