找到给定数字的所有素数的最佳,最高效的算法是什么?

时间:2011-11-03 07:29:32

标签: c# performance algorithm primes

我目前正在使用这种方法,但效果很好:

 private static List<long> GetPrimeNumbers(long number)
        {
            var result = new List<long>();
            for (var i = 0; i <= number; i++)
            {
                var isPrime = true;
                for (var j = 2; j < i; j++) 
                {
                    if (i % j == 0) 
                    {
                        isPrime = false;
                        break;
                    }
                }
                if (isPrime)
                {
                    result.Add(i);
                }
            }
            return result;
        }

以上是最好的算法吗?

当数字超过100000时,它真的很慢。

我的意思是,找到小于或等于给定数字的素数的最佳,最高效的算法是什么?

7 个答案:

答案 0 :(得分:5)

  1. Sieve of Eratosthenes。该算法可以生成最多n的所有素数。时间复杂度 - O(nlog(n)),内存复杂度 - O(n)

  2. BPSW primality test。此算法可以检查n是否为 pseudoprime 。它在前10 ^ 15个数字上进行了测试。时间复杂度 - O(log(n))

  3. <强>更新 我做了一些研究并编写了在c#中生成素数的简单实现。当我们检查数字N是否为素数时的主要想法 - 我们只需检查它是否可被任何小于sqrt(N)的素数整除。

    首次实施:

    public static List<int> GeneratePrimes(int n)
    {
      var primes = new List<int>();
      for(var i = 2; i <= n; i++)
      {
        var ok = true;
        foreach(var prime in primes)
        {
          if (prime * prime > i)
            break;
          if (i % prime == 0)
          {
            ok = false;
            break;
          }
        }
        if(ok)
          primes.Add(i);
      }
      return primes;
    }
    

    Test results

    10^6 - 0.297s
    10^7 - 6.202s
    10^8 - 141.860s
    

    使用并行计算的第二个实现: 1.生成最多sqrt(N)的所有素数 2.使用并行计算使用素数sqrt(N) + 1生成从Nsqrt(N)的所有素数。

    public static List<int> GeneratePrimesParallel(int n)
        {
          var sqrt = (int) Math.Sqrt(n);
          var lowestPrimes = GeneratePrimes(sqrt);
          var highestPrimes =  (Enumerable.Range(sqrt + 1, n - sqrt)
                                    .AsParallel()
                                    .Where(i => lowestPrimes.All(prime => i % prime != 0)));
          return lowestPrimes.Concat(highestPrimes).ToList();
        }
    

    Test results

    10^6 - 0.276s
    10^7 - 4.082s
    10^8 - 78.624
    

答案 1 :(得分:4)

Sieve of Atkin可能性能最高,但据我所知,有人从一开始就找到了更好的效果。

Erathosthenes和Sundaram也有自己的筛子,实施起来相当简单。他们中的任何一个都会通过单独查找每个数字中的一个因子来解决这个问题。

所有筛子使用的工作内存多于一次分解一个值,但通常比最终的素数列表更少的内存。

答案 2 :(得分:2)

你可以大大改善你的算法,测试n是否是2和sqrt(n)之间任意整数的倍数。

    private static List<int> GetPrimeNumbers2(long number)
    {
        var result = new List<int>();

        for (var i = 0; i <= number; i++)
        {
            var isPrime = true;
            var n = Math.Floor(Math.Sqrt(i));

            for (var j = 2; j <= n; j++)
            {
                if (i % j == 0)
                {
                    isPrime = false;
                    break;
                }
            }

            if (isPrime)
            {
                result.Add(i);
            }
        }

        return result;
    }

这将复杂性从O(N N)改为O(N sqrt(N))。

用于测试一般数字素数的最快的已知算法椭圆曲线原始性证明(ECPP):http://en.wikipedia.org/wiki/Elliptic_curve_primality_proving 我想实施它会很困难所以只有你真的需要它才能实现它。可能有图书馆可以帮助你。

答案 3 :(得分:1)

这将为初始执行提供合理的性能,然后接近O(1)(对于任何重复请求,它将是O(N)但非常,非常小)性能,并且对于大于当前最大数量。

