📜  分段筛(在一定范围内打印素数)

📅  最后修改于: 2021-04-23 22:08:23             🧑  作者: Mango

给定范围[低,高],请打印该范围内的所有素数?例如,如果给定范围为[10,20],则输出为11、13、17、19。

天真的方法是从低到高运行一个循环,并检查每个数字的素数。
更好的方法是使用Eratosthenes筛子预先计算最大数量的质数,然后打印范围内的所有质数。
上面的方法看起来不错,但是请考虑输入范围[50000,55000]。上述Sieve方法会预先计算2至50100的质数。这会浪费内存和时间。以下是基于分段筛的方法。

分段筛(背景)
以下是了解分段筛工作原理的基本步骤

  1. 使用“简单筛选”查找所有达到预定义极限的质数(在下面的代码中使用“高”的平方根),并将这些质数存储在数组“ prime []”中。基本上,我们将简单筛选称为极限,不仅找到质数,还将它们放在单独的数组prime []中。
  2. 创建一个数组标记[high-low + 1]。在这里,我们只需要O(n)空间,其中n是给定范围内的元素数。
  3. 遍历在步骤1中找到的所有素数。对于每个素数,在给定范围[low..high]中标记其倍数。

因此,与简单筛子不同,我们不检查所有小于整数high的平方根的整数倍,而仅检查在步骤1中找到的质数的倍数。我们不需要O(high)空间,我们需要O (sqrt(high)+ n)空间。
以下是上述想法的实现。

C++
// C++ program to
// print all primes in a range
// using concept of Segmented Sieve
#include 
using namespace std;
 
// This functions finds all
// primes smaller than limit
// using simple sieve of eratosthenes. 
// It stores found
// primes in vector prime[]
void simpleSieve(int limit, vector& prime)
{
    bool mark[limit + 1];
    memset(mark, false, sizeof(mark));
 
    for (int i = 2; i*i <= limit; ++i)
    {
        if (mark[i] == false)
        {
            // If not marked yet, then its a prime
            prime.push_back(i);
            for (int j = i; j <= limit; j += i)
                mark[j] = true;
        }
    }
}
 
// Finds all prime numbers
// in given range using
// segmented sieve
void primesInRange(int low, int high)
{
     
    // Compute all primes smaller or equal to
    // square root of high using simple sieve
    int limit = floor(sqrt(high)) + 1;
    vector prime;
    simpleSieve(limit, prime);
 
    // Count of elements in given range
    int n = high - low + 1;
 
    // Declaring boolean only for [low, high]
    bool mark[n + 1];
    memset(mark, false, sizeof(mark));
 
    // Use the found primes by
    // simpleSieve() to find
    // primes in given range
    for (int i = 0; i < prime.size(); i++)
    {
         
        // Find the minimum number
        // in [low..high] that is
        // a multiple of prime[i]
        // (divisible by prime[i])
        int loLim = floor(low / prime[i]) * prime[i];
        if (loLim < low)
            loLim += prime[i];
        if(loLim==prime[i])
            loLim += prime[i];
         
      /*  Mark multiples of prime[i]
          in [low..high]:
          We are marking j - low
          for j, i.e. each number
          in range [low, high] is
          mapped to [0, high - low]
          so if range is [50, 100] 
          marking 50 corresponds
          to marking 0, marking
          51 corresponds to 1 and
          so on.Also if the current j
          is prime don't mark
          it as true.In this way we need
          to allocate space only
          for range  */
        for (int j = loLim; j <= high; j += prime[i])
            mark[j - low] = true;
    }
 
    // Numbers which are not marked
    // in range, are prime
    for (int i = low; i <= high; i++)
        if (!mark[i - low])
            cout << i << "  ";
}
 
// Driver Code
int main()
{
    int low = 10, high = 20;
    primesInRange(low, high);
    return 0;
}


Python
# Python program to print
# print all primes in a range
# using concept of Segmented Sieve
from math import floor, sqrt
 
