题目向:4位数的吸血鬼数字

最近重温java,决定看《Java编程思想》,看到比较有意思的一道题:吸血鬼数字。其中参考博客为吸血鬼数字

题目
吸血鬼数字是指位数为偶数的数字,可以由一对数字相乘而得到,而这对数字各包含成绩的一半位数的数字,其中从最初的数字中选取的数字可以任意排序,以两个0结尾的数字是不允许的,例如:1260 = 21 * 60,写一个程序,找出4位数的所有吸血鬼数字

分析

  1. 错误想法:
    一开始我的想法是受到LeetCode136:只出现一次的数字这道题的启发,观察到两个两位数和最后相乘的结果中必定是每个数字呈偶次出现,结合异或自身为0的特性,头脑一热,想将所有的8个数字(两个两位数+一个四位数)存放进一个数组,之后对数组各元素进行异或,最后得到的异或结果为0则是吸血鬼数字,打印出来,完结撒花。但是调试时发现偏差,举个例子:4550 = 70 * 65。这一对明显不是吸血鬼数字,但是最终异或的结果为0。原因是5出现了三次,其他有的数字出现了一次,有的出现了两次,就有可能出现结果还为0的情况。此方案pass
  2. 正确思路:
    假设两个数字为i和j,其乘积为result = i × j。
    注意点1:我们都知道必须使用两层for循环,因此就会出现重复的问题,为了避免重复,假设j > i(事实上也可以假设i > j,结果都是一样的)。只要j从i开始计数,就不会出现类似于21 × 60 和 60 × 21一起出现的尴尬局面了。
    注意点2:由于i与j均两位数,因此result必须为四位数,因此i × j > 1000,即j > 1000 / i,取j = 1000 / i + 1,另外,到了循环后半段可能出现 i > 1000 / i + 1的情况,结合注意点1,我们不难得出结论:第二个循环j的初值应该为1000 / i + 1与i中较大的值
    注意点3:最为关键的一点,假设result = 1000a + 100b + 10c + d,那么i = 10a + b,j = 10c + d(i和j中的a、b、c、d可以在任意位置,比如i=10d + a, j = 10b + c,但是根据吸血鬼数的定义,一定是含有a、b、c、d这四个字母的),我们得出结论:result - i - j = 990a + 99b = 9(110a + 11b),因此4位数的吸血鬼数就有一条性质:能被9整除!根据这一点便能删减掉大部分不符合条件的数字。
    注意点4:可以将两个两位数存进一个数组Num_Array,乘积存进一个数组:Result_Array,之后将两个数组的值进行排序,排序之后调用Arrays类中的静态方法equals来比较两个数组里面的值是否一致。一致的可以下结论:找到了吸血鬼数

代码:

public class VampireNum {
    public static void main(String[] args) {
         char[] Num_Array = new char[4];          // 存储的是两个相乘的的二位数
         char[] Result_Array = new char[4];       // 存储相乘后的结果

        for (int i = 10; i < 100; i++){
            for (int j = 1000/i+1 > i ? 1000/i+1 : i; j < 100; j++) {   // 由于i * j > 1000, 假设j > i,则j必须从i或者1000/i+1中的较大的开始
                int result = i * j;
                if (result % 100 == 0 || (result - i - j) % 9 != 0 || result / 1000 > 9)     // 公式推导的结果
                    continue;
                Num_Array = (String.valueOf(i) + String.valueOf(j)).toCharArray();
                Result_Array = String.valueOf(result).toCharArray();
                Arrays.sort(Num_Array);
                Arrays.sort(Result_Array);
                if (Arrays.equals(Num_Array, Result_Array)) {
                    System.out.println(i + " * " + j + " = " + result);
                }
            }
        }
    }
}

运行结果

15 * 93 = 1395
21 * 60 = 1260
21 * 87 = 1827
27 * 81 = 2187
30 * 51 = 1530
35 * 41 = 1435
80 * 86 = 6880

番外

《Java编程思想》的源码,其原理和上面的注意点相似,但是少了j 在 1000/i+1 和 i之间选择的步骤,循环的更多。
此外,源码中的(num1 * num2) % 9 != (num1 + num2) % 9和注意点3异曲同工,都表示吸血鬼数可以被9整除(移项之后就和上面的结论一致)

public class E10_Vampire {
    public static void main(String[] args) {
        int[] startDigit = new int[4];                          
        int[] productDigit = new int[4];
        for (int num1 = 10; num1 <= 99; num1++)                   
            for (int num2 = num1; num2 <= 99; num2++) {
                // Pete Hartley's theoretical result:
                // If x·y is a vampire number then
                // x·y == x+y (mod 9)
                if ((num1 * num2) % 9 != (num1 + num2) % 9)
                    continue;
                int product = num1 * num2;
                startDigit[0] = num1 / 10;
                startDigit[1] = num1 % 10;
                startDigit[2] = num2 / 10;
                startDigit[3] = num2 % 10;
                productDigit[0] = product / 1000;
                productDigit[1] = (product % 1000) / 100;
                productDigit[2] = product % 1000 % 100 / 10;
                productDigit[3] = product % 1000 % 100 % 10;
                int count = 0;
                for (int x = 0; x < 4; x++)
                    for (int y = 0; y < 4; y++) {
                        if (productDigit[x] == startDigit[y]) {
                            count++;
                            productDigit[x] = -1;
                            startDigit[y] = -2;
                            if (count == 4)
                                System.out.println(num1 + " * " + num2 + " : "
                                        + product);
                        }
                    }
            }
    }

}

你可能感兴趣的:(算法题目,java,算法)