簡體   English   中英

計算並打印第 n 個質數

[英]Calculating and printing the nth prime number

我正在嘗試計算素數,我已經完成了。 但是我只想計算和打印第 n 個素數(用戶輸入),而計算其余的(它們不會被打印)只打印第 n 個素數。

這是我到目前為止所寫的內容:

import java.util.Scanner;
/**
 * Calculates the nth prime number
 * @author {Zyst}
 */
public class Prime {
    public static void main(String[] args) {

        Scanner input = new Scanner(System.in);
        int n, 
            i = 2, 
            x = 2;

        System.out.printf("This program calculates the nth Prime number\n");
        System.out.printf("Please enter the nth prime number you want to find: ");
        n = input.nextInt();

        for(i = 2, x = 2; i <= n; i++) {
            for(x = 2; x < i; x++) {
                if(i % x == 0) {
                    break;
                }
            }
            if(x == i) {
                System.out.printf("\n%d is prime", x);
            }
        }
    }
}

這是我編寫的用於計算從 1 到 n 的素數的程序。 但是,我希望它只打印第 n 個素數,

我想做的是在每次找到素數時進行某種 count int 和 ++ing,當 count == n 時它會打印出該數字,但我無法弄清楚如何着陸。

為了計算第 n 個素數,我知道兩個主要變體。

直截了當的方式

也就是說,當您找到它們時,從 2 開始計算所有素數,直到達到所需的 n th

這可以通過不同程度的復雜性和效率來完成,並且有兩種概念上不同的方法來做到這一點。 第一個是

按順序測試所有數字的素性

這將通過像這樣的驅動程序功能來完成

public static int nthPrime(int n) {
    int candidate, count;
    for(candidate = 2, count = 0; count < n; ++candidate) {
        if (isPrime(candidate)) {
            ++count;
        }
    }
    // The candidate has been incremented once after the count reached n
    return candidate-1;
}

而決定效率的有趣部分是isPrime函數。

給定素數的定義為一個大於 1 且只能被 1 及其自身整除的數,質數檢查的顯而易見的方法是我們在學校中學到的知識¹,是

審判分庭

將定義直接翻譯成代碼是

private static boolean isPrime(int n) {
    for(int i = 2; i < n; ++i) {
        if (n % i == 0) {
            // We are naive, but not stupid, if
            // the number has a divisor other
            // than 1 or itself, we return immediately.
            return false;
        }
    }
    return true;
}

但是,如果您嘗試一下,您很快就會發現,它的簡單性伴隨着緩慢。 通過該素性測試,您可以在幾毫秒內找到第 1000素數 7919(在我的計算機上大約為 20 個),但是找到第 10000素數 104729 需要幾秒鍾(~2.4 秒),第 100000素數,1299709 ,幾分鍾(大約 5),百萬分之一素數 15485863 需要大約八個半小時,百萬分之一素數 179424673,周,依此類推。 運行時復雜度比二次方差 - Θ(n² * log n)。

所以我們想稍微加快素性測試。 許多人采取的一個步驟是意識到n的除數( n本身除外)最多可以是n/2 如果我們使用這個事實,讓試除法循環只運行到n/2而不是n-1 ,算法的運行時間如何變化? 對於合數,循環下限不會改變任何東西。 對於素數,試除的次數減半,所以總體來說,運行時間應該減少一個比2小一些的因子。如果你嘗試一下,你會發現運行時間幾乎正好減半,所以幾乎所有的盡管復合數比素數多得多,但驗證素數的素性仍需花費時間

現在,如果我們想找到百萬分之一的素數,那沒有多大幫助,所以我們必須做得更好。 嘗試進一步減少循環限制,讓我們看看實際需要n/2的上限的數字是多少。 如果n/2是的除數n ,然后n/2是整數,換句話說, n是由2整除但隨后的循環不晃過2,所以它永遠不會(除了n = 4 )達到n/2 太好了,那么n的下一個最大可能除數是多少? 為什么,當然是n/3 但是,如果n/3是整數,則只能是n的除數,換句話說,如果n可以被 3 整除。那么循環將在 3(或之前,在 2)處退出並且永遠不會到達n/3 (除了對於n = 9 )。 下一個最大可能的除數......

等一下! 我們有2 <-> n/23 <-> n/3 n 的除數成對出現。

