吸血鬼数字检验之java实现

2024-04-17 10:32

本文主要是介绍吸血鬼数字检验之java实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  • 1吸血鬼数字介绍
  • 2实现思路
  • 3代码实例

1、吸血鬼数字介绍

吸血鬼数字是指位数为偶数的数字,可以由一对数字相乘而得到,而这对数字各包含乘积的一半位数的数字,其中从最初的数字中选取的数字可以任意排序。–引自百度百科

  举几个例子就明白了:
   1260 = 21 * 60  1827 = 21 * 87  2187 = 27 * 81等等
   
  吸血鬼数字都是偶数位的,两个因子的组成数字都是从吸血鬼数字中得到的,且两个因子的所有数字等于吸血鬼数字中的所有数字(是完全相同,不能重复使用)。此外还有一个规定是吸血鬼数字不能是100的整数倍。

2、实现思路

首先来分析一下吸血鬼数字的必要条件:

  • 必须是偶数位的数字,121肯定不是;
  • 不能是100的整数倍,1000肯定不是;

这些都是吸血鬼数字的定义告诉我们的,除此之外还有哪些可以利用的条件呢?

  • 末尾数字相乘%10等于吸血鬼数字的末尾数字,考虑到最后一位是0时,采用倒数第二个数字检验。比如上面的1827,最后一位是7,那么组成该数字的两个数字末尾乘积取模10肯定是7,1260中,两个因子的最后一位必为1或6,且1和6不能在同一个数字中。

通过上面这些条件我们就可以减少一些比较量,尤其是前两个条件,可以直接判断一些非吸血鬼数字,而第三个条件就需要进行转化和判断,尤其是遇到0结尾的数字。

如果上面三个条件都满足,就需要直接寻找两个因子来判断是否是吸血鬼数字了,

  • 穷举法 比较简单地方式就是穷举所有可能的数字,先判断两个因子乘积是否等于该数字,等于则判断组成两个因子的数字是否与要判断的吸血鬼数字组成完全一致,只有在满足这两个条件的情况下才能进行判断该数字是吸血鬼数字。比如判断一个四位数字是否是吸血鬼数字,则需要验证所有的两位数字乘积。

  • 排列法 我自己暂且叫这个名字吧,因为因子是吸血鬼数字的再排列,因此我们找到所有的排列形式,然后一分为二,判断前后两部分的乘积是否等于原来的数字,等于则是吸血鬼数字,反之则不是。比如1827=21*87,因此其排列是2187的形式,2187是该数字的一个排列形式,我们取到21和87进行判断就可以断定了该数字是吸血鬼数字了。

下面代码就是用排列法进行的实现,具体过程如下:

  1. 判断该数字是否是100的整数倍,是则返回false;

  2. 判断数字的位数是否是偶数,否则返回false;

  3. 寻找所有可能的末尾数字组合,如果不存在两个数字相乘取模10后等于要判断的数字末尾或倒数第二位(末尾为0时)则返回false;

  4. 获取所有可能的排列,将排列从中间截断,并比较第一部分和第二部分末尾(或只有一部分的倒数第二位)是否满足乘积取模10后等于末尾数字或倒数第二位数字(末尾为0时),满足时如果两部分的乘积等于要判断的数字,则返回true,否则返回false。

