【问题标题】:How does this code find the number of trailing zeros from any base number factorial?此代码如何从任何基数阶乘中找到尾随零的数量?
【发布时间】:2014-04-21 17:33:24
【问题描述】:

下面的代码完美运行,但我希望有人向我解释它背后的数学原理。基本上,它是如何工作的?

#include <stdio.h> 
#include <stdlib.h>  /* atoi */

#define min(x, y) (((x) < (y)) ? (x) : (y))

int main(int argc, char* argv[])
{
   const int base = 16;
   int n,i,j,p,c,noz,k;

   n = 7;  /* 7! = decimal 5040 or 0x13B0  - 1 trailing zero */  
   noz = n;
   j = base;
   /* Why do we start from 2 */
   for (i=2; i <= base; i++)
   {
      if (j % i == 0)
      {   
         p = 0;  /* What is p? */
         while (j % i== 0)
         {
            p++;
            j /= i;
         }
         c = 0;
         k = n;
         /* What is the maths behind this while loop? */
         while (k/i > 0)
         {
            c += k/i;
            k /= i;
         }
         noz = min(noz, c/p);
      }
   }
   printf("%d! has %d trailing zeros\n", n, noz);

   return 0;
}

【问题讨论】:

  • 为什么从base 2开始?因为j % 1 不会有余数。
  • @Marc B: 变量p 计算变量j 的值可被变量i 的值整除的次数,它只对变量的素数进行计算i(由于j /= i)。循环从 2 开始,因为这是最小的素因子。
  • @MarcB 这不是一个很好的解释,因为 1 实际上是一个除数,所以为什么不考虑它呢?原因是我们只想要素数

标签: c algorithm math number-theory


【解决方案1】:

注意,这个问题等价于求base除以n!的最高幂。

如果基数是素数(我们称之为p),我们可以使用theorem from number theory 来计算除以n 的p 的最高幂!

让我们将执行此操作的代码部分提取到一个函数中:

int maximum_power_of_p_in_fac(int p, int n) {
    int mu = 0;
    while (n/p > 0) {
        mu += n/p;
        n /= p;
    }
    return mu;
}

现在如果 base 是主要力量会发生什么?假设我们有 base = pq。那么如果 μp 的最高幂,它除以 n!r = floor(μ/q) ,我们有

(p^q)^r = p^(qr) 除 p^μ 除 n!

(p^q)^(r+1) = p^(q(r+1)) >= p^(μ+1) 除n!

所以 rp^q 在 n! 中的最大幂。让我们也为此编写一个函数:

int maximum_power_of_pq_in_fac(int p, int q, int n) {
    return maximum_power_of_p_in_fac(p, n) / q;
}

那么如果 base 是通用数呢?比方说

base = p1q1 p2q2 ... pmqm

(这是 base 的唯一素数分解)。然后我们只解决所有 piqi 的问题并取其中的最小值:

int maximum_power_of_base_in_fac(int base, int n) {
    int res = infinity;
    for every factor p^q in the prime factorization in base:
       res = min(res, maximum_power_of_pq_in_fac(p,q,n));
    return res;
}

如何分解base?好吧,我们可以像您的示例代码一样使用试用除法。我们首先检查 2 是否是主要因素。如果是,我们计算 maximum_power_of_pq_in_fac 并将 base 除以 2,直到它不再能被 2 整除。然后我们继续下一个候选因子:

void factorize(int base) {
    for (int p = 2; p <= base; ++p) {
        if (base % p == 0) { // if base is divisible by p, p is a prime factor
            int q = 0;
            while (base % p == 0) { // compute q and get rid of all the p factors
                q++;
                base /= p;
            }
            // do something with factor p^q
        }
        // proceed with next candidate divisor
    }
}

仔细检查代码,你会发现它包含了以上所有元素,只是放在一个循环中,有点混乱。

更新:如果您有兴趣,您提出的算法具有复杂性 O(base * log n)。您可以通过稍微调整素数分解例程轻松使其 O(sqrt(base) * log n)

void factorize(int base) {
    for (int p = 2; p*p <= base; ++p) { // only check prime factors up to sqrt(base)
        // ... same as before
    }
    if (base) {
        // there is exactly one prime factor > sqrt(base). 
        // It certainly has multiplicity 1.

        // process prime factor base^1
    }   
}

当然,如果您想进一步加快处理速度,您可以使用任何其他更复杂的素数分解算法。

【讨论】:

  • 我认为问题中的代码做了不必要的事情。我们只需要在数字的阶乘中找到 2 的最高幂和 5 的最高幂的最小值,即可找到阶乘中尾随零的数量。
  • @ajay 该代码适用于一般基础。当然,您所描述的正是该算法对base = 10 = 2^1 * 5^1 所做的
【解决方案2】:

基本上,它找到base 的质因数,其中i 是质数,ip 是基数,然后计算出n 中将存在多少i 因数!,将其除以 p,并在 base 的所有主要因子上跟踪该结果的最小数量。

所以回答代码中的问题:

  1. 为什么我们从 2 开始
    • 因为 2 是最小的素数
  2. 什么是p?
    • p 是基数 i 的主要因数的“幂”(所以 ip 是基数的因数)
  3. 这个循环背后的数学原理是什么
    • 循环正在计算(cin! 中的因子数

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-06
    • 1970-01-01
    • 2017-10-10
    相关资源
    最近更新 更多