如果我們考慮到一對(d, n/d)的相應的除數n ,或者d = n/d ,即d = √n ,或它們中的一個,比方說d ,比其他小。 但是然后d*d < d*(n/d) = n並且d < √n n每對相應的除數包含(至少)一個不超過√n

如果n是合數,則其最小非平凡除數不超過√n

所以我們可以將循環限制減少到√n ,從而降低算法的運行時復雜度。 它現在應該是 Θ(n 1.5 * √(log n)),但從經驗上看,它的比例似乎要好一點——但是,沒有足夠的數據從經驗結果中得出可靠的結論。

這將在大約 16 秒內找到百萬分之一,在不到 9 分鍾內找到百萬分之一,並且將在大約四個半小時內找到一百萬分之一。 那還是很慢的,但與天真的試煉師十年左右的時間相差甚遠。

由於存在素數的平方和兩個相近素數的乘積,例如 323 = 17*19,我們無法將試除循環的限制降低到√n以下。 因此,在保持試驗划分的同時,我們現在必須尋找其他方法來改進算法。

一個很容易看到的事情是,除了 2 之外沒有質數是偶數,所以我們只需要在處理 2 之后檢查奇數。不過,這沒有太大區別,因為偶數是最便宜的復合 - 而且大部分時間仍然花在驗證素數的素數上。 但是,如果我們將偶數視為候選除數,我們會看到如果n可被偶數整除,則n本身必須是偶數,因此(除了 2)它在被任何更大的偶數除之前將被識別為合數比 2 嘗試。 因此,算法中發生的所有被大於 2 的偶數除法必須留下非零余數。 因此,我們可以省略這些除法,只檢查 2 和從 3 到√n的奇數的可√n 這使確定一個數為素數或合數所需的除法次數減半(不完全是),因此運行時間減半。 這是一個好的開始,但我們能做得更好嗎?

另一個大家族是 3 的倍數。 我們執行的每三次除法都是 3 的倍數,但如果n可以被其中一個整除,它也可以被 3 整除,因此不能被 9、15、21 整除, ... 我們在算法中執行的操作將永遠留下 0 的余數。那么,我們如何跳過這些除法呢? 好吧,既不能被 2 也不能被 3 整除的數正是6*k ± 1形式的數。 從 5 開始(因為我們只對大於 1 的數字感興趣),它們是 5, 7, 11, 13, 17, 19, ...,從一個到下一個的步驟在 2 和 4 之間交替,也就是很簡單,所以我們可以使用

private static boolean isPrime(int n) {
    if (n % 2 == 0) return n == 2;
    if (n % 3 == 0) return n == 3;
    int step = 4, m = (int)Math.sqrt(n) + 1;
    for(int i = 5; i < m; step = 6-step, i += step) {
        if (n % i == 0) {
            return false;
        }
    }
    return true;
}

這使我們再次加速(接近)1.5 倍,因此我們需要大約一個半小時才能達到第一億個素數。

如果我們繼續這條路線,下一步就是消去 5 的倍數。 與 2、3 和 5 互質的數是以下形式的數

30*k + 1, 30*k + 7, 30*k + 11, 30*k + 13, 30*k + 17, 30*k + 19, 30*k + 23, 30*k + 29

所以我們只需要每 30 個數字除以 8(加上三個最小的素數)。 從一個到下一個的步驟,從 7 開始,循環到 4, 2, 4, 2, 4, 6, 2, 6. 這仍然很容易實現並產生另一個 1.25 倍的加速(減去一點更復雜的代碼)。 更進一步,將消除 7 的倍數,每 210 個數字中留下 48 個要除以,然后是 11 (480/2310)、13 (5760/30030) 等等。 每個素數被消除的素數p產生(幾乎) p/(p-1)的加速,因此回報減少而成本(代碼復雜性,步驟查找表的空間)隨着每個素數增加。

一般來說,在消除可能六七個素數(甚至更少)的倍數之后,很快就會停止。 然而,在這里,我們可以堅持到最后,當所有素數的倍數都被消除時,只剩下素數作為候選除數。 由於我們正在按順序查找所有素數,因此在需要將其作為候選除數之前找到每個素數,然后可以將其存儲以備將來使用。 這將算法復雜性降低到 - 如果我沒有錯誤計算 - O(n 1.5 / √(log n))。 以存儲素數的空間使用為代價。

隨着審判庭,這是盡善盡美,你必須嘗試所有素數鴻溝√n或第一分n確定的素性n 這在大約半小時內找到了第一億個素數。

