Java编程思想第四章练习题10:找出4位数的所有吸血鬼数

习题:吸血鬼数字是指位数为偶数的数字,可以由一对数字相乘而得来,而这对数字各包含乘积的一半位数的数字,其中从最初的数字中选取的数字可以任意排序。以两个0结尾的数字是不允许的,例如,下面的数字都是"吸血鬼"数字:

1395 = 15 * 93

1260 = 21 * 60

1530 = 30 * 51

写一个程序,找出4位数的所有吸血鬼数字。

 

方法1:从1000到9999的数字中,将以两个0结尾的数字和质数排除掉。然后将这个数各个位都拆分出来,如将千位、百位、十位、个位分别拆分为a、b、c、d,将这4个数组合成2个两位数,如ab、cd,再计算其乘积是否和拆分前的数字相等。因为,对4个数进行全排列,则有4!种排法,而ab、cd与cd、ab或ad、bc与bc、ad乘积相等,所以一共有12对两位数。代码实现如下:

    /**
     * 判断n是否为质数
     * 
     * @param n
     * @return true 是,false 否
     */
    public boolean isPrime(int n) {
        // 2和3是质数,1既不是质数也不是合数
        if (n <= 3) {
            return n > 1;
        }

        // 如果用2到sqrt(n)之间的所有整数去除,均无法整除,则n为质数
        int sqrtN = (int) (Math.sqrt(n) + 1);
        for (int i = 2; i < sqrtN; i++) {
            if (n % i == 0) {
                return false;
            }
        }

        return true;
    }

    public int[] splitInt(int a) {
        int[] splitResult = new int[4];
        splitResult[0] = a / 1000;
        splitResult[1] = (a / 100) % 10;
        splitResult[2] = (a / 10) % 10;
        splitResult[3] = a % 10;

        return splitResult;
    }

    public int comDoubleDigit(int s, int e) {
        return s * 10 + e;
    }

    public void printVampireNumber(int full, int a, int b) {
        if (a * b == full) {
            System.out.println(full + "=" + a + "*" + b);
        }
    }

    public void vampireNumbersOne() {
        long start = System.currentTimeMillis();
        for (int i = 1000; i < 9999; i++) {
            // 不能是以两个0结尾的数字
            if (i % 100 == 0) {
                continue;
            }

            // 不会是质数
            if (isPrime(i)) {
                continue;
            }

            // 将这个整数的各个位数拆分为数组
            int[] splitInt = splitInt(i);
            int a = 0, b = 1, c = 2, d = 3;

            printVampireNumber(i, comDoubleDigit(splitInt[a], splitInt[b]), comDoubleDigit(splitInt[c], splitInt[d]));
            printVampireNumber(i, comDoubleDigit(splitInt[a], splitInt[b]), comDoubleDigit(splitInt[d], splitInt[c]));
            printVampireNumber(i, comDoubleDigit(splitInt[a], splitInt[c]), comDoubleDigit(splitInt[b], splitInt[d]));
            printVampireNumber(i, comDoubleDigit(splitInt[a], splitInt[c]), comDoubleDigit(splitInt[d], splitInt[b]));
            printVampireNumber(i, comDoubleDigit(splitInt[a], splitInt[d]), comDoubleDigit(splitInt[b], splitInt[c]));
            printVampireNumber(i, comDoubleDigit(splitInt[a], splitInt[d]), comDoubleDigit(splitInt[c], splitInt[b]));
            printVampireNumber(i, comDoubleDigit(splitInt[b], splitInt[a]), comDoubleDigit(splitInt[c], splitInt[d]));
            printVampireNumber(i, comDoubleDigit(splitInt[b], splitInt[a]), comDoubleDigit(splitInt[d], splitInt[c]));
            printVampireNumber(i, comDoubleDigit(splitInt[b], splitInt[c]), comDoubleDigit(splitInt[d], splitInt[a]));
            printVampireNumber(i, comDoubleDigit(splitInt[b], splitInt[d]), comDoubleDigit(splitInt[c], splitInt[a]));
            printVampireNumber(i, comDoubleDigit(splitInt[c], splitInt[a]), comDoubleDigit(splitInt[d], splitInt[b]));
            printVampireNumber(i, comDoubleDigit(splitInt[c], splitInt[b]), comDoubleDigit(splitInt[d], splitInt[a]));
        }

        System.out.println("耗时为:" + (System.currentTimeMillis() - start) + "ms");
    }

 

方法2:获取2个两位数,再获取其乘积,比较这2个两位数和其乘积各个位上的数字是否相同。代码实现如下:

    public void vampireNumbersTwo() {

        long start = System.currentTimeMillis();
        for (int i = 10; i < 100; i++) {
            for (int j = i + 1; j < 100; j++) {
                // 获取2个两位数的乘积
                int target = i * j;
                if (target < 1000 || target > 9999) {
                    continue;
                }
                // 比较这2个两位数和其乘积是否包含相同的数字
                int[] targetNum = { target / 1000, target / 100 % 10, target / 10 % 100 % 10, target % 10 };
                int[] strNum = { i % 10, i / 10, j % 10, j / 10 };
                Arrays.sort(targetNum);
                Arrays.sort(strNum);
                if (Arrays.equals(targetNum, strNum)) {
                    System.out.println(target + " = " + i + " * " + j);
                }
            }
        }

        System.out.println("耗时为:" + (System.currentTimeMillis() - start) + "ms");
    }

 

方法3:与方法2基本相同,只是增加了一个(target - i - j) % 9 != 0的过滤条件,该条件是指:

假设四位数为1000a+100b+10c+d=val,则这2个两位数可以为(10a+c)*(10b+d)=x*y,这样用1000a+100b+10c+d-(10a+c)-(10b+d)=990a+90b+9c=9*(110a+10b+c),即val-x-y能被9整除;对于这2个两位数的其他组合,如(10a+b)*(10c+d),则1000a+100b+10c+d-(10a+b)-(10c+d)=990a+99b=9(110a+11b),val-x-y也能被9整除。因此,用其乘积分别减去这2个两位数应该能被9整除。代码实现如下:

    public void vampireNumbersThree() {

        long start = System.currentTimeMillis();
        int[] startDigit = new int[4];
        int[] productDigit = new int[4];
        int target;
        for (int i = 10; i <= 99; i++) {
            for (int j = i + 1; j <= 99; j++) {
                // 获取2个两位数的乘积
                target = i * j;
                if (target % 100 == 0 || (target - i - j) % 9 != 0) {
                    continue;
                }
                startDigit[0] = i / 10;
                startDigit[1] = i % 10;
                startDigit[2] = j / 10;
                startDigit[3] = j % 10;
                productDigit[0] = target / 1000;
                productDigit[1] = (target % 1000) / 100;
                productDigit[2] = target % 1000 % 100 / 10;
                productDigit[3] = target % 10;
                // 比较这2个两位数和其乘积是否包含相同的数字
                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(target + " = " + i + " * " + j);
                            }
                        }
                    }
                }
            }
        }

        System.out.println("耗时为:" + (System.currentTimeMillis() - start) + "ms");
    }

 

我觉得此题很有趣,故此记录一下,方法2和方法3都是参考其他网友的实现。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值