【问题标题】:Prime Generator Algorithm素数生成器算法
【发布时间】:2014-01-11 16:48:55
【问题描述】:

我一直在尝试解决素数生成算法的 SPOJ 问题。

问题来了

Peter 想为他的密码系统生成一些素数。帮助 他!你的任务是生成两个给定的所有素数 数字!

输入

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

输出

对于每个测试用例,打印所有质数 p,使得 m

很简单,但是网上的判断显示错误,我没有明白“测试用例”是什么意思,为什么需要使用1000000范围。

这是我的代码。

#include<stdio.h>

main()
{
int i, num1, num2, j;
int div = 0;
scanf("%d %d", &num1, &num2);
for(i=num1; i<=num2; i++)
  {
    for(j=1; j<=i; j++)
    {
      if(i%j == 0)
      {
        div++;
      }
    }
    if(div == 2)
    {
     printf("%d\n", i);
    }
    div = 0;
  }
  return 0;
}

【问题讨论】:

  • 阅读 CPPunit - 请参阅 sourceforge.net/apps/mediawiki/cppunit/…
  • 使用分段!
  • j 从 1 开始,所有数字 %1 都给 0... 那么你不必将所有数字都做到 i (sqrt(i ) 足够的)。然后你有很多算法在网上做这件事。
  • @ring0 是的,这是我的错误 sqrt(i) 就足够了,但是所有数字 %1 都给出 0,素数的定义,素数是可以被一和自身整除的数字,所以我有直到我达不到 sqrt(i) 为止。数字范围很小,因此数字将被快速处理,这没有问题。这个算法是我自己写的,这个算法比网上其他算法最简单
  • 正如我所看到的问题,Eratosthenes 的 seive 不适用于此数据。您必须使用分段的 seive。 sweet.ua.pt/tos/software/prime_sieve.html

标签: c algorithm primes


【解决方案1】:

我无法评论 alogirthm 以及 100000 数字范围是否允许优化,但您的代码无效的原因是因为它似乎没有正确解析输入。输入将类似于:

2
123123123 123173123 
987654321 987653321

即第一行将给出每行将获得的输入集的数量,然后是一组输入。乍一看,您的程序似乎只是在读取第一行以查找两个数字。

我假设在线法官只是在寻找正确的输出(可能还有合理的运行时间?),所以如果您纠正正确的输入,无论您的算法效率低下(正如其他人已经开始评论),它都应该有效.

【讨论】:

  • 没错,我认为这么小的数字范围优化不会有问题
  • 有人在意我为什么会在这个问题上被 -1 分吗?
  • 你得到了我的 +1,因为你回答了 OP 的问题。
【解决方案2】:

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

2 - //the number of test cases
1 10 - // numbers n,m
3 5 - // numbers

您的程序只能在第一行运行。

