【问题标题】:Program to find all primes in a very large given range of integers在一个非常大的给定整数范围内找到所有素数的程序
【发布时间】:2012-05-29 01:01:13
【问题描述】:

我在编程网站上遇到了以下问题: Peter 想为他的密码系统生成一些素数。帮助他!你的任务是生成两个给定数字之间的所有素数!

输入

输入以单行中的测试用例数量 t (t

我想出了以下解决方案:

import java.util.*;

public class PRIME1 {
    static int numCases;
    static int left, right;
    static boolean[] initSieve = new boolean[32000];
    static boolean[] answer;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        numCases = sc.nextInt();
        initSieve[0] = true;
        initSieve[1] = true;
        Sieve();
        for (int j = 0; j < numCases; j++) {
            String line = sc.next();
            String line2 = sc.next();
            left = Integer.parseInt(line);
            right = Integer.parseInt(line2);
            answer = new boolean[right - left + 1];
            getAnswer();
            for (int i = 0; i < answer.length; i++) {
                if (!answer[i]) {
                    int ans = i + left;
                    System.out.println(ans);
                }
            }
            System.out.println();
        }
    }

    public static void Sieve() {

        for (int i = 2; i < 32000; i++) {
            if (!initSieve[i]) {
                for (int j = 2 * i; j < 32000; j += i) {
                    initSieve[j] = true;
                }
            }
            if (i * i > 32000)
                break;
        }
    }

    public static void getAnswer() {
        for (int i = 2; i < 32000 && i <= right; i++) {
            if (!initSieve[i]) {
                int num = i;
                if (num * 2 >= left) {
                    num *= 2;
                } else {
                    num = (num * (left / num));
                    if (num < left)
                        num += i;
                }
                for (int j = num; j >= left && j <= right; j += i) {
                    answer[j - left] = true;
                }
            }
        }
    }
}

在阅读了一些建议后,我编辑了我的解决方案。我仍然收到超出时间限制的错误。关于如何进一步优化这一点的更多建议?我正在计算直到 32000 的所有素数,然后使用这些来找到 n 到 m 之间的素数。

谢谢, 罗希特

【问题讨论】:

  • 如果您从 3 开始,而不是在筛子中的 2,并设法在循环外将值交换为 2,则可以通过 i+=2; 进行迭代。然后,而不是在外循环中运行到 isNotPrime.length,√(isNotPrime.length) 应该就足够了。不相关:Scanner 有一个方法 nextInt。
  • 您可以通过仅循环范围内的赔率(从奇数开始,在for (int i = 0; i &lt; answer.length; i+=2) 中以i+=2 递增)来立即减半运行时间。确保i 对应的奇数不低于left。 2 以上的偶数永远不会是素数。 :) 那将是稀疏寻址方案,更快的是使用压缩数组,其中索引i 处的条目代表数字n=left_odd + 2i。在Sieve() 内部,j+=2*i 也可以工作(虽然这个筛子很小),但更重要的是,在getAnswer() 内部。看看丹尼尔的回答。

标签: java algorithm primes sieve-of-eratosthenes


【解决方案1】:

给你

1

这些都是非常小的数字。要筛选上限为n 的范围,您需要√n 的素数。在这里你知道n &lt;= 10^9,所以√n &lt; 31623,所以你最多需要31621的素数。有3401。你可以在几微秒内用标准筛子生成它们。

然后您可以通过标记您之前筛选过的素数的倍数来简单地筛选从mn 的小范围,当素数超过√n 时停止。通过从筛子中消除一些小素数的倍数可以获得一些加速,但逻辑变得更加复杂(您需要特别对待具有小m 的筛子)。

public int[] chunk(int m, int n) {
    if (n < 2) return null;
    if (m < 2) m = 2;
    if (n < m) throw new IllegalArgumentException("Borked");
    int root = (int)Math.sqrt((double)n);
    boolean[] sieve = new boolean[n-m+1];
    // primes is the global array of primes to 31621 populated earlier
    // primeCount is the number of primes stored in primes, i.e. 3401
    // We ignore even numbers, but keep them in the sieve to avoid index arithmetic.
    // It would be very simple to omit them, though.
    for(int i = 1, p = primes[1]; i < primeCount; ++i) {
        if ((p = primes[i]) > root) break;
        int mult;
        if (p*p < m) {
            mult = (m-1)/p+1;
            if (mult % 2 == 0) ++mult;
            mult = p*mult;
        } else {
            mult = p*p;
        }
        for(; mult <= n; mult += 2*p) {
            sieve[mult-m] = true;
        }
    }
    int count = m == 2 ? 1 : 0;
    for(int i = 1 - m%2; i < n-m; i += 2) {
        if (!sieve[i]) ++count;
    }
    int sievedPrimes[] = new int[count];
    int pi = 0;
    if (m == 2) {
        sievedPrimes[0] = 2;
        pi = 1;
    }
    for(int i = 1 - m%2; i < n-m; i += 2) {
        if (!sieve[i]) {
            sievedPrimes[pi++] = m+i;
        }
    }
    return sievedPrimes;
}

使用BitSet 或任何其他类型的打包标志数组将减少内存使用量,因此由于更好的缓存局部性可能会显着提高速度。

【讨论】:

  • 我已根据您的建议编辑了我的解决方案,但仍然遇到超出时间限制的情况。您能否建议如何进一步优化?
  • 时间限制是多少,任务是什么?如果您应该打印出所有素数,那可能是瓶颈。我不精通 Java 的 I/O,所以我无法提供如何加快打印速度的建议。如果您应该只打印每个间隔中素数的数量(或总和),那应该不是问题。此实现在 90 毫秒内完成设置和筛选 100 个大约 100000 长度的间隔,JVM 的启动时间更高。当然,测试机可能会比较慢,缓存也比较小,所以把boolean[]换成BitSets可能会比较好。
【解决方案2】:

使用 BitSet 而不是布尔数组。

public static BitSet primes (final int MAX)
{
     BitSet primes = new BitSet (MAX);
     // make only odd numbers candidates...
     for (int i = 3; i < MAX; i+=2)
     {
        primes.set(i);
     }
     // ... except no. 2
     primes.set (2, true);
     for (int i = 3; i < MAX; i+=2)
     {
        /*
            If a number z is already  eliminated (like 9),
             because it is itself a multiple of a prime 
            (example: 3), then all multiples of z (9) are
            already eliminated.
        */
        if (primes.get (i))
        {
            int j = 3 * i;
            while (j < MAX)
            {
                if (primes.get (j))
                    primes.set (j, false);
                j += (2 * i);
            }
        }
    }
    return primes;
}   

【讨论】:

  • 1000000000/32 整数元素数组.. 不会还有很多空间吗?有什么办法可以利用这个程序中 n-m
  • 1/32 GB,不是吗?价格大约:半块钱。我往往会及时发现问题,需要填充这个位集。但是,我不知道如何使用 Euler totient 来解决问题,并且在 100 000 区间中看不到帮助。我不希望 probablePrime 方法被允许。
【解决方案3】:

您是否 将结果存储在数组中?计算给定整数是否为素数并{left,left+1,...,right}中的每个数字调用它的方法怎么样?

【讨论】:

  • 我没有初始化 isNotPrime 表,因为这些值将默认为我打算拥有的 false。另外,您建议的方法在时间方面会非常昂贵,这就是我没有使用它的原因。
  • 在分离中测试每个候选数的素数(通过试除法?)比使用 Eratosthenes 的偏移筛一次在整个范围上标记复合材料要慢得多(对于每个素数低于sqrt当然是上限)。
【解决方案4】:

访问 isNotPrime 数组时始终可以使用偏移量。

给定 m,n:

boolean[] isNotPrime = new boolean[n-m+1];

// to now if number x is primer or not
boolean xIsPrime = isNotPrime[x-m];

这里 m 是偏移量。

【讨论】:

    【解决方案5】:

    您不必拥有一个大数组:您可以保留到目前为止找到的素数列表,并使用多个数组进行测试,值 = array_slot + offset(已经测试过的值)。完成从 i 到 j 的值后,将 j-i 添加到 offset 并从 J 开始一个新数组。

    您可以从数组中删除偶数,这样可以节省一些空间(值 = array_slot * 2 - 1)。

    【讨论】:

      【解决方案6】:

      由于 m 和 n 之间的距离相对较小,因此您可以对 m 和 n 之间的每个数字进行暴力破解并使用快速素数测试算法。

      如果您允许概率算法,您可以使用Miller-Rabin test。让 M = n-m

      对于问题的极限,这个复杂度将在 10^9 左右。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-05-23
        • 1970-01-01
        • 1970-01-01
        • 2011-09-21
        • 2020-10-03
        • 2015-07-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多