我一直在做一些Project Euler问题来学习/练习Lua,而我最初的快速和肮脏的方法找到n
的最大素数因素非常糟糕,所以我查了一些代码,以了解其他人如何做到这一点(试图了解不同的保理方法)。
我遇到了以下内容(最初是在Python中 - 这是我的Lua):
function Main()
local n = 102
local i = 2
while i^2 < n do
while n%i==0 do n = n / i end
i = i+1
end
print(n)
end
这在非常的短时间内 - 几乎立即就会产生巨大的数字。关于算法我注意到的事情我不会理解:
n = n / i
这似乎是所有体面的算法。我已经用较少的数字在纸上进行了研究,我可以看到它使数字收敛,但我不明白为什么这个操作会收敛于最大的素数因子。
任何人都可以解释一下吗?
答案 0 :(得分:3)
在这种情况下,i
是候选的主要因素。考虑一下,n
由以下素数组成:
n = p1^n1 * p2^n2 * p3^n3
当i
到达p1
时,语句n = n / i = n / p1
会删除一次p1
:
n / p1 = p1^(n-1) * p2^n2 * p3^n3
只要while
中有p1
s,内部n
就会重复。因此,在迭代完成后(执行i = i + 1
时),所有出现的p1
都已被删除,并且:
n' = p2^n2 * p3^n3
让我们跳过一些迭代,直到i
到达p3
。剩下的n
则为:
n'' = p3^n3
在这里,我们发现代码中的第一个错误。如果n3
为2,则外部条件不成立,我们将继续p3^2
。它应该是while i^2 <= n
。
与以前一样,内部while
删除p3
的所有出现,留下n'''=1
。这是第二个错误。它应该是while n%i==0 and n>i
(不确定LUA语法),它保持最后一次出现。
因此,上述代码适用于所有数字n
,其中最大的素数因子仅通过成功删除所有其他因子而发生一次。对于所有其他数字,所提到的更正也应该有效。
答案 1 :(得分:0)
这消除了n
之外的所有已知较小素因子,使n
变小,并且可以提前达到sqrt(n)
。这样可以提高性能,因为您不再需要将数字运行到原始N的平方根,例如n
是一百万,它包含2和5,以及天真的查询针对所有已知素数需要检查高达1000的所有素数,而将其除以2得到15625,然后除以5得到1(顺便说一句,你的算法将返回1!要修复,如果你的循环以n = 1退出,则返回i
。)通过两个步骤有效地分解大数。但是只有&#34;普通&#34;数字,具有单个高主要分母和一堆较小的分母,但将n=p*q
和p
的数字q
分解为素数并且很接近即可从这种提升中受益。
n=n/i
行是有效的,因为如果你正在寻找另一个素数而不是i
你当前被认为是一个除数,那么结果也可以被素数整除,也可以按素数的定义整除。请在此处阅读:https://en.wikipedia.org/wiki/Fundamental_theorem_of_arithmetic。此外,这仅适用于您的情况,因为您的i
从2向上运行,因此您首先按素数除以它们的复合。否则,如果你的号码有3个最大素数,也可以被2整除,你首先检查6,你就破坏了只用素数除以的原则(如果你是第一个,那就是72)除以6,你最终得到2,而答案是3)意外地除以最大素数的复合。
答案 2 :(得分:0)
该算法(校正后)采用O(max(p2,sqrt(p1)))步骤来找到n的素因子分解,其中p1是最大素因子,p2是第二大素因子。在重复的最大素因子的情况下,p1 = p2。
Knuth和Trabb Pardo研究了这个函数的行为"Analysis of a Simple Factorization Algorithm"理论计算机科学3(1976)321-348。他们反对通常的分析,例如计算将整数分解为n时所采取的平均步数。虽然少数具有较大素因子的数字会提高平均值,但在加密环境中,可能更相关的是某些百分位数非常低。例如,44.7%的数字满足max(sqrt(p1),p2)<n^(1/3)
,1.2%的数字满足max(sqrt(p1),p2)<n^(1/5)
。
一个简单的改进是在找到新的素因子后测试剩余的素数。测试数字是否为素数非常快。通过避免p2和sqrt(p1)之间的试验划分,这减少了到O(p2)的时间。第二大素数的中值大小约为n ^ 0.21。这意味着使用试验部门的这种改进可以快速(在几个处理器 - 秒内)对许多45位数字进行分解是可行的。相比之下,根据一个模型,对两个素数乘积的Pollard-rho因子分解平均需要O(sqrt(p2))步。