习题:吸血鬼数字是指位数为偶数的数字,可以由一对数字相乘而得来,而这对数字各包含乘积的一半位数的数字,其中从最初的数字中选取的数字可以任意排序。以两个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都是参考其他网友的实现。