簡體   English   中英

素數生成器算法

[英]Prime Generator Algorithm

我一直在嘗試解決質數生成器算法的 SPOJ 問題。

這是問題

彼得想為他的密碼系統生成一些質數。 幫助他! 您的任務是生成兩個給定數字之間的所有質數!

輸入

輸入以單行中的測試用例數 t (t<=10) 開始。 在接下來的每一行中,有兩個數字 m 和 n (1 <= m <= n <= 1000000000, nm<=100000) 用空格隔開。

輸出

對於每個測試用例打印所有質數 p 使得 m <= p <= n,每行一個數字,測試用例用空行分隔。

這很容易,但是在線法官顯示錯誤,我不明白“測試用例”的問題是什么意思以及為什么需要使用 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;
}

我無法評論算法以及 100000 數字范圍是否允許優化,但您的代碼無效的原因是它似乎沒有正確解析輸入。 輸入將類似於:

2
123123123 123173123 
987654321 987653321

那是第一行將給出每行將獲得的輸入組數,然后是一組輸入。 乍一看,您的程序似乎只是在讀取第一行以查找兩個數字。

我認為在線法官只是在尋找正確的輸出(可能還有合理的運行時間?)所以如果你糾正了正確的輸入,那么無論你的算法效率低下,它都應該工作(正如其他人已經開始評論)。

輸入以一行中的測試用例數 t (t<=10) 開始,您的程序中沒有測試用例。 它錯了 對不起我的英語

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

你的程序只能在第一行工作。

#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;
}

對於這么小的數字,您可以簡單地搜索 1 到 1000000000 之間的所有素數。

使用 62.5 mByte 的 RAM 創建一個二進制數組(每個奇數一位,因為我們已經知道沒有偶數(除了 2)是素數)。

將所有位設置為 0 以指示它們是素數,然后使用Eratosthenes 篩將所有不是素數的數字的位設置為 1。

做一次篩選,存儲結果的數字列表。

要找到m,n之間的素數m,n其中1 <= m <= n <= 1000000000, nm<=100000 ,您首先需要准備從 2 到sqrt(1000000000) < 32000的核心素數。 Eratosthenes 的簡單連續篩子對此綽綽有余 (篩選了核心bool sieve[]數組(一個相關的 C 代碼是here ),做一個單獨的數組int core_primes[]包含核心素數,從篩數組中濃縮,以一種易於使用的形式,因為你有更多比一個偏移段要由它們篩選。)

然后,對於每個給定的單獨片段,只需使用准備好的核心素數對其進行篩選。 100,000 足夠少,沒有偶數只有 50,000賠率 您可以使用一個預先分配的數組並為每一對新m,n調整尋址方案。 數組中的第i個條目將表示數字o + 2i ,其中o是給定段的奇數開始。

另見:

關於術語的一句話:這不是“分段篩”。 那是指對連續的段進行篩選,一個接一個地,隨着我們的進行更新核心素數列表。 這里的上限是預先知道的,它的平方根非常小。

相同的核心素數用於篩選每個單獨的偏移段,因此這可以更好地描述為 Eratosthenes 的“偏移”篩。 對於每個被篩選的片段,當然只需要使用不大於其上限平方根的核心素數; 但是在篩選每個這樣的偏移段時核心素數不會更新(更新核心素數是“分段”篩的簽名特征)。

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]);
}

}

你的上限是 10^9。 Eratosthenes 的篩子是O(N loglogN)這對於那個界限來說太多了。

這里有一些想法:

更快的素性測試

一個幼稚的解決方案的問題,你在范圍 [i, j] 上循環並檢查每個數字是否是素數是它需要O(sqrt(N))來測試一個數字是否是素數,如果你處理太多了幾種情況。

但是,您可以嘗試更智能的素性測試算法。 Miller-RabinN的位數上是多項式,對於 N <= 10^9,您只需檢查 a = 2、7 和 61。

請注意,我還沒有真正嘗試過這個,所以我不能保證它會起作用。

分段篩

正如@KaustavRay 提到的,您可以使用分段篩。 基本思想是,如果一個數 N 是合數,那么它的質數除數至多為 sqrt(N)。

我們使用 Eratosthenes 篩算法找出 32,000 以下的素數(大約是 sqrt(10^9)),然后對於 [i, j] 范圍內的每個數字,檢查是否有任何 32,000 以下的素數將其整除。

根據質數定理,log(N) 中的約 1 是質數,它小到足以擠在時間限制內。

#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;}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM