【问题标题】:Which is the quickest of prime generating algorithms?哪个是最快的素数生成算法?
【发布时间】:2015-03-15 11:31:10
【问题描述】:

我正在研究一种 Java 算法,以查找不超过某个数字的所有素数。它应该是对早期这样做的方法的改进,如下所示:

public static int[] generatePrimesUpTo(int max)
{
    int[] primes = new int[max];
    primes[0]=2;
    int p = 1;
    for (int i=3;i<max;i+=2)
    {
        if (isPrime(i))
        {
            primes[p]=i;
            p+=1;
        }
    }
    return primes;  
}

public static boolean isPrime(int a)
{
    for (int i=3;i<((int)Math.sqrt(a)+1);i+=2)
    {
        if (a%i==0)
            return false;
    }
    return true;
}

它只是检查一个数字 N 是否可以被一个较小的数字整除,从 2 开始到 sqrt(N) 结束。

现在新方法是仅将 N 除以算法之前发现的更小的素数。我认为它会大大加快这个过程,因为它需要做的计算要少得多。

public static int[] generatePrimes(int num)
{
    int[] primes = new int[num];
    int p = 3;
    primes[0] = 2;
    primes[1] = 3;
    primes[2] = 5;
    boolean prime;
    for (int i=7;i<num;i+=2)
    {
        prime = true;
        for (int j=0;primes[j+1]<(Math.sqrt(i)+1);j++)
        {
            if (i%primes[j]==0)
            {
                prime = false;
                break;
            }
        }
        if (prime)
        {
            primes[p]=i;
            p++;
        }
    }
    return primes;
}

但是,对于 Nmax = 10^7,速度似乎几乎没有差异。 对于 Nmax = 10^8,新的快了 20%,但我的电脑在计算旧的时更活跃,我只尝试了 10^8 一次。

谁能告诉我为什么这种新方法没有那么快?或者我可以做些什么来进一步改进算法?

提前致谢!

【问题讨论】:

  • 你想知道为什么你的第二个例子没有更快,或者你想知道生成素数的快速方法吗?如果是前者,请考虑更改您的标题。
  • 你为什么要测试 primes[j+1]

标签: performance algorithm comparison


【解决方案1】:

您应该考虑是否没有一种方法可以比单独检查每个素数更快地找到范围内的所有素数。例如,您将检查许多数字是否能被 73 整除。但事实是,您可以更快地确定所有能被 73 整除的数字(它们是 73、2*73、3*73、4*73 等。 )。

顺便说一句:您在循环的每次单次迭代中计算 Math.sqrt (j)。将该计算移到循环之外可能会使您的代码更快。

【讨论】:

    【解决方案2】:

    您的第二种算法更快。我不知道为什么你只看到了 20% 的改进。以下是我的测试结果,有单独的实现:

    10^6:
    First:  00:00:01.0553813   67240405 steps
    Second: 00:00:00.2416291   13927398 steps
    Sieve:  00:00:00.0269685    3122044 steps
    
    10^7:
    First:  00:00:26.4524301 1741210134 steps
    Second: 00:00:04.6647486  286144934 steps
    Sieve:  00:00:00.3011046   32850047 steps
    
    10^8:
    First:  00:12:00.8986644 46474124250 steps
    Second: 00:01:43.1543445  6320928466 steps
    Sieve:  00:00:03.6146328   342570200 steps
    

    最后一个算法是Sieve of Eratosthenes,这是一个更好的算法。我在 C# 中为一个处理器实现了所有这些,前两个基于您的代码进行了微小的更改,例如测试 primes[j]*primes[j] &lt;= i

    我使用的埃拉托色尼筛法的实现非常基础,

    Boolean[] definitelyComposite = new Boolean[max]; // initialized automatically to false
    int p = 0;
    for (long i = 2; i < max; i++)
    {
        numSteps++;
        if (!definitelyComposite[i])
        {
            primes[p] = i;
            p++;
            for (long j = i * i; j < max; j += i)
            {
                numSteps++;
                definitelyComposite[j] = true;
            }
        }
    }
    

    而且还可以改进。例如,除了 i 为 2 时,我可以在循环中使用 j+= 2*i

    【讨论】:

    • 感谢您的回复道格拉斯!很高兴听到我的第二个算法要快得多。我会考虑使用 Erathones 的筛子。我之前听说过它,但起初它似乎很原始,以至于我没有想到使用它。
    • @Mart Last:Eratosthenes 的筛子在作为素数测试时很糟糕,但它擅长找出某个数以内的所有素数。
    猜你喜欢
    • 1970-01-01
    • 2010-10-02
    • 1970-01-01
    • 1970-01-01
    • 2014-01-11
    • 2011-05-31
    • 2013-02-13
    • 2011-02-04
    • 2016-12-20
    相关资源
    最近更新 更多