我已经阅读了这个问题:Which is the fastest algorithm to find prime numbers?,但我只想对2
和5
素数这样做。
例如,数字42000
被分解为:
2 4 •3 1 • 5 3 •7 1
我只对在此示例中找到2
和5
:4
和3
的权力感兴趣。
我天真的方法是先后除以2
,其余为0
,然后相继除以5
,其余为0
。
成功分裂的数量(零余数)是2和5的幂。
这涉及执行(x + y + 2)
分区,其中x
是2的幂,y
是5的幂。
是否有更快的算法来查找2和5的幂?
答案 0 :(得分:1)
在谈话之后,我认为你的想法是最快的方式,但有一个例外:
分部(在大多数情况下)很贵。另一方面,检查数字的最后一位数(通常是?)会更快,所以我会在分割之前检查最后一位数字(0/5
和0/2/4/6/8
)。
答案 1 :(得分:1)
我的基础是OP:
我的库是用PHP编写的,数字实际上是作为字符串存储在基数10中的。这确实不是最有效的,但这是在语言的技术限制内最有效的。
如果你致力于php-in-php,那么与实际的通用重复模数和除法相比,下面的伪代码将加快速度:
while the string ends in 0, but is not 0
chop a zero off the end,
increment ctr2 and ctr5
switch repeatedly depending on the last digit:
if it is a 5,
divide it by 5
increment ctr5
if it is 2, 4, 6, 8,
divide it by 2
increment ctr2
otherwise
you have finished
这不需要任何模数运算,并且您可以比通用长数除法更便宜地实现5分频和2分频。
另一方面,如果你想要表现,使用无限大小整数的字符串表示就是自杀。使用 gmp (其中包含php library)进行数学运算,并仅在必要时转换为字符串。
修改强>
使用以下伪代码可以获得额外的效率(并简化操作):
if the string is zero, terminate early
while the last non-zero character of the string is a '5',
add the string to itself
decrement ctr2
count the '0's at the end of the string into a ctr0
chop off ctr0 zeros from the string
ctr2 += ctr0
ctr5 += ctr0
while the last digit is 2, 4, 6, 8
divide the string by 2
increment ctr2
一次砍掉多个0比循环更好。并且mul2在速度方面胜过div5(可以通过添加一次数来实现)。
答案 2 :(得分:0)
我认为你的算法将是最快的。但我有几点建议。
一种替代方案是基于最大公约数。取输入数字的gcd,最小功率2大于输入数字;这将给你所有的因素2.除以gcd,然后用5重复相同的操作;这将给你所有因素5.再次除以gcd,其余部分告诉你是否还有其他因素。
另一种选择是基于二分搜索。将输入数字的二进制表示分成两半;如果右半边是0,向左移动,否则向右移动。一旦你有2的因子,除法,然后使用5的幂来对剩余部分应用相同的算法。
我会留给您实施和计算这些算法的时间。但我的直觉是重复分裂将很难被击败。
我刚刚读到你的评论,你的输入数字存储在基数10中。在这种情况下,只要余数为0,重复除以10;给出2和5的因子。然后将算法应用于减少的数字。
答案 3 :(得分:0)
如果您有十亿位数字,除非确实需要,否则您不希望对其进行分割。如果你没有理由相信它在1 ^ 2 ^ 1000的数字中可以被2 ^ 1000整除,那么使用更快的测试只看最后几位是有意义的。您可以通过查看最后一位数字来判断一个数字是否可以被2整除,通过查看最后两位数字是否可以被4整除,通过查看最后n位数字可以看出2 ^ n。类似地,您可以通过查看最后一位数字来判断一个数字是否可被5整除,通过查看最后两位数字是否可以被25整除,通过查看最后n位数字可以将其除数为5 ^ n。
我建议您首先计算并删除尾随的0,然后从最后一位开始判断您是否正在测试2的幂(最后一个数字2,4,6或8)或5的幂(最后一个数字5) 。
如果您正在测试2的幂,那么取最后的2个,4个,8个,16个...... 2 ^ i个数字,并将其乘以25,625,... 5 ^ 2 ^ i,计数尾随0到2 ^ i(但不超过)。如果你得到少于2 ^ i尾随0,那么停止。
如果您正在测试5的幂,那么取最后的2个,4个,8个,16个...... 2 ^ i个数字,并将其乘以4,16,...... 2 ^ 2 ^ i,计数尾随0到2 ^ i(但不超过)。如果少于2 ^ i尾随0,则停止。
例如,假设您分析的数字是283,795,456。乘以56乘25,你得到1400,其中有2个尾随0,继续。乘以5,456乘625,得到3,410,000,其中有4个尾随0,继续。乘以83,795,456乘以5 ^ 8 = 390,625,得到32,732,600,000,000,其中有8个尾随0,继续。乘以283,795,456乘以5 ^ 16得到43,303,750,000,000,000,000,其中只有13个尾随0。那个小于16,所以停止,素数因子化中2的幂是2 ^ 13。
我希望对于更大的乘法,你实现了一个n log n算法来乘以n位数,但即使你不是,这种技术应该胜过任何涉及典型大数的除法。
假设每个n位数字具有相同的可能性,让我们看一下各种算法的平均时间复杂度。
两个n位数的加或减需要θ(n)步。
将n位数除以5之类的小数字需要theta(n)步。除以基数是O(1)。
将n位数除以另一个大数,使用FFT得到theta(n log n)步,或者通过朴素算法得到theta(n ^ 2)。乘法也是如此。
将基数10重复除以2的算法具有 theta(n)的平均病例时间复杂度:第一次除法需要theta(n)时间,平均而言,只需做O(1)分裂。
计算具有至少n位数的2的大功率通过重复平方获得theta(n log n),或通过简单乘法获得theta(n ^ 2)。执行Euclid算法来计算GCD需要采用θ(n)步的平均值。虽然划分采用theta(n log n)时间,但大多数步骤可以作为重复减法完成,并且只需要花费n(n)时间来完成这些步骤。以这种方式执行Euclid算法需要O(n ^ 2 log log n)。其他改进可能会将其降低到 theta(n ^ 2)。
在执行更昂贵的计算之前,将最后一位数字的可除性检查为2或5是好的,但这只会导致因子不断改进。在此之后应用原始算法仍然需要平均 theta(n)步骤。
通过2 ^ d或5 ^ d检查可分割性的最后d个数字需要O(d ^ 2)时间,O(d log d)和FFT。当d很小时,我们很可能只需要这样做。可被2 ^ d整除的n位数的分数是1/2 ^ d。因此,在这些检查上花费的平均时间是O(sum(d ^ 2/2 ^ d)),并且该和与n无关,因此平均需要 theta(1)时间。当您使用最后一位数字来检查可分性时,通常不必对接近n位的数字进行任何操作。