Algorithm 检查号码是否为吸血鬼号码的最快方法?
这里定义了一个吸血鬼号码。数字V是吸血鬼数字,如果:Algorithm 检查号码是否为吸血鬼号码的最快方法?,algorithm,numbers,number-theory,Algorithm,Numbers,Number Theory,这里定义了一个吸血鬼号码。数字V是吸血鬼数字,如果: 它可以表示为X*Y,这样X和Y各有N/2位,其中N是V中的位数 X和Y都不应具有尾随零 X和Y的数字应与V的数字相同 我想出了一个解决办法 strV = sort(toString(V)) for factor <- pow(10, N/2) to sqrt(V) if factor divides V X <- factor Y <- V/factor if X
- 它可以表示为X*Y,这样X和Y各有N/2位,其中N是V中的位数
- X和Y都不应具有尾随零
- X和Y的数字应与V的数字相同
strV = sort(toString(V))
for factor <- pow(10, N/2) to sqrt(V)
if factor divides V
X <- factor
Y <- V/factor
if X and Y have trailing zeros
continue
checkStr = sort(toString(X) + toString(Y))
if checkStr equals strV return true
strV=sort(toString(V))
对于因子,以下是一些建议:
首先是一个简单的改进:如果位数小于4或奇数,则返回false(或者如果v也是负值)
您不需要排序v
,只需计算每个数字出现O(n)的次数即可
您不必检查每个数字,只需检查数字的可能组合。这可以通过回溯来实现,并显著减少必须检查的数字量
检查是否使用了所有数字的最终排序也不需要,只需将两个数字使用的数字相加,并与v
中出现的数字进行比较即可
下面是一个类似JS的语言的代码,它的整数永远不会溢出,V
参数是一个不带前导0的整数字符串:
编辑:事实证明,该代码不仅类似于JS,而且是有效的JS代码,因此可以确定1047527295416280确实是一个吸血鬼号码()
var V,V,isVmp,数字,len;
函数是吸血鬼(numberString){
V=数字串;
如果(V.length<4 | | V.length%2==1)
返回false;
v=parseInt(v);
if(v<0)
返回false;
位数=计数位数(V);
len=V.长度/2;
isVmp=false;
支票号码();
返回isVmp;
}
函数计数位数{
var offset=“0”。charCodeAt(0);
var-ret=[0,0,0,0,0,0,0,0,0];
对于(变量i=0;i0){
数字[i]——;
支票号码(i,len-1);
数字[i]++;
}
}
}否则如果(深度==0){
如果(v%number==0){
var b=v/数量;
如果(数字%10!=0 | | b%10!=0){
变量d=计数位数(“”+b);
如果(d[0]==数字[0]&&d[1]==数字[1]&&d[2]==数字[2]&&
d[3]==数字[3]&&d[4]==数字[4]&&d[5]==数字[5]&&
d[6]==数字[6]&&d[7]==数字[7]&&d[8]==数字[8]&&
d[9]==数字[9])
isVmp=true;
}
}
}否则{
对于(变量i=0;i<10;i++){
如果(数字[i]>0){
数字[i]——;
检查编号(编号*10+i,深度-1);
数字[i]++;
}
}
}
}
在伪代码中:
if digitcount is odd return false
if digitcount is 2 return false
for A = each permutation of length digitcount/2 selected from all the digits,
for B = each permutation of the remaining digits,
if either A or B starts with a zero, continue
if both A and B end in a zero, continue
if A*B == the number, return true
这里仍然可以执行许多优化,主要是确保每对可能的因素只尝试一次。换句话说,在选择排列时,如何最好地检查重复的数字
但这就是我将使用的算法的要点
附言:你不是在寻找素数,为什么要使用素数测试呢?你只关心这些是否是吸血鬼的数字;只有极少数可能的因素。无需检查sqrt(number)之前的所有数字。我在这里提出的算法不会检查所有的数字排列。它将尽可能快地消除可能性,这样实际上只会测试一小部分置换
算法举例说明
下面是基于示例125460的工作原理。如果您可以直接阅读代码,则可以跳过此(长)部分:
起初,这两种毒牙(即吸血鬼因素)显然不为人所知,问题可以表示为:
?**
X ?**
-------
=125460
对于第一个因子的最左边的数字(标有?
),我们可以选择数字0、1、2、5、4或6中的任何一个。但仔细分析,0是不可行的,因为产品的数字永远不会超过5位数。因此,对所有以零开头的数字进行排列是浪费时间的
对于第二个因子的最左边的数字(也标有?
),也是如此。但是,在查看组合时,我们可以再次筛选出一些不能帮助达到目标产品的对。例如,应放弃此组合:
1**
X 2**
-------
=125460
使用这些数字可以获得的最大数字是199x299=59501(忽略我们甚至没有9的事实),这甚至不是所需数字的一半。因此,我们应该拒绝组合(1,2)。出于同样的原因,该对(1,5)可因采取这些位置而丢弃。类似地,对(4,5)、(4,6)和(5,6)也可以被拒绝,因为它们产生的乘积太大(>=200000)。我将把这种测试称为“范围测试”——确定目标数字是否在某个选定数字对的范围内
在这一阶段,第一个和第二个方之间没有区别,因此我们也不必调查第二个数字小于第一个数字的对,因为它们反映了一个已经被调查(或拒绝)的对
因此,在所有可能占据第一个位置的对中(有30种可能从一组6位数字中选取2位数字),只需调查以下4位:
(1, 6), (2, 4), (2, 5), (2, 6)
在更详细的表示法中,这意味着我们将搜索限制在以下数字模式:
1** 2** 2** 2**
X 6** X 4** X 5** X 6**
------- ------- ------- -------
=125460 =125460 =125460 =125460
A B C D
很明显,在查看其他位置之前,这种可能性的减少大大减少了搜索树
该算法将依次考虑这4种可能性,并为每种可能性检查下一个数字位置的可能性。因此,首先分析了配置A:
1?*
X 6?*
-------
=125460
适用于 1?*
X 6?*
-------
=125460
(0, 2), (0, 4), (0, 5)
(2, 0), (2, 4), (2, 5)
(4, 0), (4, 2), (4, 5)
(5, 0), (5, 2), (5, 4)
2?*
X 4?*
-------
=125460
2?*
X 5?*
-------
=125460
24* 24* 26* 26*
X 50* X 51* X 50* X 51*
------- ------- ------- -------
=125460 =125460 =125460 =125460
Ca Cb Cc Cd
246
X 510
-------
=125460
*-+-- (1, 6)
|
+-- (2, 4)
|
+-- (2, 5) -+-- (4, 0)
| |
| +-- (4, 1) ---- (6, 0) = success: 246 * 510
/ /
| +-- (6, 0)
| |
| +-- (6, 1)
|
+-- (2, 6) ---- (0, 1) ---- (4, 5) = success: 204 * 615