【问题标题】:C - Segmented sieve losing prime numbersC - 丢失素数的分段筛
【发布时间】:2017-09-25 18:03:39
【问题描述】:
#include <stdio.h>
#include <math.h>

void sieve(unsigned int up, unsigned int low, unsigned char primes[]);

main()
{
    unsigned int low, up;
    unsigned int steps;
    scanf("%d",&steps);
    for (unsigned int i=0;i<steps;i++){
        scanf("%d %d",&low,&up);

        unsigned char v[up-low];
        sieve (up, low, v);
        for(unsigned int j=0; j<up-low+1; j++){
            if (v[j] == 1){
                printf("%d\n",low+j);
            }
        }   
    }
}
//-------------------------------------------------------------------
void sieve(unsigned int up, unsigned int low, unsigned char primes[])
{
    for (unsigned int i=0;i<up-low+1;i++){
        primes[i]=1;
    }

    for (unsigned int i=2;i<sqrt(up+1);i++) {
        for (int j=((low/i)*i)+i;j<up+1;j+=i){
                primes[j-low] = 0;


        }
    }
}

我正在尝试从特定范围内查找素数。我正在使用 Erastothenes 的分段筛,但不幸的是它丢失了一些素数,这是因为:

for (int j=((low/i)*i)+i;j<up+1;j+=i){
                    primes[j-low] = 0;

i 大于我的下限时,筛子函数开始用0 值标记素数,毕竟它们不在我的标准输出中。

例如标准输入:
1
2 1000
例如标准输出:
2
37 它会丢失 2 到 37 之间的所有素数
41
43
...

还有一件事,下限值总是被算法识别为质数。

例如标准输入:
4 10

例如标准输出:
4
5
7

我需要一些帮助来调整我的算法以正确标记这些数字,因为几个小时后我真的不知道我需要什么条件才能让它工作。

【问题讨论】:

  • for (unsigned int i=0;i&lt;up-low+1;i++){primes[i]=1;} 不匹配 unsigned char v[up-low];
  • 它是0或1个值的char数组,由上到下决定这个数组的大小,这里什么不匹配?
  • unsigned char v[up-low]; 的最后一个有效索引是up-low-1,但您正在循环中访问primes[up-low]
  • 发布的代码没有(干净地)编译。除此之外,main() 函数始终使用int 作为返回类型(无论 Visual Studio 接受什么
  • 函数:sieve() 包含signed intunsigned int 之间的几个“隐式”转换,您应该更正这些问题。编译时,始终启用所有警告,然后修复这些警告。

标签: c sieve-of-eratosthenes


【解决方案1】:

首先,您遇到了一个错误。您的数组不足以容纳所有元素的标志。让它变大:

unsigned char v[up-low+1];

现在主要问题出在你的循环中:

for (unsigned int i=2;i<sqrt(up+1);i++) {
    for (int j=((low/i)*i)+i;j<up+1;j+=i){
        primes[j-low] = 0;
    }
}

您没有从正确的索引开始。对于low==4,内部循环的第一次迭代将j 设置为((4/2)*2)+2 == (2*2)+2 == 4*2 == 6,因此您完全跳过了4。

保持简单。从i*2 开始j。在循环中跳过任何小于lowj 值:

for (unsigned int i=2;i<sqrt(up+1);i++) {
    for (unsigned int j=i*2;j<up+1;j+=i){
        if (j < low) continue;
        primes[j-low] = 0;
    }
}

【讨论】:

    【解决方案2】:

    您可以尝试分析我的分段筛的 c++ 解决方案,因为网上没有很多分段筛正确的实现。我希望它可以帮助您了解它是如何工作的。干杯!

    void sito_delta( int delta, std::vector<int> &res)
    {
    
    std::unique_ptr<int[]> results(new int[delta+1]);
    for(int i = 0; i <= delta; ++i)
        results[i] = 1;
    
    int pierw = sqrt(delta);
    for (int j = 2; j <= pierw; ++j)
    {
        if(results[j])
        {
            for (int k = 2*j; k <= delta; k+=j)
            {
                results[k]=0;
            }
        }
    }
    
    for (int m = 2; m <= delta; ++m)
        if (results[m])
        {
            res.push_back(m);
            std::cout<<","<<m;
        }
    };
    void sito_segment(int n,std::vector<int> &fiPri)
    {
    int delta = sqrt(n);
    
    if (delta>10)
    {
        sito_segment(delta,fiPri);
       // COmpute using fiPri as primes
       // n=n,prime = fiPri;
          std::vector<int> prime=fiPri;
          int offset = delta;
          int low = offset;
          int high = offset * 2;
          while (low < n)
          {
              if (high >=n ) high = n;
              int mark[offset+1];
              for (int s=0;s<=offset;++s)
                  mark[s]=1;
    
              for(int j = 0; j< prime.size(); ++j)
              {
                int lowMinimum = (low/prime[j]) * prime[j];
                if(lowMinimum < low)
                    lowMinimum += prime[j];
    
                for(int k = lowMinimum; k<=high;k+=prime[j])
                    mark[k-low]=0;
              }
    
              for(int i = low; i <= high; i++)
                  if(mark[i-low])
                  {
                    fiPri.push_back(i);
                    std::cout<<","<<i;
                  }
              low=low+offset;
              high=high+offset;
          }
    }
    else
    {
    
    std::vector<int> prime;
    sito_delta(delta, prime);
    //
    fiPri = prime;
    //
    int offset = delta;
    int low = offset;
    int high = offset * 2;
    // Process segments one by one 
    while (low < n)
    {
        if (high >= n) high = n;
        int  mark[offset+1];
        for (int s = 0; s <= offset; ++s)
            mark[s] = 1;
    
        for (int j = 0; j < prime.size(); ++j)
        {
            // find the minimum number in [low..high] that is
            // multiple of prime[i] (divisible by prime[j])
            int lowMinimum = (low/prime[j]) * prime[j];
            if(lowMinimum < low)
                lowMinimum += prime[j];
    
            //Mark multiples of prime[i] in [low..high]
            for (int k = lowMinimum; k <= high; k+=prime[j])
                mark[k-low] = 0;
        }
    
        for (int i = low; i <= high; i++)
            if(mark[i-low])
            {
                fiPri.push_back(i);
                std::cout<<","<<i;
            }
        low = low + offset;
        high = high + offset;
    }
    }
    };
    
    int main()
    {
    std::vector<int> fiPri;
    sito_segment(1013,fiPri);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-06
      • 2018-06-22
      • 2014-07-20
      • 2015-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-10
      相关资源
      最近更新 更多