等間隔に並ぶ素数を探そう
どうもこんにちは。2018年10月入社の@wonda-tea-coffeeです。
本当はReactNativeで何か作る予定だったのですが、時間が足りず数学ネタに走ってしまいました。
というわけで(?)等間隔に並ぶ素数のお話を始めます。
等間隔に並ぶ素数とは?
素数とは、1と自分自身でしか割り切れない正の整数です。
また、等間隔に並ぶ、というのは等差数列を意味しています。
等差数列についても復習しましょう。例えば、
$$7, 13, 19$$
は初項が7、項差が6の素数のみから成る等差数列です。
今回のモチベーション
可能な限り長い等間隔に並ぶ素数を見つけたい。
探してみた
実装方針
- 事前にエラトステネスの篩で指定範囲内の素数を洗い出す
- 初項をずらしつつ、項差を30ずつ増やしていく
探索イメージ)
7は素数!
項差30: 7, 37, 67, 97, 127, 157, 187(←素数じゃないので探索終わり!)
項差60: 7, 67, 127, 187, 247(←素数じゃないので探索終わり!)
...
9は素数じゃない!
11は素数
項差30: 11, 41, 71, 101, 131, 161(←素数じゃないので探索終わり!)
項差60: 11, 71, 131, 191, 221(←素数じゃないので探索終わり!)
- 目的の長さの数列が見つかったらDone!
コード@Java
import java.util.*;
public class Solve {
public static void main(String[] args) {
long s = System.currentTimeMillis();
final int LIM = 100000000;
boolean[] isPrime = sieve(LIM);
int target_length = 15;
// 探索開始位置のループ
main: for (int p = 7; p < LIM; p += 2) {
// 項差のループ
final int tl_minus_1 = target_length - 1;
for (int d = 30; p + d * tl_minus_1 < LIM; d += 30) {
int length = 0;
for (int m = p; m < LIM && isPrime[m]; m += d)
length++;
if (length >= target_length) {
System.out.print("長さ" + length + ": ");
disp_sequence(p, d, length);
break main;
}
}
}
long e = System.currentTimeMillis();
System.out.println("time: " + (e - s) + "ms");
}
// 初項、項差、長さを元に数列を表示
static void disp_sequence(int a, int d, int length) {
System.out.print(a);
for (int i = 1; i < length; i++)
System.out.print(", " + (a + d * i));
System.out.println();
}
// エラトステネスの篩を用いて与えられた数未満の素数を求める
static boolean[] sieve(int n) {
boolean[] isPrime = new boolean[n];
Arrays.fill(isPrime, true);
// 0と1は素数でないため除外
isPrime[0] = isPrime[1] = false;
// 2より大きな偶数は素数でないため除外
for (int i = 4; i < n; i += 2)
isPrime[i] = false;
// 1より大きな奇数同士の積で表せるものは素数でないため除外
for (int i = 3; i * i <= n; i += 2) {
for (int j = i; i * j < n; j += 2)
isPrime[i * j] = false;
}
return isPrime;
}
}
今回見つけた等間隔に並ぶ素数たち
長さ3
$$3, 5, 7$$
長さ4
$$7, 19, 31, 43$$
長さ5
$$5, 11, 17, 23, 29$$
長さ6
$$7, 37, 67, 97, 127, 157$$
長さ7
$$7, 157, 307, 457, 607, 757, 907$$
長さ8
$$11, 1210241, 2420471, 3630701, 4840931, 6051161, 7261391, 8471621$$
長さ9
$$17, 6947, 13877, 20807, 27737, 34667, 41597, 48527, 55457$$
長さ10
$$37, 2040607, 4081177, 6121747, 8162317, 10202887, 12243457, 14284027, 16324597, 18365167$$
楽しいですね。
考えたこと・試したこと
- 項差は3の倍数でなければならない
→項差が3の倍数でない場合、数列の長さは2より長くなり得ない。
ただし、数列に3を含まないものとする(これは読者への練習問題とする)。 - 項差は10の倍数でなければならない
→項差が10の倍数でない場合、数列の長さは5より長くなり得ない(こちらも読者への練習問題とする) - 求めた素数をHashSetに格納すればmainのループ先頭で素数判定をしなくて良い!と思いきやかえって遅くなった
→containsが響いたかな・・・?
残る課題
・項差を30の倍数にしたとはいえ何の捻りもない全探索なのでLIMを増やすと極端に遅くなる
・ メモリ食い過ぎ
もっと長く
現在人類が見つけている等間隔に並ぶ素数の長さは26です。
では長さ27, 28, 29, ...の等間隔に並ぶ素数は存在するのでしょうか。
2004年、その問いにBen GreenとTerence Taoは解答を出しました。
彼らによれば、
素数の列は任意の長さの等差数列を含んでいる
そうなのです。
長さ100でも、1000でも、10000でも、存在すると言っているのです!
コンピュータの力を持ってしてもたったの長さ26の等間隔に並ぶ素数しか見つけられないにも関わらず!
数学ってすごいですよね・・・
本当に?
こちらのブログでGreen-Taoの定理の証明の日本語での(!)解説が見られます。
大学2年次程度の数学の知識があれば十分に理解できるそうです。
終わりに
いかがでしたでしょうか。
これを機に数学に少しでも興味を持っていただければ幸いです。
また、業務中にこんな趣味全開の記事が書けてとても満足しています。
明日は@yizknnさんで、
「RubyでRettyからランチ候補をオススメしてくれるSlackBotを作りました」
どうぞお楽しみに〜