📜  分段筛

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

给定数字n,请打印所有小于n的素数。例如,如果给定的数字为10,则输出2、3、5、7。

幼稚的方法是运行从0到n-1的循环,并检查每个数字的素数。更好的方法是使用Eratosthenes的简单筛网。

C
// This functions finds all primes smaller than 'limit'
// using simple sieve of eratosthenes.
void simpleSieve(int limit)
{
    // Create a boolean array "mark[0..limit-1]" and
    // initialize all entries of it as true. A value
    // in mark[p] will finally be false if 'p' is Not
    // a prime, else true.
    bool mark[limit];
    for(int i = 0; i


Java
// This functions finds all primes smaller than 'limit'
// using simple sieve of eratosthenes.
static void simpleSieve(int limit)
{
   
    // Create a boolean array "mark[0..limit-1]" and
    // initialize all entries of it as true. A value
    // in mark[p] will finally be false if 'p' is Not
    // a prime, else true.
    boolean []mark = new boolean[limit];
    Arrays.fill(mark, true);
  
    // One by one traverse all numbers so that their
    // multiples can be marked as composite.
    for (int p = 2; p * p < limit; p++)
    {
        // If p is not changed, then it is a prime
        if (mark[p] == true)
        {
            // Update all multiples of p
            for (int i = p * p; i < limit; i += p)
                mark[i] = false;
        }
    }
  
    // Print all prime numbers and store them in prime
    for (int p = 2; p < limit; p++)
        if (mark[p] == true)
            System.out.print(p + " ");
}
 
// This code is contributed by rutvik_56.


Python3
# This functions finds all primes smaller than 'limit'
# using simple sieve of eratosthenes.
def simpleSieve(limit):
 
    # Create a boolean array "mark[0..limit-1]" and
    # initialize all entries of it as true. A value
    # in mark[p] will finally be false if 'p' is Not
    # a prime, else true.
    mark = [True for i in range(limit)]
 
    # One by one traverse all numbers so that their
    # multiples can be marked as composite.
    for p in range(p * p, limit - 1, 1):
 
        # If p is not changed, then it is a prime
        if (mark[p] == True):
 
            # Update all multiples of p
            for i in range(p * p, limit - 1, p):
                mark[i] = False
 
    # Print all prime numbers and store them in prime
    for p in range(2, limit - 1, 1):
        if (mark[p] == True):
            print(p, end=" ")
 
# This code is contributed by Dharanendra L V.


C#
// This functions finds all primes smaller than 'limit'
// using simple sieve of eratosthenes.
static void simpleSieve(int limit)
{
   
    // Create a boolean array "mark[0..limit-1]" and
    // initialize all entries of it as true. A value
    // in mark[p] will finally be false if 'p' is Not
    // a prime, else true.
    bool []mark = new bool[limit];
    Array.Fill(mark, true);
  
    // One by one traverse all numbers so that their
    // multiples can be marked as composite.
    for (int p = 2; p * p < limit; p++)
    {
       
        // If p is not changed, then it is a prime
        if (mark[p] == true)
        {
           
            // Update all multiples of p
            for (int i = p * p; i < limit; i += p)
                mark[i] = false;
        }
    }
  
    // Print all prime numbers and store them in prime
    for (int p = 2; p < limit; p++)
        if (mark[p] == true)
            Console.Write(p + " ");
}
 
// This code is contributed by pratham76.


C++
// C++ program to print print all primes smaller than
// n using segmented sieve
#include 
using namespace std;
 
// This functions finds all primes smaller than 'limit'
// using simple sieve of eratosthenes. It also stores
// found primes in vector prime[]
void simpleSieve(int limit, vector &prime)
{
    // Create a boolean array "mark[0..n-1]" and initialize
    // all entries of it as true. A value in mark[p] will
    // finally be false if 'p' is Not a prime, else true.
    vector mark(limit + 1, true);
 
    for (int p=2; p*p prime;
    prime.reserve(limit);
    simpleSieve(limit, prime);
 
    // Divide the range [0..n-1] in different segments
    // We have chosen segment size as sqrt(n).
    int low = limit;
    int high = 2*limit;
 
    // While all segments of range [0..n-1] are not processed,
    // process one segment at a time
    while (low < n)
    {
        if (high >= n)
           high = n;
         
        // To mark primes in current range. A value in mark[i]
        // will finally be false if 'i-low' is Not a prime,
        // else true.
        bool mark[limit+1];
        memset(mark, true, sizeof(mark));
 
        // Use the found primes by simpleSieve() to find
        // primes in current 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])
            // For example, if low is 31 and prime[i] is 3,
            // we start with 33.
            int loLim = floor(low/prime[i]) * prime[i];
            if (loLim < low)
                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. In this way we need to allocate space only
                for range */
            for (int j=loLim; j


Java
// Java program to print print all primes smaller than
// n using segmented sieve
 
 
import java.util.Vector;
import static java.lang.Math.sqrt;
import static java.lang.Math.floor;
 
class Test
{
    // This methid finds all primes smaller than 'limit'
    // using simple sieve of eratosthenes. It also stores
    // found primes in vector prime[]
    static void simpleSieve(int limit, Vector prime)
    {
        // Create a boolean array "mark[0..n-1]" and initialize
        // all entries of it as true. A value in mark[p] will
        // finally be false if 'p' is Not a prime, else true.
        boolean mark[] = new boolean[limit+1];
         
        for (int i = 0; i < mark.length; i++)
            mark[i] = true;
      
        for (int p=2; p*p prime = new Vector<>(); 
        simpleSieve(limit, prime);
      
        // Divide the range [0..n-1] in different segments
        // We have chosen segment size as sqrt(n).
        int low  = limit;
        int high = 2*limit;
      
        // While all segments of range [0..n-1] are not processed,
        // process one segment at a time
        while (low < n)
        {
            if (high >= n)
                high = n;
 
            // To mark primes in current range. A value in mark[i]
            // will finally be false if 'i-low' is Not a prime,
            // else true.
            boolean mark[] = new boolean[limit+1];
             
            for (int i = 0; i < mark.length; i++)
                mark[i] = true;
      
            // Use the found primes by simpleSieve() to find
            // primes in current range
            for (int i = 0; i < prime.size(); i++)
            {
                // Find the minimum number in [low..high] that is
                // a multiple of prime.get(i) (divisible by prime.get(i))
                // For example, if low is 31 and prime.get(i) is 3,
                // we start with 33.
                int loLim = (int) (floor(low/prime.get(i)) * prime.get(i));
                if (loLim < low)
                    loLim += prime.get(i);
      
                /*  Mark multiples of prime.get(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 (int j=loLim; j


Python3
# Python3 program to print all primes
# smaller than n, using segmented sieve
import math
prime = []
 
# This method finds all primes
# smaller than 'limit' using
# simple sieve of eratosthenes.
# It also stores found primes in list prime
def simpleSieve(limit):
     
    # Create a boolean list "mark[0..n-1]" and 
    # initialize all entries of it as True.
    # A value in mark[p] will finally be False
    # if 'p' is Not a prime, else True.
    mark = [True for i in range(limit + 1)]
    p = 2
    while (p * p <= limit):
         
        # If p is not changed, then it is a prime
        if (mark[p] == True):
             
            # Update all multiples of p
            for i in range(p * p, limit + 1, p):
                mark[i] = False 
        p += 1
         
    # Print all prime numbers
    # and store them in prime
    for p in range(2, limit):
        if mark[p]:
            prime.append(p)
            print(p,end = " ")
             
# Prints all prime numbers smaller than 'n'
def segmentedSieve(n):
     
    # Compute all primes smaller than or equal
    # to square root of n using simple sieve
    limit = int(math.floor(math.sqrt(n)) + 1)
    simpleSieve(limit)
     
    # Divide the range [0..n-1] in different segments
    # We have chosen segment size as sqrt(n).
    low = limit
    high = limit * 2
     
    # While all segments of range [0..n-1] are not processed,
    # process one segment at a time
    while low < n:
        if high >= n:
            high = n
             
        # To mark primes in current range. A value in mark[i]
        # will finally be False if 'i-low' is Not a prime,
        # else True.
        mark = [True for i in range(limit + 1)]
         
        # Use the found primes by simpleSieve()
        # to find primes in current range
        for i in range(len(prime)):
             
            # Find the minimum number in [low..high]
            # that is a multiple of prime[i]
            # (divisible by prime[i])
            # For example, if low is 31 and prime[i] is 3,
            # we start with 33.
            loLim = int(math.floor(low / prime[i]) *
                                         prime[i])
            if loLim < low:
                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. In this way we need to allocate space
            # only for range
            for j in range(loLim, high, prime[i]):
                mark[j - low] = False
                 
        # Numbers which are not marked as False are prime
        for i in range(low, high):
            if mark[i - low]:
                print(i, end = " ")
                 
        # Update low and high for next segment
        low = low + limit
        high = high + limit
 
# Driver Code
n = 100
print("Primes smaller than", n, ":")
segmentedSieve(100)
 
# This code is contributed by bhavyadeep


C#
// C# program to print print
// all primes smaller than
// n using segmented sieve
using System;
using System.Collections;
 
class GFG
{
    // This methid finds all primes
    // smaller than 'limit' using simple
    // sieve of eratosthenes. It also stores
    // found primes in vector prime[]
    static void simpleSieve(int limit,
                            ArrayList prime)
    {
        // Create a boolean array "mark[0..n-1]"
        // and initialize all entries of it as
        // true. A value in mark[p] will finally be
        // false if 'p' is Not a prime, else true.
        bool[] mark = new bool[limit + 1];
         
        for (int i = 0; i < mark.Length; i++)
            mark[i] = true;
     
        for (int p = 2; p * p < limit; p++)
        {
            // If p is not changed, then it is a prime
            if (mark[p] == true)
            {
                // Update all multiples of p
                for (int i = p * p; i < limit; i += p)
                    mark[i] = false;
            }
        }
     
        // Print all prime numbers and store them in prime
        for (int p = 2; p < limit; p++)
        {
            if (mark[p] == true)
            {
                prime.Add(p);
                Console.Write(p + " ");
            }
        }
    }
     
    // Prints all prime numbers smaller than 'n'
    static void segmentedSieve(int n)
    {
        // Compute all primes smaller than or equal
        // to square root of n using simple sieve
        int limit = (int) (Math.Floor(Math.Sqrt(n)) + 1);
        ArrayList prime = new ArrayList();
        simpleSieve(limit, prime);
     
        // Divide the range [0..n-1] in
        // different segments We have chosen
        // segment size as sqrt(n).
        int low = limit;
        int high = 2*limit;
     
        // While all segments of range
        // [0..n-1] are not processed,
        // process one segment at a time
        while (low < n)
        {
            if (high >= n)
                high = n;
 
            // To mark primes in current range.
            // A value in mark[i] will finally
            // be false if 'i-low' is Not a prime,
            // else true.
            bool[] mark = new bool[limit + 1];
             
            for (int i = 0; i < mark.Length; i++)
                mark[i] = true;
     
            // Use the found primes by
            // simpleSieve() to find
            // primes in current range
            for (int i = 0; i < prime.Count; i++)
            {
                // Find the minimum number in
                // [low..high] that is a multiple
                // of prime.get(i) (divisible by
                // prime.get(i)) For example,
                // if low is 31 and prime.get(i)
                //  is 3, we start with 33.
                int loLim = ((int)Math.Floor((double)(low /
                            (int)prime[i])) * (int)prime[i]);
                if (loLim < low)
                    loLim += (int)prime[i];
     
                /* Mark multiples of prime.get(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 (int j = loLim; j < high; j += (int)prime[i])
                    mark[j-low] = false;
            }
     
            // Numbers which are not marked as false are prime
            for (int i = low; i < high; i++)
                if (mark[i - low] == true)
                    Console.Write(i + " ");
     
            // Update low and high for next segment
            low = low + limit;
            high = high + limit;
        }
    }
     
    // Driver code
    static void Main()
    {
        int n = 100;
        Console.WriteLine("Primes smaller than " + n + ":");
        segmentedSieve(n);
    }
}
 
// This code is contributed by mits


简单筛的问题:
Eratosthenes的筛子看起来不错,但考虑n较大的情况,简单筛子会遇到以下问题。

  • 大小为Θ(n)的数组可能无法容纳在内存中
  • 即使对于更大的n,简单的Sieve也不友好。该算法遍历数组时没有参考位置

分段筛
分段筛的目的是将范围[0..n-1]划分为不同的段,并一一计算所有段中的质数。该算法首先使用简单筛子来查找小于或等于√(n)的素数。以下是分段筛中使用的步骤。

  1. 使用简单筛子查找所有素数直到’n’的平方根,并将这些素数存储在数组“ prime []”中。将找到的素数存储在数组’prime []’中。
  2. 我们需要[0..n-1]范围内的所有素数。我们将此范围划分为不同的细分,以使每个细分的大小最大为√n
  3. 对每个细分[low..high]进行关注
    • 创建一个数组标记[high-low + 1]。在这里,我们只需要O(x)空间,其中x是给定范围内的元素数。
    • 遍历在步骤1中找到的所有素数。对于每个素数,在给定范围[low..high]中标记其倍数。

在简单筛中,我们需要O(n)空间,这对于大n可能不可行。这里我们需要O(√n)空间,并且一次处理较小的范围(参考位置)

以下是上述想法的实现。

C++

// C++ program to print print all primes smaller than
// n using segmented sieve
#include 
using namespace std;
 
// This functions finds all primes smaller than 'limit'
// using simple sieve of eratosthenes. It also stores
// found primes in vector prime[]
void simpleSieve(int limit, vector &prime)
{
    // Create a boolean array "mark[0..n-1]" and initialize
    // all entries of it as true. A value in mark[p] will
    // finally be false if 'p' is Not a prime, else true.
    vector mark(limit + 1, true);
 
    for (int p=2; p*p prime;
    prime.reserve(limit);
    simpleSieve(limit, prime);
 
    // Divide the range [0..n-1] in different segments
    // We have chosen segment size as sqrt(n).
    int low = limit;
    int high = 2*limit;
 
    // While all segments of range [0..n-1] are not processed,
    // process one segment at a time
    while (low < n)
    {
        if (high >= n)
           high = n;
         
        // To mark primes in current range. A value in mark[i]
        // will finally be false if 'i-low' is Not a prime,
        // else true.
        bool mark[limit+1];
        memset(mark, true, sizeof(mark));
 
        // Use the found primes by simpleSieve() to find
        // primes in current 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])
            // For example, if low is 31 and prime[i] is 3,
            // we start with 33.
            int loLim = floor(low/prime[i]) * prime[i];
            if (loLim < low)
                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. In this way we need to allocate space only
                for range */
            for (int j=loLim; j

Java

// Java program to print print all primes smaller than
// n using segmented sieve
 
 
import java.util.Vector;
import static java.lang.Math.sqrt;
import static java.lang.Math.floor;
 
class Test
{
    // This methid finds all primes smaller than 'limit'
    // using simple sieve of eratosthenes. It also stores
    // found primes in vector prime[]
    static void simpleSieve(int limit, Vector prime)
    {
        // Create a boolean array "mark[0..n-1]" and initialize
        // all entries of it as true. A value in mark[p] will
        // finally be false if 'p' is Not a prime, else true.
        boolean mark[] = new boolean[limit+1];
         
        for (int i = 0; i < mark.length; i++)
            mark[i] = true;
      
        for (int p=2; p*p prime = new Vector<>(); 
        simpleSieve(limit, prime);
      
        // Divide the range [0..n-1] in different segments
        // We have chosen segment size as sqrt(n).
        int low  = limit;
        int high = 2*limit;
      
        // While all segments of range [0..n-1] are not processed,
        // process one segment at a time
        while (low < n)
        {
            if (high >= n)
                high = n;
 
            // To mark primes in current range. A value in mark[i]
            // will finally be false if 'i-low' is Not a prime,
            // else true.
            boolean mark[] = new boolean[limit+1];
             
            for (int i = 0; i < mark.length; i++)
                mark[i] = true;
      
            // Use the found primes by simpleSieve() to find
            // primes in current range
            for (int i = 0; i < prime.size(); i++)
            {
                // Find the minimum number in [low..high] that is
                // a multiple of prime.get(i) (divisible by prime.get(i))
                // For example, if low is 31 and prime.get(i) is 3,
                // we start with 33.
                int loLim = (int) (floor(low/prime.get(i)) * prime.get(i));
                if (loLim < low)
                    loLim += prime.get(i);
      
                /*  Mark multiples of prime.get(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 (int j=loLim; j

Python3

# Python3 program to print all primes
# smaller than n, using segmented sieve
import math
prime = []
 
# This method finds all primes
# smaller than 'limit' using
# simple sieve of eratosthenes.
# It also stores found primes in list prime
def simpleSieve(limit):
     
    # Create a boolean list "mark[0..n-1]" and 
    # initialize all entries of it as True.
    # A value in mark[p] will finally be False
    # if 'p' is Not a prime, else True.
    mark = [True for i in range(limit + 1)]
    p = 2
    while (p * p <= limit):
         
        # If p is not changed, then it is a prime
        if (mark[p] == True):
             
            # Update all multiples of p
            for i in range(p * p, limit + 1, p):
                mark[i] = False 
        p += 1
         
    # Print all prime numbers
    # and store them in prime
    for p in range(2, limit):
        if mark[p]:
            prime.append(p)
            print(p,end = " ")
             
# Prints all prime numbers smaller than 'n'
def segmentedSieve(n):
     
    # Compute all primes smaller than or equal
    # to square root of n using simple sieve
    limit = int(math.floor(math.sqrt(n)) + 1)
    simpleSieve(limit)
     
    # Divide the range [0..n-1] in different segments
    # We have chosen segment size as sqrt(n).
    low = limit
    high = limit * 2
     
    # While all segments of range [0..n-1] are not processed,
    # process one segment at a time
    while low < n:
        if high >= n:
            high = n
             
        # To mark primes in current range. A value in mark[i]
        # will finally be False if 'i-low' is Not a prime,
        # else True.
        mark = [True for i in range(limit + 1)]
         
        # Use the found primes by simpleSieve()
        # to find primes in current range
        for i in range(len(prime)):
             
            # Find the minimum number in [low..high]
            # that is a multiple of prime[i]
            # (divisible by prime[i])
            # For example, if low is 31 and prime[i] is 3,
            # we start with 33.
            loLim = int(math.floor(low / prime[i]) *
                                         prime[i])
            if loLim < low:
                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. In this way we need to allocate space
            # only for range
            for j in range(loLim, high, prime[i]):
                mark[j - low] = False
                 
        # Numbers which are not marked as False are prime
        for i in range(low, high):
            if mark[i - low]:
                print(i, end = " ")
                 
        # Update low and high for next segment
        low = low + limit
        high = high + limit
 
# Driver Code
n = 100
print("Primes smaller than", n, ":")
segmentedSieve(100)
 
# This code is contributed by bhavyadeep

C#

// C# program to print print
// all primes smaller than
// n using segmented sieve
using System;
using System.Collections;
 
class GFG
{
    // This methid finds all primes
    // smaller than 'limit' using simple
    // sieve of eratosthenes. It also stores
    // found primes in vector prime[]
    static void simpleSieve(int limit,
                            ArrayList prime)
    {
        // Create a boolean array "mark[0..n-1]"
        // and initialize all entries of it as
        // true. A value in mark[p] will finally be
        // false if 'p' is Not a prime, else true.
        bool[] mark = new bool[limit + 1];
         
        for (int i = 0; i < mark.Length; i++)
            mark[i] = true;
     
        for (int p = 2; p * p < limit; p++)
        {
            // If p is not changed, then it is a prime
            if (mark[p] == true)
            {
                // Update all multiples of p
                for (int i = p * p; i < limit; i += p)
                    mark[i] = false;
            }
        }
     
        // Print all prime numbers and store them in prime
        for (int p = 2; p < limit; p++)
        {
            if (mark[p] == true)
            {
                prime.Add(p);
                Console.Write(p + " ");
            }
        }
    }
     
    // Prints all prime numbers smaller than 'n'
    static void segmentedSieve(int n)
    {
        // Compute all primes smaller than or equal
        // to square root of n using simple sieve
        int limit = (int) (Math.Floor(Math.Sqrt(n)) + 1);
        ArrayList prime = new ArrayList();
        simpleSieve(limit, prime);
     
        // Divide the range [0..n-1] in
        // different segments We have chosen
        // segment size as sqrt(n).
        int low = limit;
        int high = 2*limit;
     
        // While all segments of range
        // [0..n-1] are not processed,
        // process one segment at a time
        while (low < n)
        {
            if (high >= n)
                high = n;
 
            // To mark primes in current range.
            // A value in mark[i] will finally
            // be false if 'i-low' is Not a prime,
            // else true.
            bool[] mark = new bool[limit + 1];
             
            for (int i = 0; i < mark.Length; i++)
                mark[i] = true;
     
            // Use the found primes by
            // simpleSieve() to find
            // primes in current range
            for (int i = 0; i < prime.Count; i++)
            {
                // Find the minimum number in
                // [low..high] that is a multiple
                // of prime.get(i) (divisible by
                // prime.get(i)) For example,
                // if low is 31 and prime.get(i)
                //  is 3, we start with 33.
                int loLim = ((int)Math.Floor((double)(low /
                            (int)prime[i])) * (int)prime[i]);
                if (loLim < low)
                    loLim += (int)prime[i];
     
                /* Mark multiples of prime.get(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 (int j = loLim; j < high; j += (int)prime[i])
                    mark[j-low] = false;
            }
     
            // Numbers which are not marked as false are prime
            for (int i = low; i < high; i++)
                if (mark[i - low] == true)
                    Console.Write(i + " ");
     
            // Update low and high for next segment
            low = low + limit;
            high = high + limit;
        }
    }
     
    // Driver code
    static void Main()
    {
        int n = 100;
        Console.WriteLine("Primes smaller than " + n + ":");
        segmentedSieve(n);
    }
}
 
// This code is contributed by mits

输出:

Primes smaller than 100:
2 3 5 7 11 13 17 19 23 29 31 37 41
43 47 53 59 61 67 71 73 79 83 89 97