簡體   English   中英

找到不到200萬的所有素數之和需要多長時間?

[英]How much time should it take to find the sum of all prime numbers less than 2 million?

我試圖解決這個項目歐拉問題 我實現了euler的篩子作為java中的幫助類。 它適用於小數字。 但是,當我輸入200萬作為限制時,它不會返回答案。 我使用Netbeans IDE。 我等了很多個小時一次,但仍然沒有打印答案。 當我停止運行代碼時,它給出了以下結果

Java結果:2147483647
建立成功(總時間:2,097分43秒)

這個答案是不正確的。 即使等了這么多時間,這也是不正確的。 雖然相同的代碼返回較小限制的正確答案。

在這個頁面的底部給出了一個非常簡單的算法。

我的實現是這樣的:

package support;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author admin
 */
public class SieveOfEuler {
    int upperLimit;
    List<Integer> primeNumbers;

    public SieveOfEuler(int upperLimit){
        this.upperLimit = upperLimit;
        primeNumbers = new ArrayList<Integer>();
        for(int i = 2 ; i <= upperLimit ; i++)
            primeNumbers.add(i);
        generatePrimes();
    }

    private void generatePrimes(){
        int currentPrimeIndex = 0;
        int currentPrime = 2;
        while(currentPrime <= Math.sqrt(upperLimit)){
            ArrayList<Integer> toBeRemoved = new ArrayList<Integer>();
            for(int i = currentPrimeIndex ; i < primeNumbers.size() ; i++){
                int multiplier = primeNumbers.get(i);
                toBeRemoved.add(currentPrime * multiplier);
            }

            for(Integer i : toBeRemoved){
                primeNumbers.remove(i);
            }

            currentPrimeIndex++;
            currentPrime = primeNumbers.get(currentPrimeIndex);
        }
    }

    public List getPrimes(){
        return primeNumbers;
    }

    public void displayPrimes(){
        for(double i : primeNumbers)
            System.out.println(i);
    }
}

我很困惑! 我的問題是

1)為什么要花這么多時間? 我在做什么有什么不對嗎?

如果您發現錯誤,請建議改進​​我的編碼風格的方法。

問題更新:

這是代碼,我計算計算的素數之和:

package projecteuler;

import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import support.SieveOfEuler;

/**
 *
 * @author admin
 */
