我已经在编码平台中看到了此代码,可以有效地计算出不同值的欧拉糖度。 我无法理解此实现。我真的很想学这个。有人可以帮我解释一下吗?
for(int i = 1; i < Maxn; i++) { // phi[1....n] in n * log(n)
phi[i] += i;
for(int j = 2 * i; j < Maxn; j += i) {
phi[j] -= phi[i];
}
}
答案 0 :(得分:6)
首先,请注意,对于素数p
,phi(p) = p - 1
。这应该是非常直观的,因为所有小于质数的数字必须与该质数互质。因此,我们开始进入外部for循环:
for(int i = 1; i < Maxn; i++) { // phi[1....n] in n * log(n)
phi[i] += i;
在这里,我们将i
的值添加到phi(i)
中。对于素数情况,这意味着我们需要phi(i)
等于-1
,并且所有其他phi(i)
必须进行进一步调整以考虑辅素整数的数量。着眼于主要情况,让我们自己相信它们确实等于-1
。
如果我们遍历循环,在情况i=1
下,我们将对内部循环中的所有其他元素进行迭代,并减去1
。
for(int j = 2 * i; j < Maxn; j += i) {
phi[j] -= phi[i];
}
对于要减去的任何其他值,j
必须等于素数p
。但这需要j = 2 * i + i * k
等于p
,对于某些迭代k
。不能这样,因为2 * i + i * k == i * (2 + k)
暗示p
可以被i
平均除,而phi(p) = p - 1
不能(因为其素数)。因此,所有i
。
对于非素数i
,我们需要减去辅素整数的数量。我们在内部for循环中执行此操作。从前重用公式,如果j
除以j / i = (2 + k)
,我们得到i
。因此,每个小于(2 + k)
的值都可以乘以j
以小于(2 + k)
,但与j的公共因子为(i - 1)
(因此,不是互质的)。
但是,如果我们减去包含(2 + k)
个因子的i
倍数,我们将多次计算相同的因子。取而代之的是,我们仅计算与phi(i)
互质(即phi(x) = x - phi(factor_a) - phi(factor_b) ...
)的那些。因此,我们剩下的(2 + k_factor)
占所有小于上述因子的互质数的(2 + k_factor)
倍,而这些因子现在与x
共享因子for(int i = 1; i < Maxn; i++) { // phi[1....n] in n * log(n)
phi[i] += i;
for(int j = 2 * i; j < Maxn; j += i) {
phi[j] -= phi[i];
}
}
。
将其放入代码中可以使我们得到上面的确切信息:
text/css
答案 1 :(得分:1)
顺便说一句,出于兴趣,还有一种O(n)
算法可以实现相同目的。我们知道欧拉的产品配方为
phi(n) = n * product(
(p - 1) / p)
where p is a distinct prime that divide n
例如,
phi(18) = 18 * (
(2-1)/2 * (3-1)/3)
= 18 * 2/6
= 18 * 1/3
= 6
现在考虑一些素数m = n * p
的数字p
。
phi(n) = n * product(
(p' - 1) / p')
where p' is a distinct prime that divide n
如果p
除以n
,由于p
已经出现在phi(n)
的计算中,因此我们不需要将其添加到产品部分,而只需添加到初始乘数
phi(m) = phi(p * n) = p * n * product(
(p' - 1) / p')
= p * phi(n)
否则,如果p
不除n
,则需要使用新的质数,
phi(m) = phi(p * n) = p * n * product(
(p' - 1) / p') * (p - 1) / p
= p * (p - 1) / p * n * product(
(p' - 1) / p')
= (p - 1) * phi(n)
无论哪种方式,我们都可以仅从质数和数字自身的总和中计算出一个数字的总和与一个素数相乘,可以通过将到目前为止生成的数字重复乘以O(n)
来汇总我们找到下一个素数,直到达到Maxn
。我们通过增加一个索引来找到下一个素数,该后代我们没有记录待处理的目标素(此处生成素数是有好处的)。