求解最大质因数

最大质因数

顾名思义,对于一个自然数n,它的所有是质数的因数中,最大的那个就是n的最大质因数。本文讨论如何求解一个数的最大质因数。

算法思想

对于一个数n,我们可以用k = 2,3,4,5…这样的数来尝试整除它。对于每一个k,如果它可以整除n,就令n = n / k,一直到k不能再整除n为止。

按照上述方法,最终一定会有一个数k使得n / k = 1,这个k就是我们要求解的最大质因数。

要正确的使用这个算法,理解它的原理是非常重要的。

正确性证明

首先需要了解这一点:任何合数都可以拆分成若干个质数的积。

做一个简单的证明:对于一个自然数n,它可以写成它的最大质因数a和另一个数b的乘积。即:n = a * b,其中b可能为合数也可能为质数。

如果b为质数,那么由于a是最大质因数,所以一定有b < a(否则b是一个更大的质因数,与a是最大质因数矛盾)。

如果b为合数,那么对于b一定会有相似的拆分。即b可以拆分为它的最大质因数c与另一个数d的乘积。即:b = c * d,同理,d可以为质数也可以为合数。显然,有c < a,因为c为b的质因数,b是n的因数,所以c也是n的质因数,那么c自然小于n的最大质因数a。

那么对于d可以再分吗?答案是肯定的,并且一直分下去,我们最后要分的数一定可以写成两个质数的乘积。因为我们发现不能确定是合数还是质数的数,即b、d这一类数,如果他们是合数,那么一定是越分越小的。每一次拆分保证它会变小!但是合数终归是有下界的,所以最后那个不能确定是合数还是质数的数一定是个质数。

经过这样一个类似于递归的过程,我们最终将n分为了最大质因数与若干个质数相乘的形式。所以让k从2开始递增,从小的因数开始剔除,最终剩下的那个因数就是最大质因数。

你可能会有疑惑:k = 2,3,4,5…但是4并不是质数啊?没错,但是这并不妨碍什么。事实上,k为合数的时候一定不会被n整除(注意n在程序中是变化的)。因为k如果是合数,它一定会被它的质因数(比它小)筛去。

再回顾一下,用k剔除因数的过程中,n也是在改变的(n /= k)。最终得到的k满足条件n / k = 1。之后n = n / k = 1。

这时的k一定是个质数,因为它没有被它之前的任何一个数给分解掉。如果k是一个合数,那么它一定会被它之前的某个数分解掉。并且k也是质因数中最大的,因为k是递增的。

代码实现

// 求m的最大质因数
long long m = 600851475143;
int k = 2; //即算法中描述的k
while (m > 1){
    if (m % k == 0){
        m /= k;
    }
    else{
        ++k; // k是递增的
    }
}
//最终令m = 1的k就是最大质因数
cout << k << endl;

可以对代码进行改良。由于我们主要是用质数来筛除小的因数,让k以1为步长进行递增主要是为了省略求质数的工作。

容易发现所有的质数中只有2是偶数。所以我们可以对k = 2的情况进行单独处理,之后k = 3,以2为步长递增,即k += 2.

long long n = 600851475143;
// 对k = 2进行单独处理
while (n > 1)
{
    if (n % 2 == 0)
        n /= 2;
    else
        break;
}
// 之后k从3开始
int k = 3;
while (n > 1){
    if (n % k == 0){
        n /= k;
    }
    else{
        k += 2; // 步长改为2,增加效率
    }
}
cout << k << endl;

此外,根据如下事实还可以进行进一步的优化:

每个自然数最多只能有一个大于 n \sqrt{n} n 的质因数。

首先要明确,只要while循环没有满足n=1的条件,就说明仍然存在一个质因数未被我们检测出来,且这个质因数一定比已检测出来的质因数大。

在逐步筛除因数的过程中,可以维护k的一个上界: n \sqrt{n} n ,一旦k超出这个上界,结合大于/ s q r t ( n ) sqrt(n) sqrt(n)的质因数的唯一性,可以知道剩下的未检测出来的质因数一定只有一个!那么这个质因数就是答案。

令人兴奋的是,这个质因数就是n本身!可以简单证明一下:令该质因数为x,假设这个质因数x不是n,那么一定存在另一个数a与这个质因数x相乘等于n,即:a * x = n。由于x > n \sqrt{n} n ,可知a < x,那么a应该在x之前被筛除掉才是,所以这样的一个a事实上是不存在的。所以假设不成立,即x = n。

附上代码:

long long n = 600851475143;
while (n % 2 == 0)
{
    n /= 2;
}

int k = 3;
int maxFactor = sqrt(n);
while (n > 1 && k <= maxFactor)
{
    if (n % k == 0)
    {
        n /= k;
        maxFactor = sqrt(n);
    }
    else
    {
        k += 2;
    }
}

if (n > 1)
    k = n;
cout << k << endl;
  • 10
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值