3、代码实例

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;/*** @author jqs 吸血鬼数字识别算法* */
public class VampireNumber {/*** 组成吸血鬼数字的第一个数字*/private int sonNum1;/*** 组成吸血鬼数字的第二个数字*/private int sonNum2;private boolean isVampireNumber = false;private int value;public int getSonNum1() {return sonNum1;}public void setSonNum1(int sonNum1) {this.sonNum1 = sonNum1;}public int getSonNum2() {return sonNum2;}public void setSonNum2(int sonNum2) {this.sonNum2 = sonNum2;}public VampireNumber(int value) {this.value = value;initSonNum();}/*** 判断输入的数值是否是吸血鬼数字*/private void initSonNum() {// 以00结尾的不是if (value % 100 == 0) {setVampireNumber(false);return;}// 首先将数字分成n个int值,n代表value的数字长度String string = value + "";int length = string.length();if (length % 2 != 0) {// 非偶数位数一定不是setVampireNumber(false);return;}Integer[] rawNum = new Integer[length];for (int i = 0; i < rawNum.length; i++) {rawNum[i] = Integer.parseInt(string.charAt(i) + "");}// 记录该数字的最后一位不是0的值,以便于根据这个值来找可能的两个数字乘积取模10后等于该值boolean lastZero = rawNum[length - 1] == 0;int last = lastZero ? rawNum[length - 2] : rawNum[length - 1];// 构建两个list来存放吸血鬼数字可能的两个值的最后一位(或倒数第二位),因为吸血鬼数字的两个值的最后一位(或其中的倒数第二位)相乘必等于该值的最后一位,如果吸血鬼数字末尾是0的话,相乘必等于倒数第二位// 下面的两个list是成对出现的,且其index是一一对应的关系List<Integer> oneLastList = new ArrayList<Integer>();List<Integer> otherLastList = new ArrayList<Integer>();// 需要再检查一下任意两位相乘的情况for (int i = 0; i < rawNum.length; i++) {for (int j = i + 1; j < rawNum.length; j++) {if (rawNum[i] * rawNum[j] % 10 == last) { // 满足两个值相乘取模10等于last,则这两个值可能是组成吸血鬼数字的两个值中的最后一位或倒数第二位(最后一位是0的情况)oneLastList.add(rawNum[i]);otherLastList.add(rawNum[j]);}}}if (otherLastList.size() == 0) {// 找不到两个数相乘取模10等于最后一位或倒数第二位(最后一位是0的情况),直接返回,比如2220,肯定不是,2222肯定不是setVampireNumber(false);return;}fastMethod(length, rawNum, lastZero, oneLastList, otherLastList);}/*** 最快的判断数字是否是吸血鬼数字的方法* * @param length*            数字长度* @param rawNum*            原始数字每一位组成的数组* @param lastZeroFlag*            最后以为是否是0* @param oneLastList* @param otherLastList*/public void fastMethod(int length, Integer[] rawNum, boolean lastZeroFlag,List<Integer> oneLastList, List<Integer> otherLastList) {int size = oneLastList.size();if (lastZeroFlag) {for (int i = 0; i < size; i++) {otherLastList.add(oneLastList.get(i));oneLastList.add(otherLastList.get(i));}}int count = 0;int oneLast = 0;int otherLast = 0;int half = length / 2;// 对输入的数字进行全排列,得到所有的排列可能,并放入到这个list中。List<Object[]> posNumList = new ArrayList<Object[]>();permutation(rawNum, 0, length - 1, posNumList);while (count < oneLastList.size() && !isVampireNumber) {if (lastZeroFlag) {oneLast = 0;} else {oneLast = oneLastList.get(count);}otherLast = otherLastList.get(count);int two = 0;int one = 0;for (int m = 0; m < posNumList.size(); m++) {Integer[] list = (Integer[]) posNumList.get(m);two = 0;one = 0;// 将数组分为两半,每一半的末位为oneLast或otherLast时继续进行判断,否则continueif (list[length - 1] == oneLast && list[half - 1] == otherLast) {two += oneLast;for (int i = half; i < length - 1; i++) {int tmp = 1;for (int j = 0; j < length - 1 - i; j++) {tmp *= 10;}two += list[i] * tmp;}one += otherLast;for (int i = 0; i < half - 1; i++) {int tmp = 1;for (int j = 0; j < half - 1 - i; j++) {tmp *= 10;}one += list[i] * tmp;}} else if (list[length - 1] == otherLast&& list[half - 1] == oneLast) {two += otherLast;for (int i = half; i < length - 1; i++) {int tmp = 1;for (int j = 0; j < length - 1 - i; j++) {tmp *= 10;}two += list[i] * tmp;}one += oneLast;for (int i = 0; i < half - 1; i++) {int tmp = 1;for (int j = 0; j < half - 1 - i; j++) {tmp *= 10;}one += list[i] * tmp;}}if (two * one == value) {isVampireNumber = true;sonNum1 = one;sonNum2 = two;break;}}count++;}}/*** 耗时最短的全排列算法<br>* 从集合中依次选出每一个元素,作为排列的第一个元素,然后对剩余的元素进行全排列,如此递归处理,* 从而得到所有元素的全排列。以对字符串abc进行全排列为例,我们可以这么做:以abc为例:* 固定a,求后面bc的排列:abc,acb,求好后,a和b交换,得到bac* 固定b,求后面ac的排列:bac,bca,求好后,c放到第一位置,得到cba 固定c,求后面ba的排列:cba,cab。* * @param str*            原始元素列表* @param first*            排列的起始位置* @param end*            排列的结束位置* @param posNumList*            所有的排列结果*/public void permutation(Object[] str, int first, int end,List<Object[]> posNumList) {if (first == end) { // 输出一个排列方式Object[] result = Arrays.copyOf(str, str.length);posNumList.add(result);}for (int i = first; i <= end; i++) {swap(str, i, first);permutation(str, first + 1, end, posNumList); // 固定好当前一位,继续排列后面的swap(str, i, first);}}/*** 交换两个位置i和first的元素* * @param str* @param i* @param first*/private void swap(Object[] str, int i, int first) {if (str instanceof Integer[]) {Integer tmp;tmp = (Integer) str[first];str[first] = str[i];str[i] = tmp;} else if (str instanceof String[]) {String tmp;tmp = (String) str[first];str[first] = str[i];str[i] = tmp;}}public int[] getSonNum() {return new int[] { sonNum1, sonNum2 };}public boolean isVampireNumber() {return isVampireNumber;}public void setVampireNumber(boolean isVampireNumber) {this.isVampireNumber = isVampireNumber;}public static void main(String[] args) {VampireNumber vn = null;int count = 0;long l = System.currentTimeMillis();for (int i = 0; i < 10000; i++) {vn = new VampireNumber(i);if (vn.isVampireNumber()) {System.out.println(i + "=" + vn.getSonNum1() + "*"+ vn.getSonNum2());count++;}}System.out.println((System.currentTimeMillis() - l) + "共计:" + count);}
}

测试结果:

