[英]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/2
和3 <-> 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
,對於多少a
, 1 < 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 * k
且k
奇數,則寫
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
,如果
a^k ≡ 1 (mod n)
或a^((2^j)*k) ≡ -1 (mod n)
為任何j
與0 <= 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
的素數
N
的所有數字N
每個k
:如果k
尚未划掉,則它是素數; 划掉所有k
倍數作為復合素數是列表中沒有划掉的數字。
該算法與試除法有着根本的不同,盡管兩者都直接使用質數的可分性表征,而費馬檢驗和使用質數其他性質的類似檢驗則相反。
在試除法中,每個數n
與不超過√n
和n
的最小素數除數中的較小者的所有素數配對。 由於大多數復合物的質數除數非常小,因此平均而言,檢測復合物的成本很低。 但是測試素數是很昂貴的,因為√n
以下的素數相對較多。 盡管復合數比素數多得多,但測試素數的成本如此之高,以至於它完全支配了整個運行時間,並使試除法成為一種相對較慢的算法。 對所有小於N
數字進行試除需要 O(N 1.5 / (log N)²) 步。
在篩子中,每個復合n
與其所有的質因數配對,但僅與那些質因數配對。 因此,質數是便宜的數字,它們只看一次,而復合數更昂貴,它們被多次划掉。 人們可能會認為,由於篩子包含的“昂貴”數字比“便宜”數字要多得多,因此總體而言,這將是一種糟糕的算法。 然而,合數沒有許多相異素因子-的相異素因子的數量n
為界log n
,但通常要小得多,平均的數字的不同的素因子的數量的<= n
是log 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))。
如上所述,算法有一些簡單的改進:
p²
處開始p²
p
倍數,而不是在2*p
這些都不降低算法復雜性,但它們都減少由顯著量常數因子(如用試除法,的倍數的消除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.