简单了解吸血鬼数
今天看文档,突然看到一个奇怪的名字——“吸血鬼数”。
大概查了一下,这种数字有正常的吸血鬼数,伪吸血鬼数和质吸血鬼数。
正常的吸血鬼数,有以下特征:
1.位数为偶数;
2.由两个数字相乘得到,并且得到的乘积数字中,包含这两个数字拥有的全部数字,单个数字的数量也是一致的;
3.不以两个0结尾。
比如:21×60 = 1260,其中乘积1260就是正常的吸血鬼数,因为该数字包含了乘数21和被乘数60所拥有的所有数字。21和60即是该吸血鬼数的两个尖牙。
再比如:210×600 = 126000,因为210和600都以0为个位数(结尾),所以乘积126000就不是一个吸血鬼数。
再比如:31×33 = 1023,因为乘积1023中,拥有乘数31和被乘数33没有的数字“0”和“2”,所以乘积1023也不是一个吸血鬼数。
一个吸血鬼数可以多对尖牙。在四位数的吸血鬼数中,有一个数字125460,该数字就很特殊:204×615=246×510=125460。所以204、615和246、510都是125460的尖牙。
伪吸血鬼数:和正常的吸血鬼数唯一不同的就是,其位数可以是奇数。
质吸血鬼数:尖牙是质因数(能整除给定正整数的质数)的吸血鬼数,例如117067, 124483, 146137, 371893, 536539。
查找吸血鬼数
因为有点空余时间,所以打算用java实现一下正常吸血鬼数的查找逻辑。
实现时,遵循的基本查找规则,就是正常吸血鬼数拥有的那3个特征。
查找的实现方式有多种,下面是我比较喜欢的一个实现方式。
该实现方式可以查找任意指定位数(位数必须是偶数)的吸血鬼数。
小伙伴们可以自己拷贝代码执行一下即可看到输出结果。
Java代码:
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 查找吸血鬼数字
* 数字特点:
* 位数为偶数
* 由两个数字相乘得到,并且得到的乘积数字中,包含这两个数字拥有的全部数字,单个数字的数量也是一致的
* 不已以两个0结尾
* @author Alisallon
*/
public class VampireTools {
private static class VampireData {
private int m; // 乘数
private int n; // 被乘数
private int v; // 乘积
private VampireData(int m, int n, int v) {
this.m = m;
this.n = n;
this.v = v;
}
}
public static void main(String[] args) {
// 查找4位数范围内的吸血鬼数字
System.out.println("查找4位数范围内的吸血鬼数字");
findVampireNum(4);
// 查找6位数范围内的吸血鬼数字
System.out.println("查找6位数范围内的吸血鬼数字");
findVampireNum(6);
// // 查找8位数范围内的吸血鬼数字
// System.out.println("查找8位数范围内的吸血鬼数字");
// findVampireNum(8);
}
/**
* 查找指定位数的吸血鬼数字
* @author Alisallon
*
* @param digits int 指定位数
*/
private static void findVampireNum(int digits) {
if (digits < 4) {
System.out.println("查找的位数至少是4位");
return;
}
if (digits % 2 != 0) {
System.out.println("查找的位数必须数偶数");
return;
}
// 计算开始查找的位置
int startNum = Double.valueOf(Math.pow(10, digits / 2 - 1)).intValue();
// 计算结束查找的位置
int endNum = Double.valueOf(Math.pow(10, digits / 2)).intValue() - 1;
// 查找开始时间
long startTime = System.currentTimeMillis();
// 存放满足条件的吸血鬼数字对象,注意防止出现重复组合,比如21 * 60 和 60 * 21
Map<String, VampireData> vampireMap = new HashMap<>();
for (int m = startNum; m < endNum; m++) {
for (int n = startNum; n < endNum; n++) {
// 排除长度不相等的两个数字
if (String.valueOf(m).length() != String.valueOf(n).length()) {
continue;
}
int value = m * n;
// 排除小于最小乘积和大于最大乘积的乘积
if (value < startNum * endNum || value > startNum * endNum * 10 - 1) {
continue;
}
// 去除以两个0结尾的乘积
if (value % 100 == 0) {
continue;
}
// 检测指定两个数字的乘积是否是吸血鬼数字
checkNumIsVampireNum(m, n, vampireMap);
}
}
// 查找结束时间
long endTime = System.currentTimeMillis();
System.out.println("吸血鬼数字查找时间: " + (endTime - startTime) + "ms");
Set<Map.Entry<String, VampireData>> entrySet = vampireMap.entrySet();
// 存放吸血鬼数字对象,用于排序显示
List<VampireData> valueList = new ArrayList<>();
// 保存非重复的吸血鬼数字
Set<Integer> valueSet = new HashSet<>();
for (Map.Entry<String, VampireData> entry : entrySet) {
valueList.add(entry.getValue());
valueSet.add(entry.getValue().v);
}
System.out.println("吸血鬼数字数量: " + vampireMap.size() + " 其中重复的数量:" + (vampireMap.size() - valueSet.size()));
// 对valueList降序排序
valueList.sort(Comparator.comparingInt(o -> o.v));
for (VampireData vampireData : valueList) {
// 控制台输出结果
System.out.println(vampireData.m + " * " + vampireData.n + " = " + vampireData.v);
}
}
/**
* 检测指定两个数字的乘积是否是吸血鬼数字
* @author Alisallon
*
* @param m int
* @param n int
* @param vampireMap Map<String, VampireData> 存放满足条件的吸血鬼数字对象,注意防止出现重复组合,比如21 * 60 和 60 * 21
*/
private static void checkNumIsVampireNum(int m, int n, Map<String, VampireData> vampireMap) {
char[] charArray = concat(String.valueOf(m).toCharArray(), String.valueOf(n).toCharArray());
char[] valueStrArray = String.valueOf(m * n).toCharArray();
for (char aCharArray : charArray) {
// 乘积必须包含两个乘数拥有的所有数字
// 检测乘积字符数组中指定的字符,并替换为其他非数组字符
if (!checkAndReplaceChar(aCharArray, valueStrArray)) {
return;
}
}
// 判断乘积字符数组中,是否还有数字,如果还有数字,则表示m * n的值,不是吸血鬼数字
String valueStr = String.valueOf(valueStrArray);
Pattern p = Pattern.compile(".*\\d+.*");
Matcher matcher = p.matcher(valueStr);
if (!matcher.matches()) {
// 乘积字符数组中没有数字了,全部被替换为其他非数组字符,m * n的值是吸血鬼数字
// 比较大小,是为了防止出现重复组合,比如21 * 60 和 60 * 21, 在这里统一成 21 * 60
if (m < n) {
vampireMap.put(m + " * " + n, new VampireData(m, n, m * n));
} else {
vampireMap.put(n + " * " + m, new VampireData(n, m, m * n));
}
}
}
/**
* 检测并替换乘积字符数组中指定的字符
* @author Alisallon
*
* @param c char 指定的字符
* @param valueCharArray char[] 乘积字符数组
* @return boolean 检测并替换结果
*/
private static boolean checkAndReplaceChar(char c, char[] valueCharArray) {
for (int index = 0; index < valueCharArray.length; index++) {
if (valueCharArray[index] == c) {
// 替换该位置字符为x,也可以是其他非数字字符
valueCharArray[index] = 'x';
return true;
}
}
return false;
}
/**
* 合并两个char数组
* @author Alisallon
*
* @param first char[]
* @param second char[]
* @return char[] 合并后的char数组
*/
private static char[] concat(char[] first, char[] second) {
char[] result = Arrays.copyOf(first, first.length + second.length);
System.arraycopy(second, 0, result, first.length, second.length);
return result;
}
}
命令行输出(注意125460这个数字):
查找4位数范围内的吸血鬼数字
吸血鬼数字查找时间: 6ms
吸血鬼数字数量: 7 其中重复的数量:0
21 * 60 = 1260
15 * 93 = 1395
35 * 41 = 1435
30 * 51 = 1530
21 * 87 = 1827
27 * 81 = 2187
80 * 86 = 6880
查找6位数范围内的吸血鬼数字
吸血鬼数字查找时间: 148ms
吸血鬼数字数量: 142 其中重复的数量:1
201 * 510 = 102510
260 * 401 = 104260
210 * 501 = 105210
204 * 516 = 105264
150 * 705 = 105750
135 * 801 = 108135
158 * 701 = 110758
152 * 761 = 115672
161 * 725 = 116725
167 * 701 = 117067
141 * 840 = 118440
231 * 534 = 123354
281 * 443 = 124483
152 * 824 = 125248
231 * 543 = 125433
246 * 510 = 125460
204 * 615 = 125460
201 * 627 = 126027
261 * 486 = 126846
140 * 926 = 129640
179 * 725 = 129775
311 * 422 = 131242
323 * 410 = 132430
315 * 423 = 133245
317 * 425 = 134725
231 * 588 = 135828
351 * 387 = 135837
215 * 635 = 136525
146 * 938 = 136948
350 * 401 = 140350
351 * 414 = 145314
317 * 461 = 146137
156 * 942 = 146952
251 * 608 = 152608
261 * 585 = 152685
356 * 431 = 153436
240 * 651 = 156240
269 * 581 = 156289
165 * 951 = 156915
176 * 926 = 162976
396 * 414 = 163944
221 * 782 = 172822
231 * 750 = 173250
371 * 470 = 174370
231 * 759 = 175329
225 * 801 = 180225
201 * 897 = 180297
225 * 810 = 182250
281 * 650 = 182650
216 * 864 = 186624
210 * 906 = 190260
210 * 915 = 192150
327 * 591 = 193257
395 * 491 = 193945
275 * 719 = 197725
252 * 801 = 201852
255 * 807 = 205785
216 * 981 = 211896
341 * 626 = 213466
251 * 860 = 215860
323 * 671 = 216733
321 * 678 = 217638
248 * 881 = 218488
269 * 842 = 226498
276 * 822 = 226872
248 * 926 = 229648
338 * 692 = 233896
461 * 524 = 241564
422 * 581 = 245182
296 * 851 = 251896
350 * 725 = 253750
470 * 542 = 254740
323 * 806 = 260338
284 * 926 = 262984
437 * 602 = 263074
489 * 582 = 284598
420 * 678 = 284760
468 * 612 = 286416
320 * 926 = 296320
431 * 707 = 304717
431 * 725 = 312475
321 * 975 = 312975
534 * 591 = 315594
351 * 909 = 319059
336 * 951 = 319536
524 * 623 = 326452
342 * 963 = 329346
356 * 926 = 329656
530 * 635 = 336550
360 * 936 = 336960
392 * 863 = 338296
533 * 641 = 341653
366 * 948 = 346968
369 * 981 = 361989
392 * 926 = 362992
533 * 686 = 365638
585 * 630 = 368550
381 * 969 = 369189
383 * 971 = 371893
431 * 878 = 378418
435 * 870 = 378450
432 * 891 = 384912
465 * 831 = 386415
593 * 662 = 392566
446 * 908 = 404968
491 * 845 = 414895
641 * 650 = 416650
468 * 891 = 416988
482 * 890 = 428980
464 * 926 = 429664
476 * 941 = 447916
540 * 846 = 456840
546 * 840 = 458640
570 * 834 = 475380
624 * 780 = 486720
549 * 891 = 489159
545 * 899 = 489955
590 * 845 = 498550
681 * 759 = 516879
572 * 926 = 529672
563 * 953 = 536539
630 * 855 = 538650
588 * 951 = 559188
657 * 864 = 567648
650 * 875 = 568750
680 * 926 = 629680
650 * 983 = 638950
720 * 936 = 673920
788 * 926 = 729688
765 * 963 = 736695
843 * 876 = 738468
776 * 992 = 769792
875 * 902 = 789250
825 * 957 = 789525
855 * 927 = 792585
807 * 984 = 794088
891 * 909 = 809919
894 * 906 = 809964
858 * 951 = 815958
896 * 926 = 829696
891 * 945 = 841995
953 * 986 = 939658