这段代码如何从任何基数阶乘法中找到尾随零的数量?

Ang*_*ber 9 c algorithm math number-theory

下面的代码完美无缺,但我希望有人向我解释它背后的数学.基本上,它是如何工作的?

#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;
}
Run Code Online (Sandbox Code Playgroud)

Nik*_* B. 12

请注意,问题相当于找到划分n的最高基数!.

如果基数是素数(让我们称之为p),我们可以使用数论中的一个定理来计算除以np的最高幂!:

在此输入图像描述

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

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;
}
Run Code Online (Sandbox Code Playgroud)

现在如果基地是主要力量会发生什么?假设我们有base = p q.然后,如果μp的最高幂,它除了n!我们有rfloor(μ/ q)

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

(P ^ Q)^(R + 1)= P ^(Q(R + 1))> = P ^(μ+ 1)并不能整除n!

所以r是n!中p ^ q的最大功率.我们也为此编写一个函数:

int maximum_power_of_pq_in_fac(int p, int q, int n) {
    return maximum_power_of_p_in_fac(p, n) / q;
}
Run Code Online (Sandbox Code Playgroud)

那么如果基数是一般数呢?让我们说吧

base = p 1 q 1 p 2 q 2 ... p m q m

(这是唯一的因式分解基地).然后我们只解决所有p i q i的问题,并采取最小的:

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;
}
Run Code Online (Sandbox Code Playgroud)

如何分解基数?好吧,我们可以使用试验分区,就像您的示例代码一样.我们首先检查2是否是一个主要因素.如果是,我们计算maximum_power_of_pq_in_fac和分基地 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
    }
}
Run Code Online (Sandbox Code Playgroud)

通过仔细检查代码,您会发现它包含所有上述元素,只在一个循环中放在一起,这有点令人困惑.

更新:如果您感兴趣,您提出的算法具有复杂度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
    }   
}
Run Code Online (Sandbox Code Playgroud)

当然,如果你想加快速度,你可以使用任何其他更复杂的素数因子分解算法.