private static List<ulong> KnownPrimes = new List<ulong>();
private static ulong LargestValue = 1UL;


private static List<ulong> GetFastestPrimeNumbers(ulong number)
{
    var result = new List<ulong>();
    lock (KnownPrimes)
    {
        result.AddRange(KnownPrimes.Where(c => c < number).ToList());
        if (number <= LargestValue)
        {
            return result;
        }
        result = KnownPrimes;

        for (var i = LargestValue + 1; i <= number; i++)
        {
            var isPrime = true;
            var n = Math.Floor(Math.Sqrt(i));

            for (var j = 0; j < KnownPrimes.Count; j++)
            {
                var jVal = KnownPrimes[j];
                if (jVal * jVal > i)
                {
                    //isPrime = false;
                    break;
                }
                else if (i % jVal == 0)
                {
                    isPrime = false;
                    break;
                }
            }
            if (isPrime)
            {
                result.Add(i);
            }
        }
        LargestValue = number;
    }
    return result;

}

编辑:使用阿特金筛选的速度要快得多,我想知道了:

private static List<ulong> KnownPrimes = new List<long>();
private static ulong LargestValue = 1UL;

private unsafe static List<ulong> FindPrimes(ulong number)
{
    var result = new List<ulong>();
    var isPrime = new bool[number + 1];
    var sqrt = Math.Sqrt(number);
    lock (KnownPrimes)
    {

        fixed (bool* pp = isPrime)
        {
            bool* pp1 = pp;
            result.AddRange(KnownPrimes.Where(c => c < number).ToList());
            if (number <= LargestValue)
            {
                return result;
            }
            result = KnownPrimes;

            for (ulong x = 1; x <= sqrt; x++)
                for (ulong y = 1; y <= sqrt; y++)
                {
                    var n = 4 * x * x + y * y;
                    if (n <= number && (n % 12 == 1 || n % 12 == 5))
                        pp1[n] ^= true;

                    n = 3 * x * x + y * y;
                    if (n <= number && n % 12 == 7)
                        pp1[n] ^= true;

                    n = 3 * x * x - y * y;
                    if (x > y && n <= number && n % 12 == 11)
                        pp1[n] ^= true;
                }

            for (ulong n = 5; n <= sqrt; n++)
                if (pp1[n])
                {
                    var s = n * n;
                    for (ulong k = s; k <= number; k += s)
                        pp1[k] = false;
                }

            if (LargestValue < 3)
            {
                KnownPrimes.Add(2);
                KnownPrimes.Add(3);
            }
            for (ulong n = 5; n <= number; n += 2)
                if (pp1[n])
                    KnownPrimes.Add(n);
            LargestValue = number;
        }
    }

    return result;
}

改编自Source

这很容易改进,以便在添加项目时获得更好的性能,但我建议您在执行之间将以前的KnownPrimes列表保存到磁盘,并加载预先存在的值列表,例如http://primes.utm.edu/lists/small/millions中的列表 - 积分转到CodingBarfield

答案 4 :(得分:0)

我找到了这个链接:

http://www.troubleshooters.com/codecorn/primenumbers/primenumbers.htm

根据你的问题,似乎你感兴趣的不是证明某个特定数字可能(或肯定)是一个素数,而且你也不想对大数字进行分解。要查找到达给定N的所有素数,可以使用Eratosthenes Sieve,但似乎在上面的链接中考虑了进一步的优化。

答案 5 :(得分:0)

我认为一个相关的问题是“上限有多大”。如果数字在一个相对较小的范围内[比如2 ^ 16]你可能只是预先计算并保存所有素数(低于某个限制)到文件,然后在适当的时候加载到内存中(然后可能继续使用其中一个Sieves列在下面。

上面的Ivan Benko和Steve Jessop说明了两个更为人熟知的快速方法[Eratosthenes,Atkin],虽然Ivan,Sieve的复杂性是O(n * log(log(n)))。

Sieve相对容易实现,与您的方法相比速度非常快。

答案 6 :(得分:-1)

绝对最高性能:

(最小化工作以获得结果)。

将域中所有数字的素数存储在哈希表中,并将数字作为键。