【问题标题】:I'm trying to implement sieve of eratosthenes but it works only for numbers smaller then 33我正在尝试实施 Eratosthenes 筛,但它仅适用于小于 33 的数字
【发布时间】:2020-03-15 20:59:53
【问题描述】:

我正在尝试为 Eratosthenes 筛编写一个程序,它可以工作,但如果输入数字为 33 或更大,我会收到此错误:

Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0
    at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
    at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
    at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
    at java.base/java.util.Objects.checkIndex(Objects.java:373)
    at java.base/java.util.ArrayList.get(ArrayList.java:425)
    at Main.main(Main.java:23)

这是我使用的代码

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner read = new Scanner(System.in);
        int nr = read.nextInt();

        ArrayList<Integer> listA = new ArrayList<Integer>();
        ArrayList<Integer> listB = new ArrayList<Integer>();

        for (int i = 2; i <= nr; i++)
                listA.add(i);
        //System.out.println(listA);
        int m = 2;
        listB.add(m);
        while (m <= nr-2) {
            listA.removeAll(Arrays.asList(m));
            for (int j = m*2; j <= nr; j = j + m) {
                listA.removeAll(Arrays.asList(j));
            }
            m = listA.get(0);
            listB.add(m);
        }
        System.out.println(listB);

    }
}

【问题讨论】:

  • 不需要listA.removeAll(Arrays.asList(m));,因为您只删除了一个值。可以直接listA.remove(m)。就您的其余代码而言,我不确定您要完成什么。你可以只使用一个 for 循环和一个 boolean[] 你不需要两个 ArrayLists
  • 请指出您的代码的哪一行引发了异常
  • 顺便说一句,最简单和最快的实现是巨大的boolean[],但如果您的筛子很大(数百万),请使用BitSet,每个值仅使用 1 位。

标签: java sieve-of-eratosthenes


【解决方案1】:

当您获得java.lang.IndexOutOfBoundsException 时,这意味着此时您已经从listA 中删除了所有数字,因此您不能这样做listA.get(0)

我会声明一个布尔数组并将它们全部设置为真。你可以假装这些是从 0 到 n 的数字。

然后通过从 2 开始将每个非质数设置为 false 并将倍数设置为 false 等。在相乘之前,您可以检查该数字是否已经设置为 false,这意味着不需要乘这个数字,因为所有倍数已经设置为假。这使得算法明显更快。

最后打印出从2开始的所有素数。

您可以删除 Arrays.fill 以提高效率,但随后您需要反转所有其他逻辑,因为布尔值将默认为 false。

        boolean[] primeNumbers = new boolean[nr + 1];
        Arrays.fill(primeNumbers, true);

        int m = 2;
        while (m <= Math.sqrt(nr)) {
            if (primeNumbers[m])
                for (int j = m * 2; j <= nr; j = j + m) {
                    primeNumbers[j] = false;
                }
            m++;
        }

        for (int i = 2; i <= nr; i++) {
            if (primeNumbers[i]) System.out.println(i);
        }

【讨论】:

  • 请注意,实际限制是root nr(不是nr / 2),因为这允许您跳过检查逆序因子(即2 * 17 -> 17 * 2)。还要注意,第二个很好的优化(在速度和内存方面)是简单地排除所有偶数,因为所有偶数都是非素数(当然,除了 2) - 数组可以是一半大小。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-04-15
  • 1970-01-01
  • 2016-01-07
  • 2015-03-19
  • 2022-01-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多