  • 10000以下的吸血鬼数字共7个,耗时133毫秒

  • 1000000以下的吸血鬼数字共147个,耗时29362毫秒

详细输出如下:

找到0-10000之间所有的吸血鬼数字:
1260=21*60
1395=15*93
1435=41*35
1530=51*30
1827=87*21
2187=27*81
6880=86*80
耗时:133毫秒,找到吸血鬼数字共计:7找到0-1000000之间所有的吸血鬼数字:
1260=21*60
1395=15*93
1435=41*35
1530=51*30
1827=87*21
2187=27*81
6880=86*80
102510=201*510
104260=401*260
105210=501*210
105264=516*204
105750=150*705
108135=135*801
110758=158*701
115672=152*761
116725=161*725
117067=167*701
118440=141*840
123354=231*534
124483=281*443
125248=152*824
125433=231*543
125460=246*510
126027=201*627
126846=261*486
129640=140*926
129775=179*725
131242=311*422
132430=323*410
133245=315*423
134725=317*425
135828=588*231
135837=351*387
136525=635*215
136948=146*938
140350=401*350
145314=414*351
146137=461*317
146952=156*942
152608=251*608
152685=585*261
153436=356*431
156240=651*240
156289=581*269
156915=165*951
162976=176*926
163944=396*414
172822=782*221
173250=750*231
174370=470*371
175329=759*231
180225=801*225
180297=897*201
182250=810*225
182650=281*650
186624=864*216
190260=906*210
192150=915*210
193257=327*591
193945=395*491
197725=719*275
201852=252*801
205785=255*807
211896=216*981
213466=341*626
215860=251*860
216733=671*323
217638=678*321
218488=248*881
226498=269*842
226872=276*822
229648=248*926
233896=338*692
241564=461*524
245182=422*581
251896=296*851
253750=350*725
254740=542*470
260338=323*806
262984=284*926
263074=602*437
284598=489*582
284760=420*678
286416=612*468
296320=926*320
304717=431*707
312475=431*725
312975=321*975
315594=534*591
319059=351*909
319536=336*951
326452=623*524
329346=342*963
329656=356*926
336550=635*530
336960=360*936
338296=392*863
341653=641*533
346968=366*948
361989=369*981
362992=392*926
365638=686*533
368550=630*585
369189=381*969
371893=383*971
378418=878*431
378450=870*435
384912=891*432
386415=831*465
392566=593*662
404968=446*908
414895=491*845
416650=641*650
416988=468*891
428980=482*890
429664=464*926
447916=476*941
456840=540*846
458640=546*840
475380=570*834
486720=624*780
489159=891*549
489955=899*545
498550=845*590
516879=681*759
529672=572*926
536539=563*953
538650=855*630
559188=588*951
567648=657*864
568750=650*875
629680=680*926
638950=650*983
673920=720*936
729688=788*926
736695=765*963
738468=876*843
769792=776*992
789525=825*957
792585=927*855
794088=984*807
809919=891*909
809964=894*906
815958=858*951
829696=896*926
841995=891*945
939658=953*986
耗时:29462毫秒,找到吸血鬼数字共计:147

这篇关于吸血鬼数字检验之java实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/911552

相关文章

springboot健康检查监控全过程

《springboot健康检查监控全过程》文章介绍了SpringBoot如何使用Actuator和Micrometer进行健康检查和监控,通过配置和自定义健康指示器,开发者可以实时监控应用组件的状态,... 目录1. 引言重要性2. 配置Spring Boot ActuatorSpring Boot Act

使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)

