首页 > 技术文章 > Java求吸血鬼数算法(通用)

yanguangyu 2017-12-19 14:45 原文

/*吸血鬼数字是指位数为偶数的数字,可以由一
 * 对数字相乘而得到,而这对数字各包含乘积的一半位数的数字,
 * 其中从最初的数字中选取的数字可以任意排序。
 * 以两个0结尾的数字是不允许的。
 *
 * 1994年柯利弗德·皮寇弗在Usenet社群sci.math的
 * 文章中首度提出吸血鬼数。后来皮寇弗将吸血鬼数
 * 写入他的书Keys to Infinity的第30章。
 *
 * 以两个0结尾的数字是不允许的,例如,下列数字都是“吸血鬼”数字:
 * 1260 = 21 * 60  1827 = 21 * 87  2187 = 27 * 81
 * 4位数的吸血鬼数有7个:
 * 1260, 1395, 1435, 1530, 1827, 2187, 6880
 * */

/*算法思路
 * 1.获取符合吸血鬼数的范围值(范围值容易理解不再多说)
 * 2.从范围值中算出吸血鬼数
 *      a).从吸血鬼的定义中取得条件:
 *          一,大于4位的偶数;
 *          二,两个乘数的位数分别占吸血鬼数位数的一半;
 *          三,以两个0结尾的数字是不允许的;
 *          四,两个乘数的数字与乘积的数字排序后一一对应;
 *(不是相等,是一一对应,因为有0的吸血鬼数的数字排序后0在前面,重点)
 * 作者:Jack.Yan  完成时间:2017/12/19
 * */

public class DanforhanTest {

    public static void main(String[] args) {

        //参数为吸血鬼数字的位数,只可为偶数
        getVampireNum(8); //这里实参为8,即找出 8 位的吸血鬼数; 
                                  //此方法返回值为void,只是把找出的吸血鬼数打印出来.

    }

    public static void getVampireNum(int count){

        if(count<4 && count%2 != 0)  //限制为偶数位数且至少为4位
            return;

        //    4            6           8
        // 0 1 2 3       0 1 3 4     0 1 4 5    
        //这些数字分别为求得 num1,num2,num3,num4 的幂值,即 10^n
        //这里列出为了让读者易于理解下面的代码
        /*例如:4位的吸血鬼数,两个乘数最小的起始值为 10,
         *达到的最大值为99(因为两个乘数分别占吸血鬼数位数的一半)
         *而4位的吸血鬼数的范围值为 1000 至 9999*/

        //num1为 两个乘数最小的起始值
        int num1 = (int)Math.pow(10,count-(count/2+1));
        //num2为 两个乘数达到的最大值为(下面 if 中判断是 i <= num2 , j <= num2 ;所以要减去 1)
        int num2 = (int)Math.pow(10,count-(count/2)) - 1;

        //num3 和 num4 为四位吸血鬼数的范围值
        int num3 = (int)Math.pow(10,count-1);
        int num4 = (int)Math.pow(10,count) - 1;

        //i 为左边乘数 j 为右边乘数;k 记录吸血鬼数个数
        int i=num1,j=i;int k=0;
        outer:
            while(true){
            i++;
            if(i <= num2){
                    j = i;  // j不赋num1值而赋i的值,是为了避免反向相乘例如 (15 * 30 和 30 * 15)
                while(true){
                    j++;
                    if (j <= num2){

                          if (i*j < num3){
                            continue;

                           }else if (i*j <= num4){
 
                               if(VampireNum(i,j,i*j)){  //调用方法,返回true则是吸血鬼数,打印
                               System.out.println(i+" * "+j+" = "+i*j);
                               k++;
                               }

                           }else{ // 计算结果超出了范围值
                             continue outer;// 跳出整个循环 
                           }

                    }else{
                      break;
                    }

                }

            }else{
              break;
            }

        }
        System.out.println(count+"位的吸血鬼数有"+k+"个");
    }

 

    private static boolean VampireNum(int x,int y,int w){

        if(w%100 == 0)  //排除所有两个00结尾的数字
            return false;

        String str_xy = Integer.toString(x);
        str_xy += Integer.toString(y); /*两个乘数合并为一个字符串*/
        String str_w = Integer.toString(w);

        //将乘数和乘积数转为 char[] ,然后排序和比较
        char charXY[] = str_xy.toCharArray();
        char charW[] = str_w.toCharArray();

        //排序 (这里要提高性能可以选择java类库中的方法;为了清晰可见,这里自己写了冒泡排序)

        char tmp = '0';
        for(int i=0;i<charXY.length-1;i++){
            for(int j=i;j<charXY.length;j++){
                if(charXY[i] > charXY[j]){
                    tmp = charXY[i];
                    charXY[i] = charXY[j];
                    charXY[j] = tmp;
                }

                if(charW[i] > charW[j]){
                    tmp = charW[i];
                    charW[i] = charW[j];
                    charW[j] = tmp;
                }
            }

        }

        //比较
        int res = 0;
        for(int i =0;i < charW.length;i++){
            if(charXY[i] == charW[i])
                res ++;
        }

        //完全配对则为吸血鬼数
        if(res == charW.length)
            return true;
        else
            return false;

    }

}

   JackYan.print(" 祝大家2018元宵节快乐!");

 

推荐阅读