那么怎么樣

快速素性測試

除了不存在合數通常沒有的非平凡除數之外,素數還有其他數論性質。 如果這些屬性可以快速檢查,則可以構成概率或確定性素性測試的基礎。 該原型財產與皮埃爾·德·費馬,誰,在17世紀初,發現的名稱相關聯

如果p是一個素數,則p是(為p-A)為所有的除數a

這 - 費馬所謂的“小定理” - 在等價公式中

p是一個素數及a不整除p 然后p除以p-1 - 1。

大多數廣泛使用的快速素性檢驗的基礎(例如 Miller-Rabin)及其變體或類似物出現在更多(例如 Lucas-Selfridge)中。

因此,如果我們想知道一個不太小的奇數n是否是素數(偶數和小數可以通過試除有效地處理),我們可以選擇任何不是n倍數的數a (> 1),例如2,並檢查n是否除以n-1 - 1。由於n-1變得很大,最有效的方法是檢查是否a^(n-1) ≡ 1 (mod n) ,即通過模冪運算。 如果該同余不成立,我們就知道n是合數。 但是,如果它成立,我們不能得出結論n是素數,例如2^340 ≡ 1 (mod 341) ,但341 = 11 * 31是合數。 復合數n使得a^(n-1) ≡ 1 (mod n)被稱為基a費馬偽素數。

但這種情況很少見。 給定任何基數a > 1 ,雖然有無數費馬偽素數作為基數a ,但它們比實際素數少得多。 例如,100000以下的2鹼基費馬偽素數只有78個,3鹼基費馬偽素數有76個,但有9592個素數。 因此,如果選擇任意奇數n > 1和任意底數a > 1並找到a^(n-1) ≡ 1 (mod n) ,則n很有可能實際上是素數。

但是,我們的情況略有不同,給定了n並且只能選擇a 那么,對於奇數復合n ,對於多少a1 < a < n-1可以a^(n-1) ≡ 1 (mod n)成立? 不幸的是,合數-卡邁克爾數-使得一致性適用於a互質n 這意味着要使用費馬測試將卡邁克爾數識別為復合數,我們必須選擇一個底數,該底數是n的素數除數之一的倍數 - 可能沒有很多這樣的倍數。

但是我們可以加強費馬測試,以便更可靠地檢測復合材料。 如果p是奇素數,則寫p-1 = 2*m 然后,如果0 < a < p

a^(p-1) - 1 = (a^m + 1) * (a^m - 1)

p正好整除這兩個因數之一(這兩個因數相差 2,因此它們的最大公約數是 1 或 2)。 如果m是偶數,我們可以用同樣的方式拆分a^m - 1 繼續,如果p-1 = 2^s * kk奇數,則寫

a^(p-1) - 1 = (a^(2^(s-1)*k) + 1) * (a^(2^(s-2)*k) + 1) * ... * (a^k + 1) * (a^k - 1)

然后p正好除以因子之一。 這產生了強費馬測試,

n > 2為奇數。 k奇數寫n-1 = 2^s * k 給定任何a 1 < a < n-1 ,如果

  1. a^k ≡ 1 (mod n)
  2. a^((2^j)*k) ≡ -1 (mod n)為任何j0 <= j < s

然后n為鹼基的強(費馬)可能素a 一種復合強鹼a (費馬)可能素被稱為對於基部的強(費馬)偽素a 強費馬偽素數比普通費馬偽素數還要稀少,1000000以下,有78498個素數,245個鹼基2費馬偽素數,2鹼基強費馬偽素數只有46個。 更重要的是,對於任何奇數復合n ,最多有(n-9)/4鹼基1 < a < n-1其中n是強費馬偽素數。

因此,如果n是奇數組合,則n通過k強費馬測試的概率小於1/4^k其中隨機選擇的基數介於 1 和n-1 (互斥邊界)。

強費馬測試需要 O(log n) 步,每一步都涉及 O(log n) 位數字的一兩次乘法,因此復雜度為 O((log n)^3) 與朴素乘法 [對於巨大的n ,更復雜的乘法算法可能是值得的]。

Miller-Rabin 檢驗是隨機選擇鹼基的 k 折強費馬檢驗。 這是一個概率測試,但對於足夠小的邊界,已知的短鹼基組合會給出確定性結果。

強費馬測試是確定性 APRCL 測試的一部分。