# This functions finds
# all primes smaller than limit
# using simple sieve of eratosthenes.
# It stores found
# primes in list prime[]
 
 
def simpleSieve(limit, primes):
    mark = [False]*(limit+1)
 
    for i in range(2, limit+1):
        if not mark[i]:
 
            # If not marked yet,
            # then its a prime
            primes.append(i)
            for j in range(i, limit+1, i):
                mark[j] = True
 
 
# Finds all prime numbers
# in given range using
# segmented sieve
def primesInRange(low, high):
 
    # Comput all primes smaller
    # or equal to
    # square root of high
    # using simple sieve
    limit = floor(sqrt(high)) + 1
    primes = list()
    simpleSieve(limit, primes)
 
    # Count of elements in given range
    n = high - low + 1
 
    # Declaring boolean only for
    # [low, high]
    mark = [False]*(n+1)
 
    # Use the found primes by
    # simpleSieve() to find
    # primes in given range
    for i in range(len(primes)):
 
        # Find the minimum number
        # in [low..high] that is
        # a multiple of prime[i]
        # (divisible by prime[i])
        loLim = floor(low/primes[i]) * primes[i]
        if loLim < low:
            loLim += primes[i]
        if loLim == primes[i]:
            loLim += primes[i]
 
        # Mark multiples of primes[i]
        # in [low..high]:
        # We are marking j - low for j,
        # i.e. each number
        # in range [low, high] is mapped
        # to [0, high-low]
        # so if range is [50, 100]
        # marking 50 corresponds
        # to marking 0, marking 51
        # corresponds to 1 and
        # so on. In this way we need
        # to allocate space
        # only for range
        for j in range(loLim, high+1, primes[i]):
            mark[j-low] = True
 
    # Numbers which are not marked
    # in range, are prime
    for i in range(low, high+1):
        if not mark[i-low]:
            print(i, end=" ")
 
 
# Driver code
low = 10
high = 100
primesInRange(low, high)


Java
// Java program to print
// all primes in a range
// using concept of Segmented Sieve
import java.io.*;
import java.util.*;
 
class GFG {
    // This function uses simple sieve of eratosthenes to
    // find primes upto sqrt(high)
    static void simpleSieve(int limit,
                            Vector prime)
    {
        // bound is square root of "high"
        int bound = (int)Math.sqrt(limit);
 
        boolean[] mark = new boolean[bound + 1];
        for (int i = 0; i <= bound; i++)
            mark[i] = true;
 
        for (int i = 2; i * i <= bound; i++) {
            if (mark[i] == true) {
                for (int j = i * i; j <= bound; j += i)
                    mark[j] = false;
            }
        }
 
        // adding primes to vector
        for (int i = 2; i <= bound; i++) {
            if (mark[i] == true)
                prime.add(i);
        }
    }
 
    // Finds all prime numbers in range low to high
    // using the primes we obtained from
    // simple sieve
    static void segmentedSieve(int low, int high)
    {
        Vector prime = new Vector();
        simpleSieve(high, prime); // stores primes upto
                                  // sqrt(high) in prime
 
        boolean[] mark = new boolean[high - low + 1];
        for (int i = 0; i < mark.length; i++)
            mark[i] = true;
 
        for (int i = 0; i < prime.size(); i++) {
            // find minimum number in [low...high]
            // that is multiple of prime[i]
            int loLim = (low / prime.get(i)) * prime.get(i);
            // loLim += prime.get(i);
            if (loLim < low)
                loLim += prime.get(i);
            if (loLim == prime.get(i))
                loLim += prime.get(i);
 
            for (int j = loLim; j <= high;
                 j += prime.get(i)) {
                 mark[j - low] = false;
            }
        }
 
        // print all primes in [low...high]
        for (int i = low; i <= high; i++) {
            if (mark[i - low] == true)
                System.out.print(i + " ");
        }
    }
 
    // Driver Code
    public static void main(String args[])
    {
        int low = 10, high = 100;
        segmentedSieve(low, high);
    }
}


输出
11  13  15  17  19