C语言 如何提高这段代码的效率,找到600851475143的最大素因子?

mec1mxoz  于 2023-04-05  发布在  其他
关注(0)|答案(5)|浏览(137)

我想让我的程序找到数字600851475143的最大素因子。例如,13195的素因子是5,7,13和29,29是最大的一个。虽然我的代码可以工作,但即使对于小得多的输入,如6kk,答案也需要很长时间才能出现(大约需要15秒。对于12kk,需要37秒,所以增量甚至比线性更糟糕),这比我应该用作输入的数字小100k倍。下面是我的代码,任何关于提高代码效率的帮助都将非常感谢。

#include <stdio.h>
#include <math.h>

int main()
{
    long long int number=600851475143;
    int largest_prime_factor,i,j,k;
    for (i=1;i<number/2;i+=2){
         k=0;
         j=3;
         for (j=3;j<=sqrt(i);j+=2){
            if (i%j==0){
                k++;
                break;
              }
                }
        if (k==0){
            if (number%i==0)
                largest_prime_factor=i;
        }
    }
    printf("The largest prime factor of 600851475143 is: %d",  
    largest_prime_factor);
    return 0;
}
hmtdttj4

hmtdttj41#

你应该把sqrt(i)移出for循环。它在每个循环中都计算。而且j*j <= ij <= sqrt(i)快得多。
您的代码中有错误:如果number是long long int,其他变量也应该是,否则条件i<number/2总是为真!

lsmd5eda

lsmd5eda2#

这段代码应该相当快,对于数字600851475143只需要几毫秒:

long long int primes[1000];
int primesSize = 0;
long long int primeFactors[100];
int primeFactorsSize = 0;

long long int number = 600851475143ll;

for (long long int f = 2; f < number / 2; ++f)
{
    // Check if f is a prime number
    int primesIndex = 0;
    while (primesIndex < primesSize && (f%primes[primesIndex]) != 0)
        ++primesIndex;

    if (primesIndex >= primesSize)
    {
        primes[primesSize++] = f;

        // Check if f is a prime factor of number
        while ((number % f) == 0)
        {
            primeFactors[primeFactorsSize++] = f;
            number /= f;
        }
    }
}

if (number != 1)
    primeFactors[primeFactorsSize++] = number;

创建一个已经找到的素数列表可以加快对另一个可能因素的检查。
如果你找到了一个素数因子,你可以用这个素数因子除以你的数字,然后继续除法。也许这个除法需要多次进行。number中的最终值也是最大的素数因子。
警告:我的代码根本没有经过测试。我只是确保结果对于number = 600851475143ll是正确的。而且我使用的是C++编译器,所以你可能需要做一些小的修改。
对于更大的number,至少需要为primes数组实现动态内存分配:

#include <stdlib.h>
#include <stdio.h>

int main()
{
    long long int *primes = NULL;
    int primesSize = 0;
    int primesCapacity = 0;
    long long int *primeFactors = NULL;
    int primeFactorsSize = 0;
    int primeFactorsCapacity = 0;

    long long int number = 600851475143ll;
    number = 13456769ll;

    for (long long int f = 2; f < number / 2; ++f)
    {
        // Check if f is a prime number
        int primesIndex = 0;
        while (primesIndex < primesSize && (f%primes[primesIndex]) != 0)
            ++primesIndex;

        if (primesIndex >= primesSize)
        {
            if (primesSize == primesCapacity)
            {
                primesCapacity += 1000;
                primes = (long long int*)realloc(primes, primesCapacity * sizeof(long long int));
            }
            primes[primesSize++] = f;

            // Check if f is a prime factor of number
            while ((number % f) == 0)
            {
                if (primeFactorsSize == primeFactorsCapacity)
                {
                    primeFactorsCapacity += 1000;
                    primeFactors = (long long int*)realloc(primeFactors, primeFactorsCapacity * sizeof(long long int));
                }
                primeFactors[primeFactorsSize++] = f;
                number /= f;
            }
        }
    }

    if (number != 1)
    {
        if (primeFactorsSize == primeFactorsCapacity)
        {
            primeFactorsCapacity += 1000;
            primeFactors = (long long int*)realloc(primeFactors, primeFactorsCapacity * sizeof(long long int));
        }

        primeFactors[primeFactorsSize++] = number;
    }

    printf("Last prime factor is %lld", primeFactors[primeFactorsSize-1]);

    return 0;
}
c3frrgcw

c3frrgcw3#

使用j*j〈=i而不是j〈=sqrt(i),这取决于我使用的数字的大小,使代码快了5到10倍。而不是k++;也有轻微的影响。大约0.5%。我还没有检查所有内容。谢谢大家的宝贵意见!感谢他们,有很多东西值得思考和学习!

wqlqzqxt

wqlqzqxt4#

你不需要遍历整个列表,一旦你找到一个素因子,你就用它分解你的数字,然后继续处理剩下的。
举个例子:600,851,475,143。你可以很快找到它的第一个素因子是71。如果你把600,851,475,143除以71,你会得到8,462,696,833。除了71之外,这两个数字都有相同的素因子。所以现在你可以搜索原始数字的最大因子,但搜索空间减少了2个数量级。
另外,请注意,如果数字本身是质数,代码将失败。

int largest_prime_factor = 1;

如果最后仍然是1,则返回数字本身。(您可以使用number进行初始化,但您很快就会明白我为什么选择1)
因此,首先将2视为特殊情况:

long long remain = number;
    while (remain % 2 == 0) {
            remain /= 2;
            largest_prime_factor = 2;
    }

然后在你的循环中做同样的事情,因为对于素数我们只需要检查它的平方根,我们将把我们的循环限制在两种情况下,这取决于我们是否仍然认为这个数可能是素数。
1.仍然是总理:测试到sqrt(数量)
1.不再Prime:测试直到最大因子超过剩余因子。
最后,修改后的代码可能看起来像这样:

#include <stdio.h>

int main()
{   
    long long int number=600851475143;
    long long largest_prime_factor = 1,i,j,k;
    long long remain = number;

    while (remain % 2 == 0) {
        remain /= 2;
        largest_prime_factor = 2;
        /* Uncomment to see the factors 
           printf("2 ");*/
    }   

    for (i=3; (largest_prime_factor == 1 && i*i <= number) ||  
            (largest_prime_factor > 1&& i <= remain); i+=2){
        k=0;
        j=3;
        for (j=3; j*j<=i;j+=2){
            if (i%j==0){
                k++;        
                break;   
            }        
        }
        if (k==0 && remain%i==0) {
            largest_prime_factor=i;
            while (remain % i == 0) {
                /* Uncomment to see the factors 
                printf("%d ", i); */
                remain /= i;  
            }            
        }
    }
    printf("The largest prime factor of %Ld is: %Ld",
            number, largest_prime_factor);
    return 0;
}

还要注意,其他变量也应该是long long类型。
瓶颈是检查每个数是否是质数,如果质数因子本身很大,整个过程仍然会很慢。但是你可以得到一个更快的平均情况。对于你的例子,这个算法在不到一秒的时间内得到因子71,839,1471和6857。

up9lanfz

up9lanfz5#

一般来说,没有有效的方法可以做到这一点:https://en.wikipedia.org/wiki/Integer_factorization#Difficulty_and_complexity
不过,还是有一些方法可以提高代码的速度。看看那篇文章中的一些算法吧。

相关问题