建議在這些測試之前先用前幾個小素數進行試除,因為除法相對便宜,而且可以剔除大多數復合材料。

對於尋找第n素數的問題,在測試所有數的素數可行的范圍內,存在使多重強費馬測試正確的已知鹼基組合,這樣會給出更快的 - O(n*(log n) 4 ) - 算法。

對於n < 2^32 ,基數 2、7 和 61 足以驗證素性。 使用它,可以在大約六分鍾內找到第 100 萬個素數。

用素數除數消除復合,埃拉托色尼篩法

也可以將一整套相關數字視為一個整體,一次性消除給定素數的倍數,而不是按順序調查數字並從頭開始檢查每個數字是否為素數。 這被稱為埃拉托色尼篩法:

求不超過N的素數

  1. 列出從 2 到N的所有數字
  2. 對於從 2 到N每個k :如果k尚未划掉,則它是素數; 划掉所有k倍數作為復合

素數是列表中沒有划掉的數字。

該算法與試除法有着根本的不同,盡管兩者都直接使用質數的可分性表征,而費馬檢驗和使用質數其他性質的類似檢驗則相反。

在試除法中,每個數n與不超過√nn的最小素數除數中的較小者的所有素數配對。 由於大多數復合物的質數除數非常小,因此平均而言,檢測復合物的成本很低。 但是測試素數是很昂貴的,因為√n以下的素數相對較多。 盡管復合數比素數多得多,但測試素數的成本如此之高,以至於它完全支配了整個運行時間,並使試除法成為一種相對較慢的算法。 對所有小於N數字進行試除需要 O(N 1.5 / (log N)²) 步。

在篩子中,每個復合n與其所有的質因數配對,但與那些質因數配對。 因此,質數是便宜的數字,它們只看一次,而復合數更昂貴,它們被多次划掉。 人們可能會認為,由於篩子包含的“昂貴”數字比“便宜”數字要多得多,因此總體而言,這將是一種糟糕的算法。 然而,合數沒有許多相異素因子-的相異素因子的數量n為界log n ,但通常小得多,平均的數字的不同的素因子的數量的<= nlog log n - 因此,即使篩子中的“昂貴”數字平均也不會比試除的“便宜”數字貴(或幾乎不貴)。

最多篩選N ,對於每個素數p ,有Θ(N/p)倍數要交叉,因此交叉的總數為Θ(∑ (N/p)) = Θ(N * log (log N)) 與使用更快的素性測試的試除或順序測試相比,這產生更快的算法來查找最多N的素數。

然而,sieve 有一個缺點,它使用O(N)內存。 (但是使用分段篩,可以在不增加時間復雜度的情況下將其減少到O(√N) 。)

為了找到第n素數,而不是直到N的素數,還有一個問題是事先不知道篩子應該到達多遠。

后者可以使用素數定理解決。 PNT 說

π(x) ~ x/log x (equivalently: lim π(x)*log x/x = 1),

其中π(x)是不超過x的素數的數量(此處和以下, log必須是自然對數,對於算法復雜性,為對數選擇哪個底並不重要)。 由此可知, p(n) ~ n*log n ,其中p(n)是第n素數,並且從更深入的分析中得知p(n)有很好的上限,特別是

n*(log n + log (log n) - 1) < p(n) < n*(log n + log (log n)), for n >= 6.

所以可以把它作為篩分極限,它不會超出目標太遠。

O(N)空間需求可以通過使用分段篩來克服。 然后,可以記錄√N以下的素數以√N O(√N / log N)內存,並在篩子接近 N 時使用長度增加的段 (O(√N))。

如上所述,算法有一些簡單的改進:

  1. 僅在處開始 p倍數,而不是在2*p
  2. 從篩子中消除偶數
  3. 從篩子中消除更多小素數的倍數

這些都不降低算法復雜性,但它們都減少由顯著量常數因子(如用試除法,的倍數的消除p產率較小的加速比為較大的p同時增加了代碼的復雜性超過較小p )。

使用前兩個改進產量

