线性时间Euler的Totient函数计算

时间:2015-12-14 05:34:49

标签: c++ sieve-of-eratosthenes

在浏览了一下之后,我发现这个代码用于使用Sieve of Eratostenes计算线性时间内的Euler的phi值。但是没有理解这个代码中使用的主要逻辑,特别是在内部for循环中做了什么以及在这个循环用于计算phi值。如果有人帮助我理解这段代码,那将会很有帮助。

do{
    let attrString =
    try NSMutableAttributedString(URL: rtfPath, options: [NSDocumentTypeDocumentAttribute:NSRTFTextDocumentType], documentAttributes: nil)
                textView.attributedText = attrString 
}catch{}

1 个答案:

答案 0 :(得分:5)

编码既不在这里也不在那里,但算法本身就是辉煌。但它与Eratosthenes的筛子几乎没有关系。这种方法有点让人联想到Sieve of Sundaram,因为它系统地产生多个质数以标记复合材料;它比Sundaram更好,因为每个复合材料只被划掉一次(no overdraw)。同样,每个phi值都会被计算并分配一次。

如果首先更改代码,则更容易理解代码:

enum {  N = 3000000  };
vector<unsigned> primes;
vector<unsigned> phi(N + 1);        // indexed directly with numbers, hence the +1
vector<bool> is_composite(N + 1);   // indexed directly with numbers, hence the +1

phi[1] = 1;   // phi[0] is 0 already; phi[1] needs to be set explicitly

for (unsigned i = 2; i <= N; ++i)
{
   if (not is_composite[i])  // it's a prime
   {
      phi[i] = i - 1;        // a prime is coprime to all numbers before it
      primes.push_back(i);
   }

   for (auto current_prime: primes)
   {
      unsigned ith_multiple = i * current_prime;

      if (ith_multiple > N)
         break;

      is_composite[ith_multiple] = true;

      if (i % current_prime)  // i and current_prime are coprime -> phi is multiplicative in this case
      {
         phi[ith_multiple] = phi[i] * (current_prime - 1);  // (current_prime - 1) == phi[current_prime]
      }
      else
      {
         phi[ith_multiple] = phi[i] * current_prime;  // based on phi(p^(k+1)) / phi(p^k) == p

         break;
      }
   }
}

phi(k)值的计算必须考虑三种不同的情况:

(1)k是素数:phi(k)= k - 1,这是微不足道的

(2)k = m * p,m和p互质和p prime:phi(k)= phi(m * p)= phi(m)* phi(p)= phi(m)*(p - 1)

(3)k = m * p,m = m&#39; * p ^ n和m&#39;和p互质和p素数:phi(k)= phi(m)* p

这两个相关的数学事实列在Euler's product formula的维基百科文章的Euler's totient function下。情况1在外循环中处理,情况2是内循环中条件的then分支,情况3是else分支,它也终止内循环。情况2和3为较小数字的预先存在的phi值构建复合材料的phi值,所有这些都最终来自素数的phi值(在外环中设置)。

算法的亮点在于它安排要完成的工作的方式,这样每个值只计算一次并且在需要时已经计算过。它为复合材料实现的递归基于通过分解其最小的素因子来有效地分割每个复合:m = m&#39; * p。这有利于根据上述情况2和3计算复合材料的phi,并且它导致用于生产没有重复的复合材料的简单规则。除此之外,外环呈现所有可能的m&#39;到内循环(虽然对于i> N / 2,内循环从不接受任何外循环仅旋转以收集剩余的素数)。然后内环产生所有复合物,它们是m&#39;的产物。并且是不超过其自身主要因素的最小因素。

代码已针对从list of the first 100,000 phi values检索到的OEIS page for the phi function进行了验证。它是明确地写出来展示算法的工作原理;在用于程序之前,必须对其进行审查,调整和强化(特别是防止溢出)。

附录 - 如果有人无意中跳过DanaJ的评论:is_composite[]数组是多余的,因为给定i的非复合性(素性)可以通过在外部循环中测试phi[i]为零来确定。原因是复合材料的phi值向上传播 - 当i是其中一个因素时,它们会在早期迭代中计算出来。另一种推理方法是is_composite[m]只有在计算和存储相应的phi值时才会设置为true,并且这些值永远不会为零。因此,外环中的测试变为if (phi[i] == 0)。而实施者(而不仅仅是&#39;鉴赏家)可能想更仔细地看待DanaJ的评论...