2018年12月の時点で「素数として確認された最大の数」は 282,589,933 − 1 である。この素数は24,862,048 桁の長さを持ち、2018年12月に Great Internet Mersenne Prime Search (GIMPS) によって発見された。
素数?そ~すぅね...はい、どうもボクです。というわけで、今回は、Java のお話です。
素数を求めるプログラム
PHPで、求めてる人がいらっしゃいました。
んじゃぁ、Javaでやってみようってことで、Javaの標準APIである、BigIntegerクラスに、nextProbablePrime() ってメソッドがあるんですな。
BigInteger java.math.BigInteger.nextProbablePrime()
nextProbablePrime
public BigInteger nextProbablePrime()
このBigIntegerより大きい最初の整数(おそらく素数)を返します。
このメソッドの返す数が合成数である確率は2-100を超えません。このメソッドは検索時に素数をスキップしません。
pを返す場合、this < q < pが成り立つような素数qはありません。
戻り値:このBigIntegerより大きい最初の整数(おそらく素数)。
例外:ArithmeticException - this < 0であるか、thisが大きすぎる場合。
導入されたバージョン:1.5
ん?なんか、微妙な空気を醸し出している...駄目な予感が...
⇧ 上記サイト様のスライドによりますと、まぁ、大丈夫であろうと。 内部的には、「ミラー - ラビン素数判定法」ってものを使ってるらしいと。
2023年1月29日(日)編集:↓ ここから
コメントいただき、計算が間違ってましたので、再計算いたしました。
2-100 っていうことは、1.0069555500567189 % で、失敗するってことかしら?
実際に、計算してみた。
double base = 2;
double exponent = Double.valueOf("1") / Double.valueOf("100");
System.out.println(Math.pow(base, exponent));
うん、そういうことらしい、って... ...
え? 100回やったら、1回は素数じゃない値が返却される可能性があるってこと?
駄目じゃね?
精度低過ぎでしょ...
ちなみに、double はプリミティブ型ということで、
⇧ 上記サイト様が詳しいです。
Double
クラスは、プリミティブ型double
の値をオブジェクトにラップします。Double
型のオブジェクトには、型がdouble
の単一フィールドが含まれます。
https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Double.html
⇧ Double型はdoubleのラッパークラス。
2023年1月29日(日)編集:↑ ここまで
2023年1月29日(日)追記:↓ ここから
コメントいただたき、2-100 の計算が間違ってたので、再計算しました。
double base = 2; double exponent = Double.valueOf("-100"); System.out.println(Math.pow(base, exponent));
double base = 2; double exponent = Double.valueOf("100"); System.out.println(1/Math.pow(base, exponent));
⇧ だいぶ小さい値ということが分かりました。
指数表記しないで表示。
double base = 2; double exponent = Double.valueOf("100"); System.out.println(BigDecimal.valueOf(1/Math.pow(base, exponent)).toPlainString());
失敗する確率は、0.0000000000000000000000000000007888609052210118%となり、精度は高いことが分かりました。
10のマイナス1乗は「0.1(=1/10)」です。マイナス〇乗の計算では「a-m=1/am」のように「分母をam」とする分数になります。よって、10のマイナス1乗は「101を分母、分子は1」なので、1/101=1/10=0.1です。10のマイナス2乗は「0.01」、10のマイナス3乗は「0.001」になります。
2023年1月29日(日)追記:↑ ここまで
実際にやってみた
Eclipseを起動して、適当にJavaプロジェクトを作成。クラスも作成。
そしたらば、今回は、結果をファイルに書き込みたいので、適当にフォルダを作成し、ファイルも作成。(Maven や、Gradle なんかのビルドツールでプロジェクトを作成してる場合は、プロジェクトのフォルダの構成とかも変わってくると思われます。)
今回、「FunctionIFTest」ってJavaプロジェクトを作成しているので、プロジェクトディレクトリ直下に、「file」というフォルダを作成しました。
「file」フォルダ直下に、ファイルを作成で。
適当なファイルを作成で。
今回は、1000個の素数で試しました。(時間かかってもOKよって人は、数を増やしてトライしてみよう。)
4桁の素数を出すのでも調べるのに時間がかかってしまった、世界記録は、24,862,048 桁 というのに(涙)。
ソースコードはこんな感じで。
package main; import java.io.IOException; import java.math.BigInteger; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.OpenOption; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.List; import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; public class Eratoshenes { final static Logger logger = Logger.getLogger("EratoshenesLogging"); final static String PRIME_NUM_OUTPUT_FILE_PATH = "file/primeNumber.txt"; public static void main(String[] args) { // TODO 自動生成されたメソッド・スタブ List<String> list = takePrimeList(new BigInteger("1000")); fileWrite(Paths.get(PRIME_NUM_OUTPUT_FILE_PATH), list, StandardCharsets.UTF_8, StandardOpenOption.TRUNCATE_EXISTING); System.out.println("素数の数: " + list.size()); } /** * 指定した範囲で素数を求めて、テキスト(文字列)のListに変換するメソッド * @param number 指定範囲の整数 * @return 素数のテキスト(文字列) */ public static List<String> takePrimeList(BigInteger number) { return Stream.iterate(BigInteger.valueOf(2), BigInteger::nextProbablePrime) .limit(number.longValue()) .peek(x -> { System.out.println(x); }).map(bigInt -> bigInt.toString()) .collect(Collectors.toList()); } /** * ファイルに書き込むメソッド * @param outPutPath 出力先のファイルパス * @param lines 出力したいテキスト(文字列) * @param cs 文字コード * @param options 出力のオプション */ public static void fileWrite(Path outPutPath, Iterable<? extends CharSequence> lines, Charset cs, OpenOption... options) { try { Files.write(outPutPath, lines, cs, options); } catch (IOException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } } }
んで、実行してみる。
テキストファイルに出力されました。Loggerクラスのインスタンスとか用意しておきながら、使ってないや...
Stream API の limit() が無いと無限ループ?になってしまうようなので、どこまで処理するか制限を設ける必要があるようです。まぁ、このへんは決め打ちで指定するしかないかと。(だって、素数がいくつあるかを調べたいんだから、事前に素数がいくつあるかなんて分かるわけないしね)
クラス名を、Eratoshenes とかにしちゃったけど、全然、「エラトステネスの篩」になってないっす(涙)。
ファイルの2GBの壁に関する問題は、
⇧ 上記サイト様によりますと、java.nio.file.Files パッケージのcopy メソッドなら問題なく回避できるらしいです。
ファイルパスとかは、
⇧ 上記サイト様を参考にさせていただきました。
ちなみに、Stream API ですが、
⇧ 上記サイト様が詳しいです。
発展途上ということで、Java 9 とかでちょこっと機能が追加されてるそうです。
Stream API の peek() メソッドとかってデバッグ用途だったんすね。forEach() と違って、戻り値を戻せる状態のままで処理してくれるのはありがたいかな。
Wikipedia さんによりますと、
⇧ かなり、たくさんのアルゴリズムがあるらしい。ネットとかの情報だと「エラトステネスの篩」が多いかな。何を使えば良いのかサッパリですわ...
まぁ、何て言うか、 いつもながら、モヤモヤ感が半端ない...数学の知識ないと厳しい感じがしてならない...
とりあえず、BigInteger クラスの nextProbablePrime() メソッドは、あんまりイケてないらしいということですかね。
今回はこのへんで。