// Entry k in the array represents the number 2*k+3, so we have to do
// a bit of arithmetic to get the indices right.
public static int nthPrime(int n) {
    if (n < 2) return 2;
    if (n == 2) return 3;
    int limit, root, count = 1;
    limit = (int)(n*(Math.log(n) + Math.log(Math.log(n)))) + 3;
    root = (int)Math.sqrt(limit) + 1;
    limit = (limit-1)/2;
    root = root/2 - 1;
    boolean[] sieve = new boolean[limit];
    for(int i = 0; i < root; ++i) {
        if (!sieve[i]) {
            ++count;
            for(int j = 2*i*(i+3)+3, p = 2*i+3; j < limit; j += p) {
                sieve[j] = true;
            }
        }
    }
    int p;
    for(p = root; count < n; ++p) {
        if (!sieve[p]) {
            ++count;
        }
    }
    return 2*p+1;
}

它在大約 18 秒內找到了第一億個素數 2038074743。 通過存儲打包的標志,每個標志一位,而不是boolean ,這個時間可以減少到大約 15 秒(這里是 YMMV),因為減少的內存使用量提供了更好的緩存局部性。

打包標志,消除 3 的倍數並使用位旋轉來更快地計數,

// Count number of set bits in an int
public static int popCount(int n) {
    n -= (n >>> 1) & 0x55555555;
    n = ((n >>> 2) & 0x33333333) + (n & 0x33333333);
    n = ((n >> 4) & 0x0F0F0F0F) + (n & 0x0F0F0F0F);
    return (n * 0x01010101) >> 24;
}

// Speed up counting by counting the primes per
// array slot and not individually. This yields
// another factor of about 1.24 or so.
public static int nthPrime(int n) {
    if (n < 2) return 2;
    if (n == 2) return 3;
    if (n == 3) return 5;
    int limit, root, count = 2;
    limit = (int)(n*(Math.log(n) + Math.log(Math.log(n)))) + 3;
    root = (int)Math.sqrt(limit);
    switch(limit%6) {
        case 0:
            limit = 2*(limit/6) - 1;
            break;
        case 5:
            limit = 2*(limit/6) + 1;
            break;
        default:
            limit = 2*(limit/6);
    }
    switch(root%6) {
        case 0:
            root = 2*(root/6) - 1;
            break;
        case 5:
            root = 2*(root/6) + 1;
            break;
        default:
            root = 2*(root/6);
    }
    int dim = (limit+31) >> 5;
    int[] sieve = new int[dim];
    for(int i = 0; i < root; ++i) {
        if ((sieve[i >> 5] & (1 << (i&31))) == 0) {
            int start, s1, s2;
            if ((i & 1) == 1) {
                start = i*(3*i+8)+4;
                s1 = 4*i+5;
                s2 = 2*i+3;
            } else {
                start = i*(3*i+10)+7;
                s1 = 2*i+3;
                s2 = 4*i+7;
            }
            for(int j = start; j < limit; j += s2) {
                sieve[j >> 5] |= 1 << (j&31);
                j += s1;
                if (j >= limit) break;
                sieve[j >> 5] |= 1 << (j&31);
            }
        }
    }
    int i;
    for(i = 0; count < n; ++i) {
        count += popCount(~sieve[i]);
    }
    --i;
    int mask = ~sieve[i];
    int p;
    for(p = 31; count >= n; --p) {
        count -= (mask >> p) & 1;
    }
    return 3*(p+(i<<5))+7+(p&1);
}

在大約 9 秒內找到第 100 萬個質數,這並不是無法忍受的長。

還有其他類型的素數篩,特別令人感興趣的是阿特金篩,它利用了這樣一個事實,即(有理)素數的某些同余類是 ℚ 的某些二次擴展的代數整數環中的復合物。 這里不是擴展數學理論的地方,只需說阿特金篩比埃拉托色尼篩具有更低的算法復雜性,因此更適合大限制(對於小限制,沒有過度優化的阿特金篩具有更高的算法復雜度)開銷,因此可能比相對優化的 Eratosthenes 篩慢)。 DJ Bernstein 的primegen庫(用 C 編寫)針對 2 32以下的數字進行了很好的優化,並在大約 1.1 秒內找到了第 100 萬個素數(此處)。

快捷方式

如果我們只想找到第n素數,那么找出所有較小的素數也沒有內在價值。 如果我們可以跳過其中的大部分,我們可以節省大量的時間和工作。 給定一個很好的近似a(n)到第n素數p(n) ,如果我們有一個快速的方法來計算不超過a(n)的素數π(a(n))的數量,那么我們可以篩選一個小的范圍高於或低於a(n)以識別a(n)p(n)之間的少數缺失或多余素數。

我們已經看到了上面p(n)一個很容易計算的相當好的近似值,我們可以取

a(n) = n*(log n + log (log n))

