阶乘的质因数分解

6

我需要编写一个程序,输入一个数字并输出它的阶乘的质因数分解结果,格式如下:

4!=(2^3)*(3^1)

5!=(2^3)*(3^1)*(5^1)

问题是我仍然无法弄清如何得到那个结果。
显然,括号中的每个第一个数字对应于升序质数直到实际阶乘。括号中的第二个数字是该数字在阶乘中出现的次数。
我不能理解的是,在5!=120(5!=120)中,例如在5!=(2^3)*(3^1)*(5^1)中,如何只使2出现3次,3只出现1次,5只出现1次。
我现在已经通过评论帮助我的好心人解决了这个问题,但我现在遇到的问题是如何在不实际计算阶乘的情况下以这种方式获取阶乘。

你知道 2*2*2 * 3 * 5 等于 120,对吧?你对此有什么困惑吗? - Keith Thompson
我刚刚不知道那些数字是从哪里来的,现在我已经弄清楚了。 - Bradg89
1
看看我在底部的答案。它解释了如何在不实际计算阶乘的情况下获得阶乘的质因数分解。基本上,您要因式分解组成阶乘的所有数字,并添加相同基数的指数。 - user2105505
5个回答

12

每个数字都可以用质数的唯一(顺序可变)乘积表示,称为该数字的质因数分解,你正在寻找能唯一创建该数字的质因数。

2^3=8

3^1=3

5^1=5

8*3*5=120

但这也意味着:(2^3)*(3^1)*(5^1) = 120

它并不是说数字120中有3个2,显然并没有,而是将2乘以2再乘以2,共计3个2。对于3和5也是如此,它们在数字120的质因数分解中只出现一次。你提到的表达式向你展示了该数字的唯一质因数分解。这是在Python中获取数字质因数分解的一种方式:

def pf(number):
    factors=[]
    d=2
    while(number>1):
        while(number%d==0):
            factors.append(d)
            number=number/d
        d+=1
    return factors

运行它,您将得到:

>>> pf(120)
[2, 2, 2, 3, 5]

如上所述,它们相乘可以得到120。以下是一个简单的图示:

enter image description here


谢谢,质因数位给了我提示。 - Bradg89
我明白,尽管我不熟悉Python。我的下一个问题是,如何在不实际计算阶乘的情况下以这种格式获取一个数字的阶乘。 - Bradg89
@Bradg89,你可以使用阶乘的定义。它是一堆因子(组件)的乘积。每个因子(组件)都可以分解成质因数。因此,因子的乘积可以用组件的发现的质因数的乘积来重写——这样就可以回答你的问题,而无需计算阶乘。 - user268396

11

考虑例如 33!。 它是以下数值的积:

2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

这些因素包括:

2   2   2   2    2     2     2     2     2     2     2     2     2     2     2     2
    2       2          2           2           2           2           2           2
            2                      2                       2                       2
                                   2                                               2
                                                                                   2
  3     3     3        3        3        3        3        3        3        3        3
              3                          3                          3
                                                                    3
      5          5              5              5              5              5
                                                              5
          7                  7                    7                    7
                   11                               11                               11
                         13                                     13
                                     17
                                           19
                                                       23
                                                                         29    31

你看到这个模式了吗?

33! = 2^( 33 div 2 + 33 div 4 + 33 div 8 + 33 div 16 + 33 div 32) *
      3^( 33 div 3 + 33 div 9 + 33 div 27) *
      5^( 33 div 5 + 33 div 25) *
      ----
      7^( 33 div 7) * 11^( 33 div 11) * 13^( 33 div 13) *
      ----
      17 * 19 * 23 * 29 * 31

因此,要找到n!的质因数分解,而不进行任何乘法或因式分解,我们只需要有不大于n的素数的有序列表,我们将其分为三个阶段处理(通过重复整数除法和可能的求和)-小于等于n平方根的质数,小于等于n/2的质数以及剩余的质数。

实际上,使用惰性求值甚至比这更简单。假设primes在Haskell中已经实现并返回按顺序排列的质数流,阶乘分解可以找到:

ff n = [(p, sum . takeWhile (> 0) . tail . iterate (`div` p) $ n) 
         | p <- takeWhile (<= n) primes]

-- Prelude> ff 33
-- [(2,31),(3,15),(5,7),(7,4),(11,3),(13,2),(17,1),(19,1),(23,1),(29,1),(31,1)]

因为33 div 4等于(33 div 2) div 2,以此类推。


4
2^3是另一种写法,意思是2的3次方。(2^3)(3^1)(5^1)= 2的3次方 × 3 × 5 = 120,这就是120的质因数分解式用普通ASCII文本表达而不是数学格式化。之所以要求输出的格式如此简单,只是为了让您更容易地输出,而不是为了让您弄懂如何输出格式化的等式(还可能更容易得到评分)。在此处使用的文本表示方程的约定是标准的,您可以直接将此文本输入google.com或wolframalpha.com中,它将为您计算结果为120: 在wolframalpha.com上的(2^3)(3^1)(5^1) / 在google.com上的(2^3)(3^1)(5^1)
WolframAlpha也可以计算质因数分解,您可以使用它来获取测试结果,以与您的程序进行比较。例如: 1000!的质因数分解 一个天真的解决方案实际上会计算处理数字并且只能处理不大于12(如果使用32位int)的数字。这是因为13!是62亿,大于可以用32位int表示的最大数字。
然而,如果避免先计算出阶乘,就可以处理更大的输入。我不会告诉你确切的做法,因为弄清楚它要么是您的任务的一部分,要么您可以向您的教授/助教询问。但以下是一些提示。

ab × ac = ab+c


公式(a)      10 = 21 × 5 1
公式(b)      15 = 31 × 51
10 × 15 = ?      使用公式(a)和公式(b)的右侧来回答,而不使用数字150。

10 × 15 = (21 × 51) × (31 × 51) = 21 × 31 × (51 × 51) = 21 × 31 × 52 通过上面的计算,我们可以看出,计算10×15的质因数分解并不需要将10乘以15;相反,你可以先计算每个数字的质因数分解,然后再将这些分解组合起来。

谢谢你的正确推测。在查看之前,我会先自己尝试一下。如果我依赖别人来做困难的事情,就无法找到工作。 - Bradg89

3
如果你写出5!的阶乘:
1 * 2 * 3 * 4 * 5,
你会发现有一个非质数:4。4可以写成2 * 2或2^2,这就是额外的2的来源。 把所有出现的数加起来(指数形式用括号表示;对于相同的底数,将指数相加):
2 (2^1) * 3 (3^1) * 4 (2^2) * 5 (5^1),你就得到了正确的答案。

读者应该注意,在这里,“2(2 ^ 1)* 3(3 ^ 1)* 4(2 ^ 2)* 5(5 ^ 1)”并不意味着“2 × 2¹ × 3 × 3¹ × 4 × 2² × 5 × 5¹”,(这将计算出120²而不是120)。在此上下文中,括号中的文本仅展开阶乘的相应项,而不是作为项本身。 - bames53

1
你可以使用仅使用求和(无需预处理质数)的 O(n/2 log log n) 算法。
这是一种使用关系的筛法。
f = a * b  ~>  f^k = a^k * b^k

然后,我们将所有初始因子1 * 2 * 3 * ... * n从大数移动到小数进行缩减。使用Atkin筛法,Will Ness算法可用于非常大的n,否则我认为它可能会更好。
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
  int n = atoi(argv[1]);
  int *p = (int *) malloc(sizeof(int) * (n + 1));
  int i, j, d;
  for(i = 0; i <= n; i++)
    p[i] = 1;
  for(i = n >> 1; i > 1; i--)
    if(p[i]) {
      for(j = i + i, d = 2; j <= n; j += i, d++) {
        if(p[j]) {
          p[i] += p[j];
          p[d] += p[j];
          p[j] = 0;
        }
      }
    }
  printf("1");
  for(i = 2; i <= n; i++)
    if(p[i])
      printf(" * %i^%i", i, p[i]);
  printf("\n");
  return 0;
}

1
据报道,Atkin筛法实现起来非常难以高效地完成。 - Will Ness
很好的观点@WillNess,因此我认为你的方法对于非常(非常)大的数字更好:)另一方面,我想知道是否可以将Atkin用作筛子来获得更好的算法。不错的问题! - josejuan
1
我认为对于非常大的数字N,输出将包含太多条目 - 约为N /(log N-1) 条目(与N以下的质数一样多)。 对于10^9,它有50,701,542个条目(实际上是50,847,534)。 我想我们不需要做那么多。 :) 因此,我认为任何正常的SoE实现都可以,无论是预先计算还是作为算法的一部分。 :) - Will Ness
你说服了我 :) XD XD - josejuan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接