[Raku] 如何判斷一個數值是否為精緻質數
作者:gugod 發佈於: ,更新於: #rakulang所謂精緻質數,指的是在質數中一個子集合。如果有個數 $n
為精緻質數,那表示若將此數字的任一位數替換為 0..9 中的任一其他數字,所得到的新數字就不是質數了。
比方說 43
,將任一位數替換後可得這幾個數所構成的集合:
40, 41, 42, 44, 45, 46, 47, 48, 49, 3, 13, 23, 33, 53, 63, 73, 83, 93
不過,在此集合中 41, 47, 3, 13, 23, 53, 73, 83 這幾個數字都是質數。所以 43
並不是精緻質數。
同理, 41, 47, 3, 13, 23, 53, 73, 83 這幾個質數也都不是精緻質數。
依照其定義,要檢查某數 $x
是否為精緻質數,必須檢查由其生成的眾多其他數。若 $x
為 n 位數字,則需檢查 9n 個其他數。若其他 9n 個數都不是質數的話,則 $x
變為精緻質數。
既然有了這基本想法,以下的 is-delicate-prime
判別函式也就隨之而生了。
sub is-delicate-prime (Int $x --> Bool) {
# 如果 $x 不是質數,自然也就不是精緻質數
return False unless $x.is-prime;
# 由 $x 生成其他 9n 個相關的數,存於 @alt_x 內
my @digits = $x.Str.flip.comb;
my @alt_x = ( @digits.keys X (0..9) )
.grep(-> ($i, $d) { @digits[$i] != $d })
.map(-> ($i, $d) { $x + ($d - @digits[$i]) * 10 ** $i });
# 若 @alt_x 內的數字都不是質數,那麼 $x 就是精緻質數
return @alt_x.map(&is-prime).none.so;
}
既然有了判別函數,那麼就可以直接配合 .grep
來做產生器了。比方說可以用如下幾行程式碼來將前 10 個精緻質數印出:
my @delicate-primes = (2..*).hyper.grep(&is-delicate-prime).[^10];
say @delicate-primes;
#=> [294001 505447 584141 604171 971767 1062599 1282529 1524181 2017963 2474431]
精緻質數在自然數中所佔的比例很小,單純用普通的串列配上 .grep
來計算的話多半要讓人等上好一陣子才會有點進展。因此在此使用了 .hyper.grep
,讓 raku 自動將運算分散到所有 CPU 上去。
.hyper
大致上來說可加在 .map
前,讓 raku 自動生出夠多執行緒來進行 .map
內的運算。但如果要做的運算過於細小,反而可能會在情景切換與結果合併的階段耗掉許多時間。又、如果運算內容實際上是要彼此共享一些資訊的話,或許會適得其反。使用時機需要好好斟酌。