【讨论】:

    【解决方案3】:
    #include <stdio.h>
    #include <math.h>
    int main()
    {
        int test;
        scanf("%d",&test);
        while(test--)
        {
            unsigned int low,high,i=0,j=2,k,x=0,y=0,z;
            unsigned long int a[200000],b[200000];
            scanf("%d",&low);
            scanf("%d",&high);
            for(i=low;i<=high;i++)
                a[x++]=i;
            for(i=2;i<=32000;i++)
                b[y++]=i;
            i=0;
            while(b[i]*b[i]<=high)
            {
                if(b[i]!=0)
                {
                    k=i;
                    for(;k<y;k+=j)
                    {
                        if(k!=i)
                        {
                            b[k]=0;
                        }
                    }
                }
                i+=1;j+=1;
            }
                for(i=0;i<y;i++)
                {
                    if(b[i]!=0 && (b[i]>=low && b[i]<=sqrt(high)))
                        printf("%d\n",b[i]);
                }
                int c=0;
                for(i=0;i<y;i++)
                {
                    if(b[i]!=0 && (b[i]>=1 && b[i]<=sqrt(high)))
                        b[c++]=b[i];
                }
                int m=a[0];
                for(i=0;i<c;i++)
                {
                    z=(m/b[i])*b[i];k=z-m;
                    if(k!=0)
                        k += b[i];
                    for(;k<x;)
                    {
                        if(a[k]!=0)
                        {
                            a[k]=0;
                        }
                        k+=b[i];
                    }
                }
                for(i=0;i<x;i++)
                {
                    if(a[i]!=0 && (a[i]>=2 && a[i]<=(high)))
                        printf("%d\n",a[i]);
                }
            printf("\n");
        }
        return 0;
    }
    

    【讨论】:

    【解决方案4】:

    要找到m,n1 &lt;= m &lt;= n &lt;= 1000000000, n-m&lt;=100000 之间的素数,首先需要准备从2 到sqrt(1000000000) &lt; 32000 的核心素数。简单的连续sieve of Eratosthenes 就足够了。 (筛选出核心 bool sieve[] 数组 (a related C code is here),请创建一个单独的数组 int core_primes[] 包含核心素数,从筛数组中浓缩,以一种易于使用的形式,因为您有多个 offset 段可供它们筛选。)

    然后,对于每个给定的单独片段,只需使用准备好的核心素数对其进行筛选。 100,000 足够短,没有 evens 它只有 50,000 odds。您可以使用一个预先分配的数组并为每一对新的m,n 调整寻址方案。数组中的第 i 条目将表示数字 o + 2i 其中 o 是一个奇数 em> 给定段的开始。

    另见:

    关于术语的一句话:这不是“分段筛子”。这指的是对连续的片段进行筛选,一个接一个地更新核心素数列表。这里的上限是预先知道的,它的平方根非常小。

    相同的核心素数用于筛选每个单独的偏移段,因此可以更好地将其描述为埃拉托色尼的“偏移”筛。对于每个被筛分的片段,当然只需要使用不大于其上限平方根的核心素数;但是在筛选每个这样的偏移段时,核心素数更新(更新核心素数是“分段”筛网的签名特征)。

    【讨论】:

      【解决方案5】:

      对于如此小的数字,您可以简单地搜索 1 到 1000000000 之间的所有素数。

      使用 62.5 mByte 的 RAM 来创建一个二进制数组(每个奇数一个位,因为我们已经知道没有偶数(除了 2)是素数)。

      将所有位设置为 0 表示它们是素数,而不是使用 Sieve of Eratosthenes 将所有非素数的位设置为 1。

      筛选一次,存储得到的数字列表。

      【讨论】:

      • 我认为简单的 Eratosthenes 的 Seive 会超过给定问题的时间限制。
      • 根据我的经验,如果你想在一分钟内完成执行,筛子只适用于最大值低于约一千万的情况。我怀疑达到 10 亿需要更长的时间。
      • 您可以在知道 n 和 m 之前创建做 Eratosthenes 的筛子。因此,创建时间不是回答质数在 n 和 m 之间的问题所需时间的一部分。创建筛子可以离线完成(并且只需要完成一次,独立于所有 n,m 个请求)。
      • 筛选一次,存储得到的数字列表。
      • 使用模板元编程来产生数字,将问题转移到编译时间;-)。让我想起了我以前的老板在 PostScript 打印机上打印曼德布罗集;打印作业是几行 PostScript 计算集在打印机上
      【解决方案6】:
      int num; 
      bool singleArray[100000]; 
      static unsigned long allArray[1000000]; 
      unsigned long nums[10][2]; 
      
      unsigned long s;       
      long n1, n2;
      int count = 0; 
      long intermediate; 
      
      
      scanf("%d", &num);
      
      for(int i = 0; i < num; ++i) 
      {
          scanf("%lu", &n1);  
          scanf("%lu", &n2); 
          nums[i][0] = n1;   
          nums[i][1] = n2;   
      }
      
      
      for(int i = 0; i < 100000; ++i)  
      {
          singleArray[i] = true;
      }
      
      for(int i = 0; i < num; ++i) 
      {
          s = sqrt(nums[i][1]);
          for(unsigned long k = 2; k <= s; ++k)  
          {
              for (unsigned long j = nums[i][0]; j <= nums[i][1]; ++j) 
              {
                  intermediate = j - nums[i][0];
                  if(!singleArray[intermediate]) 
                  {
                      continue;
                  }
                  if((j % k == 0 && k != j) || (j == 1))      
                  {
                      singleArray[intermediate] = false;
                  }
              }
          }
      
      
          for(unsigned long m = nums[i][0]; m <= nums[i][1]; ++m) 
          {
              intermediate = m - nums[i][0];
              if(singleArray[intermediate])  
              {
                  allArray[count++] = m;
              }
          }
      
          for(int p = 0; p < (nums[i][1] - nums[i][0]); ++p)
          {
              singleArray[p] = true;
          }
       }
      
      
      for(int n = 0; n < count; ++n)
      {
          printf("%lu\n", allArray[n]);
      }
      

      }

      【讨论】:

        【解决方案7】:

        你的上限是 10^9。 Eratosthenes 的筛子是 O(N loglogN),对于这个界限来说太多了。

        这里有一些想法:

        更快的素性测试

        循环遍历范围 [i, j] 并检查每个数字是否为素数的简单解决方案的问题在于,它需要 O(sqrt(N)) 来测试一个数字是否为素数是素数,如果您处理多个案例,那就太多了。

        但是,您可以尝试更智能的素性测试算法。 Miller-RabinN的位数的多项式,对于N

        请注意,我实际上并没有尝试过,所以我不能保证它会起作用。

        分段筛

        正如@KaustavRay 提到的,您可以使用分段筛。基本思想是,如果一个数 N 是合数,那么它的质数除数最多为 sqrt(N)。

        我们使用埃拉托色尼筛算法找到低于 32,000 的素数(大约为 sqrt(10^9)),然后对范围 [i, j] 中的每个数检查是否有任何低于 32,000 的素数除以它。

        prime number theorem 看来,大约有一个 log(N) 数是质数,它小到可以挤进时间限制。

        【讨论】:

        • 这是不正确的。 SoE is perfectly capable 处理这个问题。事实上,我的一个提交在 SPOJ 运行了 0.01 秒。并且 Eratosthenes 的筛子不会测试任何部门,而是“试验部门”这样做,而且速度要慢得多。作为比较,我也尝试过(使用展开的 2-3 轮),它在 SPOJ 运行了 0.21 秒。此外,这也不是“分段筛子”,它是一个 偏移 筛子,因为我们在这里 依次筛选连续的片段。
        【解决方案8】:
        #include <iostream>
        using namespace std;
        
        int main() {
        
            // your code here
        unsigned long int m,n,i,j;int N;
        cin>>N;
        for(;N>0;N--)
        {
            cin>>m>>n;
            if(m<3)
                switch (n)
                {
                    case 1: cout<<endl;continue;
                    case 2: cout<<2<<endl;
                            continue;
                    default:cout<<2<<endl;m=3;
                }
            if(m%2==0) m++;        
            for(i=m;i<=n;i+=2)
            {  
                for(j=3;j<=i/j;j+=2)
                    if(i%j==0)
                        {j=0;break;}
                if(j)
                cout<<i<<endl;
            }
                cout<<endl;
        
        }return 0;}
        

        【讨论】:

        • 欢迎来到 SO。一个解释将大大改善这个答案。
        猜你喜欢
        • 2011-05-08
        • 1970-01-01
        • 2015-08-04
        • 1970-01-01
        • 2012-01-05
        • 2012-01-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多