例如。

計算π(x)一個好方法是Meissel-Lehmer 方法,它在大約O(x^0.7)時間內計算π(x) (確切的復雜性取決於實現,由 Lagarias、Miller、Odlyzko、Deléglise 改進並且 Rivat 可以在 O(x 2/3 / log² x) 時間內計算π(x) )。

從簡單的近似a(n) ,我們計算e(n) = π(a(n)) - n 根據素數定理, a(n)附近的素數密度約為1/log a(n) ,因此我們期望p(n)接近b(n) = a(n) - log a(n)*e(n) ,我們會篩選一個比log a(n)*e(n)稍大的范圍。 為了讓p(n)在篩選范圍內有更大的信心,可以將范圍增加 2 倍,例如,這幾乎肯定會足夠大。 如果范圍似乎過大,一個可以遍歷與更好的近似b(n)代替a(n)計算π(b(n))f(n) = π((b(n)) - n . 通常, |f(n)|會比|e(n)|小得多。如果f(n)大約是-e(n) ,則c(n) = (a(n) + b(n)) / 2將是p(n)的更好近似值。只有在f(n)非常接近e(n) (而不是非常接近 0)的極不可能的情況下,才能找到對p(n) ) 的足夠好的近似值p(n)最終篩分階段可以在與計算π(a(n))相當的時間內完成成為一個問題。

一般情況下,對初始近似進行一兩次改進后,要篩選的范圍足夠小,篩選階段的復雜度為 O(n^0.75) 或更好。

此方法在大約 40 毫秒內找到第 100 萬個素數,在不到 8 秒內找到第 10個 12個素數,29996224275833。


tl;dr:可以有效地找到第n素數,但是您想要的效率越高,涉及的數學就越多。


我為這里討論的大多數算法准備Java 代碼,以防有人想玩弄它們。


¹ 除了對過分感興趣的靈魂的評論:現代數學中使用的素數的定義是不同的,適用於更一般的情況。 如果我們修改學校的定義以包含負數——所以如果一個數既不是 1 也不是 -1 並且只能被 1、-1、它本身和它的負數整除,那么它就是素數——這就定義了(對於整數)現在稱為不可約元素的東西的ℤ,然而,對於整數,素數和不可約元素的定義是一致的。

int counter = 0;

