关于吸血鬼数字算法问题,我也是读《java编程思想》中遇到的,觉得很有意思。于是,就去做了做。但因为我的粗心,读题的时候忽略了点问题,所以导致我的思路出现了岔口!(当时的思路就是想着把一个4位数拆分成两个2位数,存在数组的形式找出吸血鬼数字,也就是官方答案的那种解法)


虽然我没有解出来,但是我对吸血鬼数字的算法问题做了总结、整理,也搜集了很多解法,并给他们编写了大量的注释,为让你们看的更明白。注释中如有错误,请来指正,我将虚心更改!


《Java编程思想》中的Dan Forhan推荐
什么是“吸血鬼”数字呢?

“吸血鬼”数字是指位数为偶数的数字,可以由一对数字相乘而得到,而这对数字各包含乘积一半位数的数字,其中从最初的数字中选取的数字可以任意排序。(以两个0结尾的数字是不允许的),例如下列数字都是“吸血鬼”数字
1260=2160
1827=21
87
2187=27*81

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


解法一、字符串拆分法

思路:虽然我不知道这是谁写的,但是我的确佩服他的解法。
这个解法,利用了字符串的拆分的性质,将4位数拆分成两个2位数,再对它进行字符的拆分、比较得出的吸血鬼数字!
从数据看来比较的次数是3271次

