关于c#:什么是找到数字最大素数因子的最佳方法?

Whats the best way to find the largest prime factor of a number?

我对编程还不熟悉,我的一个朋友建议我应该在ProjectEuler上做一些练习,以便更好地使用它。我在问题3中遇到了一个问题:

"The prime factors of 13195 are 5, 7, 13 and 29. What is the largest prime factor of the number 600851475143 ?"

下面是我的解决方案:

1
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
34
class Program
{
    static void Main(string[] args)
    {
        long number = 600851475143;
        bool prime = true;

        for (long i = 3; i <= number; i++)
        {
            for (long n = 2; n < i; n++)
            {

                if (i % n == 0)
                {
                    prime = false;
                    break;
                }
            }

            if (prime)
            {
                if (number % i == 0)
                {
                    Console.WriteLine(i);

                }

            }
            prime = true;

        }
        Console.ReadKey();
    }
}

现在,虽然我得到了正确的答案(即6857),但我发现我的方法非常低效。如果你运行我的代码,你会发现它仍然会运行超过2分钟…我的问题是,我如何才能为此编写更高效/更快的代码?


My question is how can I write a more efficient/faster code for this?

这是个错误的问题。或者更确切地说,这是一个过早的问题。

正确的问题是:

  • 我的程序正确吗?
  • 我的计划组织得好吗?

快速编写一个错误的程序可以让你更快地得到错误的答案,这并不是一个改进。要使一个组织不良的程序更快是非常困难的,所以首先要把它组织好。

让我们从对你的程序做一个小小但极其重要的改进开始:我们注意到"这是最重要的吗?"可以由助手清晰地表示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Program
{
  static bool IsPrime(long i)
  {
    for (long n = 2; n < i; n++)
    {
      if (i % n == 0)
        return false;
    }
    return true;
  }

  static void Main(string[] args)
  {
    long number = 600851475143;
    for (long i = 3; i <= number; i++)
    {
      if (IsPrime(i))
        Console.WriteLine(i);
    }
    Console.ReadKey();
  }
}

看看刚才发生了什么。你的程序突然变得越来越容易理解了。Isprime做什么?它告诉你一个整数是否是素数。主循环做什么?它打印出3和数字之间的所有素数。

现在,重新开始。程序的每个部分都正确吗?不,当给定1时,IsPrime返回true,但通常不被视为质数。修复错误。您希望助手方法可靠。确保你的助手方法完全按照他们在罐头上说的做。编写测试!因为您希望确保当您更改这些方法以使其更快时,不会意外地使它们不正确。

既然我们既正确又有条理,我们就可以开始优化了。你能想出办法让我更快吗?当然:

  • 我们只需要检查i的平方根。(注意,n * n <= in <= Sqrt(i)快)
  • 一旦我们检查了2,我们就不需要检查4,6,8,…

你能想出其他方法使它更快吗?

但关键是要组织好你的程序。一旦你有了一个组织良好的程序,你就可以在更高的层次上进行推理,并且你可以找到并消除缓慢的部分。


首先,为了知道一个数是否是素数,您使用的算法效率非常低(顺序(n?2)),可以改进返回数字是否为质数的函数的顺序。

要确定一个数字是否是质数,您需要检查它是否不能被小于n的任何数字整除。

你只需要考虑小于√n的因子,因为如果n可以被某个数字p整除,那么n=p×q,由于p≤q,你可以得出p≤√n。注意,你考虑的是小于n而不是小于√n的因子。

也许这个类似的问题可以帮助你,"下面最大的素数是什么"。

解决这一问题的最佳方法是使用埃拉托斯滕筛。(https://en.wikipedia.org/wiki/screen-of-eratosthenes)

这是一个使用橡皮擦的伪代码。

1
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
   public int primes(int n) {
     boolean[] isPrime = new boolean[n];
     for (int i = 2; i < n; i++) {
        isPrime[i] = true;
     }
     // Loop's ending condition is i * i < n instead of i < sqrt(n)
     // to avoid repeatedly calling an expensive function sqrt().
     for (int i = 2; i * i < n; i++) {
       if (!isPrime[i]) continue;
       for (int j = i * i; j < n; j += i) {
         isPrime[j] = false;
       }
     }
     int count = 0;
     for (int i = 2; i < n; i++) {
       if (isPrime[i]) count++;
     }

    //return the max prime
    int maxPrime = 1;
    for(int i = 0; i < isPrime.count; i++){
      if(isPrime[i]){
        maxPrime = i;
      }
      return maxPrime;
    }
  }


我写了一个方法,可以给你所有的基本因素,其中最大的一个。我的代码正在运行,您可以查看它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void PrimeFactor1(long n)
{
    List<int> sList = new List<int>();
    long temp = 1;
    for (int i = 2; i <= n; i++)
    {
        if ((n % i) == 0)
        {
            temp = n / i;
            sList.Add(i);
            i = 1;
            n = temp;
        }    
    }
    string arr = string.Join(",", sList.ToArray());
    Console.Write(arr);
    Console.WriteLine(".");
    Console.WriteLine("The Biggest Prime number is: {0}", sList.Max());
}


如果您要处理许多项目Euler问题,那么您需要很好地实现Eratostenes的筛。Eratostenes类的两个有用方法是nextPrime(int p)previousPrime(int p),它们分别返回下一个和下一个最低质数。

ETA:伪代码被修改为不正确。对不起的。