for(int i = 1; ; i++) {
    if(isPrime(i)
        counter++;

    if(counter == userInput) {
        print(i);
        break;
    }
}

編輯:您的主要功能可能需要一些工作。 這是我寫的一篇:

private static boolean isPrime(long n) {
    if(n < 2)
        return false;

    for (long i = 2; i * i <= n; i++) {
        if (n % i == 0)
            return false;
    }
    return true;
}

注意 - 在查看因子時,您只需要上升到 sqrt(n),因此i * i <= n

您試圖在 main 方法中做太多事情。 您需要將其分解為更易於管理的部分。 編寫一個boolean isPrime(int n) ,如果數字是素數則返回真,否則返回假。 然后修改main方法使用isPrime。

java.math.BigInteger 有一個 nextProbablePrime() 方法。 雖然我猜這適用於密碼學,但您可以將它用於您的工作。

BigInteger prime = BigInteger.valueOf(0);
for (int i = 0; i < n; i++) {
    prime = prime.nextProbablePrime();
}
System.out.println(prime.intValue());

我只是在您自己的思考過程中添加了缺失的線條。

static int nthPrimeFinder(int n) {

        int counter = 1; // For 1 and 2. assuming n is not 1 or 2.
        int i = 2;
        int x = 2;
        int tempLength = n;

        while (counter <= n) {
            for (; i <= tempLength; i++) {
                for (x = 2; x < i; x++) {
                    if (i % x == 0) {
                        break;
                    }
                }
                if (x == i && counter < n) {
                    //System.out.printf("\n%d is prime", x);
                    counter++;
                    if (counter == n) {
                        System.out.printf("\n%d is prime", x);
                        return counter;
                    }
                }
            }
            tempLength = tempLength+n;
        }
        return 0;
    }
public class prime{
    public static void main(String ar[])
    {
      int count;
      int no=0;
      for(int i=0;i<1000;i++){
        count=0;
        for(int j=1;j<=i;j++){

        if(i%j==0){
          count++;
         }
        }
        if(count==2){
          no++;
          if(no==Integer.parseInt(ar[0])){
            System.out.println(no+"\t"+i+"\t") ;
          }
        }
      }
    }
}

我可以看到您收到了許多正確的答案,而且非常詳細。 我相信你不是在測試非常大的素數。 你唯一關心的是避免你的程序打印中間質數。

對您的程序稍作更改即可解決問題。

保持你的邏輯相同,只需在循環之外拉出打印語句。 在 n 個素數后中斷外循環。

import java.util.Scanner;
/**
 * Calculates the nth prime number
 * @author {Zyst}
 */
public class Prime {
    public static void main(String[] args) {

        Scanner input = new Scanner(System.in);
        int n, 
            i = 2, 
            x = 2;

        System.out.printf("This program calculates the nth Prime number\n");
        System.out.printf("Please enter the nth prime number you want to find:");
        n = input.nextInt();

        for(i = 2, x = 2; n > 0; i++) {
            for(x = 2; x < i; x++) {
                if(i % x == 0) {
                    break;
                }
            }
            if(x == i) {
                n--;
            }
        }
        System.out.printf("\n%d is prime", x);

    }
}

雖然有許多正確和詳細的解釋。 但這是我的 C 實現:

#include<stdio.h>
#include<conio.h> 

main()
{
int pk,qd,am,no,c=0;
printf("\n Enter the Number U want to Find");
scanf("%d",&no);
for(pk=2;pk<=1000;pk++)
{
am=0;
for(qd=2;qd<=pk/2;qd++)
{
if(pk%qd==0)
{
am=1;
break;
}}
if(am==0)
c++;
if(c==no)
{
printf("%d",pk);
break;
}}
getch();
return 0;
}

這個程序是一個高效的程序。 如果要獲得數字的平方根,我又添加了一個簽入,並檢查它是否可整除,如果它不是質數。 這將有效地解決所有問題。

public static void main(String[] args) {

            Scanner sc = new Scanner(System.in);
        int T; // number of test cases
        T = sc.nextInt();
        long[] number = new long[T];
        if(1<= T && T <= 30){
        for(int i =0;i<T;i++){
            number[i]=sc.nextInt(); // read all the numbers
        }
        for(int i =0;i<T;i++){
            if(isPrime(number[i]))
                System.out.println("Prime");
            else
               System.out.println("Not prime");    
        }
    }
    else
      return;
    }
    // is prime or not
    static boolean isPrime(long num){
        if(num==1)
          return false;
        if(num <= 3)
          return true;
        if(num % 2 == 0 || num % 3 == 0 || num % (int)Math.sqrt(num) == 0)
          return false;  
        for(int i=4;i<(int)Math.sqrt(num);i++){
            if(num%i==0)
              return false;
        }
       return true;     
    }

另一種解決方案

import java.util.Scanner;

public class Prime {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);

        int[] arr = new int[10000000];
        for(int i=2;i<10000000;i++)
        {
            arr[i]=i;
        }
        for(int i=2;i<10000000;i++)
            for(int j=i+i;j<10000000;j+=i)
                arr[j]=0;

        int t = in.nextInt();
        for(int a0 = 0; a0 < t; a0++){
            int n = in.nextInt();
            int count=0;
            for(int j=2;j<10000000;j++)
            {
                if(arr[j]!=0)
                {
                    count++;
                    if(count==n)
                    {
                        System.out.println(j);
                        break;
                    }
                }
            }
        }
    }
}

希望這將有助於更大的數字...

使用 Java 8 parallelStream 會更快。 下面是我查找第 N 個素數的代碼

public static Integer findNthPrimeNumber(Integer nthNumber) {
    List<Integer> primeList = new ArrayList<>();
    primeList.addAll(Arrays.asList(2, 3));
    Integer initializer = 4;
    while (primeList.size() < nthNumber) {
        if (isPrime(initializer, primeList)) {
            primeList.add(initializer);
        }
        initializer++;
    }
    return primeList.get(primeList.size() - 1);
}

public static Boolean isPrime(Integer input, List<Integer> primeList) {
    return !(primeList.parallelStream().anyMatch(i -> input % i == 0));
}

@Test
public void findNthPrimeTest() {
    Problem7 inputObj = new Problem7();
    Integer methodOutput = inputObj.findNthPrimeNumber(100);
    Assert.assertEquals((Integer) 541, methodOutput);
    Assert.assertEquals((Integer) 104743, inputObj.findNthPrimeNumber(10001));
}

暫無
暫無

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

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