/*** * 吸血鬼数字算法*/
public class TestVampireNumber {public static void main(String[] args) {//创建存放两个两位数的字符串数组String[] ar_str1, ar_str2;int sum = 0; //吸血鬼数字个数计数器int count = 0; //算法循环次数计数器for (int i = 10; i < 100; i++) { //寻找的第一个两位数for (int j = i + 1; j < 100; j++) { //寻找的第二个两位数int i_val = i * j; //两个两位数的乘积if (i_val < 1000 || i_val > 9999)continue; //积小于1000或大于9999排除,继续下一轮环count++; //计算循环次数ar_str1 = String.valueOf(i_val).split(""); //将乘积后的数字拆分成单个字符串并放在字符串数组中ar_str2 = (String.valueOf(i) + String.valueOf(j)).split(""); //同样将两个两位数拼成一个字符串并拆分成单个字符串放在字符串数组中java.util.Arrays.sort(ar_str1); //对两个数组中的元素进行排序java.util.Arrays.sort(ar_str2);if (java.util.Arrays.equals(ar_str1, ar_str2)) { //乘积排序与两个两位数的所有单个字符串进行比较// 排序后比较,为真则找到一组(也就是说他们比较左边四个数右边四个数比较完全相等的时候返回true,记为一组吸血鬼数字)sum++;//吸血鬼数字个数计数System.out.println("第" + sum + "组: " + i + "*" + j + "=" + i_val);}}}System.out.println("共找到" + sum + "组吸血鬼数" + "\ncount = " + count);}
}


解法二、《Thinking in Java》官方答案

思路:利用了数字的位数拆分,通过算法满足条件,将4位数拆分成两个2位数,再将乘积和两个2位数进行位数的拆分。两个嵌套循环遍历,利用计数器满足4次相等来实现找出吸血鬼数字!
注意:两个嵌套循环遍历时,会出现像是连线一样出现重复比较的问题,此问题官方答案中有解决我也加了注释
从数据看来比较的次数是310次

//: control/E10_Vampire.java
/******************* Exercise 10 ********************* A vampire number has an even number of* digits and is formed by multiplying a pair of numbers containing half the* number of digits of the result. The digits are taken from the original number* in any order. Pairs of trailing zeroes are not allowed. Examples include:* 1260 = 21 * 60 1827 = 21 * 87 2187 = 27 * 81 Write a program that finds all* the 4-digit vampire numbers. (Suggested by Dan Forhan.)* * 中文翻译:* 一个吸血鬼数字有偶数个数字,它是由一对数字相乘而成,其中的一半是吸血鬼的* 结果的位数。这些数字取自原始数字* 在任何顺序。不允许有一对尾随的0。例子包括:* 1260 = 21 * 60 1827 = 21 * 87 2187 = 27 * 81编写一个程序,找出所有* 4位数的吸血鬼号码。(Dan Forhan建议)****************************************************/
public class E10_Vampire {public static void main(String[] args) {int sign = 0;//计数器,计比较次数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)// 中文翻译:// Pete Hartley的理论结果:// 如果x·y是吸血鬼的号码// 就满足这个条件 x·y == x+y (mod 9)if ((num1 * num2) % 9 != (num1 + num2) % 9)continue; //如果满足条件就继续下去sign++;//计数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; //因为假如左边数组4个数字、右边数组4个数字,让左边的遍历去进行和右边的比较startDigit[y] = -2;   //就像连线一样,我们满足条件之后也会多比较一次相同的数字,我们就想办法避免//这个重复的比较,所以进行了对这个数组下标值得改变,因此避免了这个问题//比如:数字中出现重复数字,如果不将左右值变了的话,就会出现和重复的值比较if (count == 4) //满足4个单数字都相等即计数器计了4次数,满足了吸血鬼数字System.out.println(num1 + " * " + num2 + " : " + product); //打印所有吸血鬼数字}}}System.out.println("count = " + sign);}
}


当然,我发现官方的答案中也是可以优化的,比较的次数明显较少了几十次

//将num2 = num1 改为 num2 = num1 + 1 做优化
for (int num2 = num1 + 1; num2 <= 99; num2++) { //寻找第二个两位数


解法三、参考老紫竹先生整理出来的解法,我做了记录和注释。这不得不说我们的前辈真是是太聪明了!点赞!我学到了!

思路:这个解法和第一种字符串拆分的解法特别像,主要是用了更好的数学算法作为核心,大大减少了比较的次数
从数据看来比较的次数是232次,不愧是高效算法!
算法核心

 //i_val % 100 == 0 的解释://吸血鬼算法得出的数字不能是两个00结尾的,所以i_val % 100 == 0就直接continue//i_val - i - j) % 9 != 0 的解释://假设i_val = 1000a + 100b + 10c + d,因为满足i_val = x * y,则有x = 10a + b,y = 10c + d //则i_val - x - y = 990a + 99b + 9c = 9 * (110a + 10b),所以i_val - x - y能被9整除//所以满足该条件的数字必定能被9整除,所以可以直接过滤其他数字if (i_val % 100 == 0 || (i_val - i - j) % 9 != 0) {continue;}

高效主代码

import java.util.Arrays;/*** 吸血鬼数字,高效率版本* 一个4位数字,可以拆分2个2位数数字的乘积,顺序不限* 比如 1395 =15 * 93*/
public class Vampire {public static void main(String[] arg) {String[] ar_str1, ar_str2;//创建两个字符串数组int sum = 0;//吸血鬼数字的个数int from;int to;int i_val;//拆分成两个两位数的乘积int count = 0;//计比较次数的计数器//双重循环穷举for (int i = 10; i < 100; i++) {// j=i+1避免重复from = Math.max(1000 / i, i + 1);to = Math.min(10000 / i, 100);for (int j = from; j < to; j++) {i_val = i * j;//i_val % 100 == 0 的解释://吸血鬼算法得出的数字不能是两个00结尾的,所以i_val % 100 == 0就直接continue//i_val - i - j) % 9 != 0 的解释://假设i_val = 1000a + 100b + 10c + d,因为满足i_val = x * y,则有x = 10a + b,y = 10c + d //则i_val - x - y = 990a + 99b + 9c = 9 * (110a + 10b),所以i_val - x - y能被9整除//所以满足该条件的数字必定能被9整除,所以可以直接过滤其他数字if (i_val % 100 == 0 || (i_val - i - j) % 9 != 0) {continue;}count++;//计数ar_str1 = String.valueOf(i_val).split("");//后面这些代码的做法就是字符串拆分法的那个解释ar_str2 = (String.valueOf(i) + String.valueOf(j)).split("");Arrays.sort(ar_str1);Arrays.sort(ar_str2);if (Arrays.equals(ar_str1, ar_str2)) { //排序后比较,为真则找到一组sum++;//计数System.out.println("第" + sum + "组: " + i + "*" + j + "=" + i_val);}}}System.out.println("共找到" + sum + "组吸血鬼数");System.out.println("count = " + count);}
}


而我们将from和to注释掉在第二个for循环做改动呢?

//将from和to做了改动
for (int j = from; j < to; j++)
//改为
for (int j = i + 1; j < 100; j++)

看到结果,明显比使用from和to的方法多比较了几十次

前辈们的思想真是太牛了,能把范围缩小到这么小,而找出最少比较的算法!点赞!学到了!
向你们看齐,向你们学习!加油!

怎样轻松找出4位数的所有“吸血鬼”数字,多种高效算法详解相关推荐

  1. java 4位数,java 找出4位数的所有吸血鬼数字

    java 找出4位数的所有吸血鬼数字 /** * 找出四位数所有的吸血鬼数字 * 吸血鬼数字:位数为偶数的数字可以由一对数字相乘而得,这对数字包含乘积一半的位数 * 如:1260 = 21*60 */ ...

  2. Java 找出四位数的所有吸血鬼数字 基础代码实例

    /**  * 找出四位数的所有吸血鬼数字  * 吸血鬼数字是指位数为偶数的数字,可以由一对数字相乘而得到,而这对数字各包含乘积的一半位数的数字,其中从最初的数字中选取的数字可以任意排序.  * 以两个 ...

  3. java编程找出吸血鬼数字,Java 找到四位数的所有吸血鬼数字 基础代码实例

    Java 找出四位数的所有吸血鬼数字 基础代码实例 /** * 找出四位数的所有吸血鬼数字 * 吸血鬼数字是指位数为偶数的数字,可以由一对数字相乘而得到,而这对数字各包含乘积的一半位数的数字,其中从最 ...

  4. c语言:找出1到4000中,数字的各位数之和能被4整除的数有多少个?

    找出1到4000中,数字的各位数之和能被4整除的数,如:745:7+4+5=16,16可以被4整除:28:2+8=10,10不能被4整除:745就是这样的特殊数字,而28不是,求:这样的数字共有多少个 ...

  5. 找出数组中任一重复的数字

    找出数组中任一重复的数字 找出数组中任一重复的数字   在一个长度为 n 的数组 nums 里的所有数字都在 0-n-1 的范围内.数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重 ...

  6. java编程找出吸血鬼数字,找出四位數的所有吸血鬼數字(JAVA)

    /** * 找出四位數的所有吸血鬼數字 * 吸血鬼數字是指位數為偶數的數字,可以由一對數字相乘而得到,而這對數字各包含乘積的一半位數的數字,其中從最初的數字中選取的數字可以任意排序. * 以兩個0結尾 ...

  7. 算法期中1007. 怪兽训练 (找出有向图中所有的强连通分量的Kosaraju算法)

    Description 贝爷的人生乐趣之一就是约战马会长. 他知道马会长喜欢和怪兽对决,于是他训练了N只怪兽,并对怪兽用0到N-1的整数进行编号. 贝爷训练怪兽的方式是让它们一对一互殴. 两只怪兽互殴 ...

  8. 如何在10亿个整数中找出前1000个最大的数(TopN算法)

    面试题目:如何在10亿个整数中找出前1000个最大的数. 我们知道排序算法有很多: 冒泡算法:通过两层for循环,外层第一次循环找到数组中最大的元素放置在倒数第一个位置,第二次循环找到第二大的元素放置 ...

  9. [LeetCode] Find All Numbers Disappeared in an Array 找出数组中所有消失的数字

    Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and ot ...

最新文章

  1. UI之UI View--属性及用法
  2. git提交时支持文件名大小写的修改
  3. 汇编语言第二课作业2.1
  4. 物联网 数据驱动企业 如何应对数据洪流
  5. AB1601烧程序时注意事项
  6. Linux 下的dd命令使用详解(摘录)
  7. 在node.js中如何使用ES6模块化
  8. 更换mysql_安利给你,关于MySQL字符集乱码与解决方案
  9. cannot restore segment prot after reloc: Permission denied
  10. MarkDown下载以及入门语法(一)
  11. PicGO + 阿里云对象存储OSS 个人图床入门教程
  12. nginx配置多个域名转向80端口
  13. 微信小程序使用sass
  14. MyDLNote-High-Resolution: CooGAN: 协同GAN网络,高分辨率面部属性的高效记忆框架
  15. [经验教程]iPhone苹果手机电池健康度怎么查询及如何更换苹果iPhone手机电池恢复健康度到100%?
  16. HDU - 6609
  17. 货郎担问题java算法_迷宫最短路径-货郎担问题的解决思路
  18. 小球间完全弹性斜碰(赋Python代码)
  19. 数据库~如何快速、准确选取候选码(候选键)?
  20. 跟着 NC 学作图 | 多组散点图+配对连线+差异分析

热门文章

  1. 【postgreSQL】时间类型模糊查询
  2. IU5706 外置MOS、33V输出大功率同步升压芯片产品介绍
  3. html中插入图片img的相对路径
  4. 产品窜货是什么意思?怎么防止窜货行为?
  5. Linux 查看网关gateway方法
  6. 【youcans 的 OpenCV 例程200篇】137. 灰度开运算和灰度闭运算原理
  7. 软件测试常见笔试面试题---含答案
  8. 微信小程序项目实例——备忘录
  9. 自己做量化交易软件(34)小白量化实战7--投资理念与通达信公式回测(2)
  10. java addall equals_java.util.Collections.addAll()