《使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)》在现代软件开发中,处理JSON数据是一项非常常见的任务,无论是从API接口获取数据,还是将数据存储为JSON格式,解析... 目录1. 背景介绍1.1 jsON简介1.2 实际案例2. 准备工作2.1 环境搭建2.1.1 添加

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五

java如何分布式锁实现和选型

《java如何分布式锁实现和选型》文章介绍了分布式锁的重要性以及在分布式系统中常见的问题和需求,它详细阐述了如何使用分布式锁来确保数据的一致性和系统的高可用性,文章还提供了基于数据库、Redis和Zo... 目录引言:分布式锁的重要性与分布式系统中的常见问题和需求分布式锁的重要性分布式系统中常见的问题和需求

SpringBoot基于MyBatis-Plus实现Lambda Query查询的示例代码

《SpringBoot基于MyBatis-Plus实现LambdaQuery查询的示例代码》MyBatis-Plus是MyBatis的增强工具,简化了数据库操作,并提高了开发效率,它提供了多种查询方... 目录引言基础环境配置依赖配置(Maven)application.yml 配置表结构设计demo_st

在Ubuntu上部署SpringBoot应用的操作步骤

《在Ubuntu上部署SpringBoot应用的操作步骤》随着云计算和容器化技术的普及,Linux服务器已成为部署Web应用程序的主流平台之一,Java作为一种跨平台的编程语言,具有广泛的应用场景,本... 目录一、部署准备二、安装 Java 环境1. 安装 JDK2. 验证 Java 安装三、安装 mys

Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单

《Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单》:本文主要介绍Springboot的ThreadPoolTaskScheduler线... 目录ThreadPoolTaskScheduler线程池实现15分钟不操作自动取消订单概要1,创建订单后

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

python使用watchdog实现文件资源监控

《python使用watchdog实现文件资源监控》watchdog支持跨平台文件资源监控,可以检测指定文件夹下文件及文件夹变动,下面我们来看看Python如何使用watchdog实现文件资源监控吧... python文件监控库watchdogs简介随着Python在各种应用领域中的广泛使用,其生态环境也

el-select下拉选择缓存的实现

《el-select下拉选择缓存的实现》本文主要介绍了在使用el-select实现下拉选择缓存时遇到的问题及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录项目场景:问题描述解决方案:项目场景:从左侧列表中选取字段填入右侧下拉多选框,用户可以对右侧