public class Problem10 {
    private int upperLimit;
    private BigInteger sumOfPrimes;
    public void getInput() {
        try {
            System.out.println("Enter the upper limit");
            upperLimit = Integer.parseInt(br.readLine());
        } catch (IOException ex) {
            Logger.getLogger(Problem10.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void execute() {
        BigInteger sum = new BigInteger("0");
        SieveOfEuler soe = new SieveOfEuler(upperLimit);
        ArrayList<Integer> primeNumbers = (ArrayList<Integer>)soe.getPrimes();
        for(int i : primeNumbers){
            sum = sum.add(new BigInteger(Integer.toString(i))) ;
        }
        System.out.println(sum);
    }

    public void printOutput() {
       //System.out.println(sumOfPrimes);
    } 
}

你的Sieve速度太慢的原因是你犯了一個根本性的錯誤。 primeNumbers應該是一個布爾數組,而不是List 完成后, primeMumbers[i]的值對於素數將為true ,對於復合而言為false

這就是它產生如此巨大差異的原因:

  • 設置或清除數組中的標志是O(1) ; 即每次操作的恆定時間很短。
  • ArrayList刪除元素是O(N) ,其中N是列表的大小......並且非常大
  • 每個ArrayList.remove(...)操作都必須搜索列表。 如果該值不再存在(因為您已經刪除了它),則刪除操作必須查看列表中的每個剩余元素......每次調用時最多約200萬個...每次調用它。
  • ArrayList.remove(...)找到一個元素時,它會通過將元素一個索引后面的所有剩余元素復制到后備數組中的左側來刪除它。 同樣,每次刪除一個條目時,您最多可復制約200萬個條目。

我希望一個實施良好的Erasothenes篩子能夠在幾秒鍾內計算出不到200萬的所有素數。

for(int i = currentPrimeIndex ; i < primeNumbers.size() ; i++){
    int multiplier = primeNumbers.get(i);
    toBeRemoved.add(currentPrime * multiplier);
}

在第一步,這將生成一個200萬倍的2的ArrayList(toBeRemoved)。

然后它迭代到BeRemoved,為toBeRemoved中的每個條目掃描整個200萬個候選素數的數組 toBeRemoved中的一半值不可能是primeNumbers因為它們太大了。 每次刪除都會導致每個值的索引大於刪除的索引,向下移動一個位置。

我認為這是效率低下的主要原因。 實現Eratosthenes篩子的通常方法是創建一個包含200萬個布爾值的數組,最初都是真的。 i被確定為非素數時,將possibly_prime[i]設置為false。 要查找下一個素數,請向前掃描以查找true值。 要獲得最后所有素數的列表,請迭代記錄每個true值的索引的數組。 你應該為歐拉的篩子做同樣的事情。

對於高達200萬的素數,您不需要優化。

為了回答這個主題標題中的問題:這是項目Euler網站所說的: http//projecteuler.net/index.php?section = about

我已經編寫了我的程序但是需要幾天才能得到答案嗎? 絕對不! 每個問題都是根據“一分鍾規則”設計的 ,這意味着雖然設計一個成功的算法可能需要幾個小時才能解決更加困難的問題,但是有效的實施方案可以在一個適度的計算機上獲得解決方案。不到一分鍾。

;-)

一些明顯的錯誤:

while(currentPrime <= Math.sqrt(upperLimit))

在每個步驟中計算平方根(除非由編譯器優化)。 您應該計算一次並存儲結果。

currentPrimeIndex++;

你應該至少做出明顯的優化,只測試奇數。 你已經知道偶數不是素數。 這應該把你的時間縮短一半。

此外,您正在使用強力方法來查找素數。 對於大的上限,這將是緩慢的。 您應該為搜索使用更好的算法 - 您將能夠在網絡中找到更多信息,但我不知道是否允許這是Project Euler的精神。

import java.util.*;

public class PrimeSum
{
    public static int isPrime(int num)
    {
        int sum = 0;
        int factor = 1;
        while(factor <= num)
        {
            if(num % factor != 0)
            {
                sum += factor;
                factor ++;
            }
            else
            {
                factor ++;
            }
        }
        return sum;
    }

    public static void main(String[] args)
    {
        System.out.println("The program gets the sum of all prime numbers.");

        Scanner scan = new Scanner(System.in);

        System.out.print("Enter a number: ");
        int num = scan.nextInt();

        int sum = isPrime(num);

        System.out.println(sum);
    }
}

這是一個使用簡單的Eratosthenes篩子的解決方案:

function sumPrimes(n)
    sum, sieve := 0, makeArray(2..n, True)
    for p from 2 to n
        if sieve[p]
            sum := sum + p
            for i from p*p to n step p
                sieve[i] := False
    return sum

我在這里用Python編寫這個解決方案,需要一秒鍾的時間。 如果你對使用素數編程感興趣,我謙虛地在我的博客上推薦這篇文章

while(currentPrime <= Math.sqrt(upperLimit))//它降低了復雜性,並且總和不是int的另一點。 發生溢出

如果它可以幫助您查看我的解決方案。 這是我的解決方案

public static boolean isPrime(int i) { // general isPrime method
      if (i < 2) {
         return false;
      } else if (i % 2 == 0 && i != 2) {
          return false;
      } else {
           for (int j = 3; j <= Math.sqrt(i); j = j + 2) {
               if (i % j == 0) {
                  return false;
                }
           }
      }

  return true;
 }

    public static boolean isPrimeForOdd(int i){ // only for odds
        for (int j = 3; j <= Math.sqrt(i); j = j + 2) {
             if (i % j == 0) {
                return false;
              }
        }

      return true;
     }

 public static long sumOfPrimes(int n) {
    long sum = 2;

    for (int i = 3; i < n; i += 2) {
          if (isPrimeForOdd(i)) {
              sum += i;
          }
    }

    return sum;
 }

 public static void main(String[] args) throws ParseException {
      System.out.println(sumOfPrimes(2000000));
 }
  1. 列表是正確的類型嗎? remove(obj)期間,Set會表現得更好。 在您的情況下,嘗試一個BitSet

  2. 首先創建一個(長)要刪除的元素列表,然后單獨刪除它們。 為什么不簡單地刪除它們?

  3. 結果不適合int

即使沒有篩子,這個問題也可以在不到1秒的時間內解決。 檢查數字是否為素數: http//www.parseexception.com/how-to-check-whether-a-number-is-prime-or-not/

對所有數字執行此操作並將它們相加。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM