西暦13446204年には素数がない
この記事はなに
グレゴリオ暦の年月日を十進法で表現された数として読むと素数になることがある(例:2022年1月3日→20220103は素数)。グレゴリオ暦が未来永劫に有効であるとして、このような素数が含まれない年は13446204年に初めて訪れる。本記事では、この値を最終的に発見するまでに試みた探索の過程を説明する。
コードは
github.com
に上げています。
バージョンは、
- Rust: 1.55.0
- Python: 3.8.10
用語の約束
素数日
年月日を十進法で読むと素数になる日。上述の2022年1月3日など。各年の素数日の数の列が A296008 - OEIS に登録されている。
合成数日
年月日を十進法で読むと合成数になる日。2021年12月28日は明らかに合成数日である。
無素数年
素数日を含まない年(その年の全ての日が合成数日である年)。13446204は最初の無素数年である。
「グレゴリオ暦」
歴史的には グレゴリオ暦は1582年に導入されている。また、日付と時刻に関する国際規格であるISO8601では9999年以降の扱いに関しては定めていない。本記事で「グレゴリオ暦」(西暦)という場合、西暦0年から無期限にこの暦法が適用されると仮定する。すなわち、各月に含まれる日数や閏年・閏日に関する扱いは現状に準じる*1。なお、負数(紀元前)は考えない。
経緯
@tsujimotterさんによる記事「カレンダーの上の素数 〜素数には毎年出会えるか?〜」において、年月日を十進法で読んだときに素数になる日(以下「素数日」)を含む個数の期待値が1を下回る年の見積もりとして、 年が与えられている。
最初の目標は、具体的に無素数年を構成することでこの評価を改良することにあった。
ところが、計算を進めるうちにこの上限がどんどん下がり、愚直な探索で最初の無素数年が見つけられることが判明、ついに13446204が実際に最初の年であることが分かった*2。
結果としては最初から愚直な探索を回せばよかったものの、この宝石探しそれ自体が楽しいものだった。そのツイートを見てくださったtsujimotterさんご本人からもコメント*3をいただき、簡単にまとめることにした。
適当な数の総積による構成
具体的に無素数年を探していく。年 に対して、 が全て合成数なら無素数年になる。ただし、 はmmdd形式の月日を十進法で読んだ数で、具体的には(カレンダーを書き下すだけだが)平年(common year)なら 以下の の要素。
閏年(leap year)なら、閏日に対応する を加えて、 の要素を取る。
つまり、年 が平年か閏年かに応じて、
で定義される の要素すべてが合成数になればよい。
アプローチのひとつは、 が 全ての と2以上の公約数を持つような を作ることである。
mmddの総積(1001桁)
階乗による構成は明らかに無駄を含んでいる。2月30日は存在しない*4 から、積を取るとき 230 は除外してよい。
平年のmmddすべての積を
によってとると、1001桁の無素数年になる。なお、平年を取ったのは、こうして得られる数が明らかに400で割り切れるから。
2か5の倍数を除いたmmddの総積(412桁)
20211230, 20220102, 20220105, ...は明らかに合成数。 が 2 または 5 を約数に持てば、 もそれを約数に持つため。だから、積から 2 か 5 の倍数は除いてもいい。
として の総積を取ると、
と412桁の無素数年が得られる。
1より大きい最小の公約数のみ共有(167桁)
積を取る中で、 それ自体を掛ける必要はない。たとえば、 は、 との公約数として7だけを共有していればよい。
また、順に掛けていくと、 の約数3は、 が現れた時点で積に取り込まれているから追加する必要がない。
この方法で計算すると、
が得られる。
なお、構成法から明らかなように、この は無平方数である。
中国剰余定理
ここで少しアプローチを変える。 がある素数 で割り切れるようにするために それ自体を掛ける方法では、積がどんどん膨らむためなかなか小さな値は得られない。もっと直接的に、 という条件を課すほうが小さい値が得られる見込みは高いだろう。
全ての日に制約(367桁)
各年月日がある(小さい)素数で割り切れるという制約を課して、その条件を満たす整数を求める。具体的には、
を解く。これを満たす最小の正整数 は、 未満に唯一存在することが中国剰余定理から保証されている。
なお、閏日は条件から除いておく。もし閏日()が素数になってしまった場合、平年の素数日として を取れる。
さて、実際に計算すると、
条件をシャッフルして
などとすれば は変わるが、 を上限として オーダーに留まるため、あまり改善しない。
関係式を減らす + 乱択(~9桁)
が3の倍数なら は自動的に3の倍数になる。だから、 が3の倍数になるように条件を課したなら、 に関する条件は不要になる。
さらに、総積による方法で用いたのと同様、すべての月日に対して条件を課す必要はない。適当な閾値を定めて、解いた後でそれが無素数年であれば採択、そうでなければ再試行すればよい。
肝は「適当な閾値」の取り方にある。条件に課す素数の範囲としてもよいし、条件が課されない月日の数にとってもよい。
一例として、 までの制約を課すとすると、100万回程度の試行で
という連立方程式から
が見つかった。西暦4億年。太陽が赤色巨星になるより前だ。
ちなみに後で行った計算で、この405956995は122番目の無素数年であることが分かった。かなりいい線を行っている。
10の8乗? このあたりで、無素数年がずいぶん小さい領域に存在することに気付く。
ここまでの目的は構成的に無素数年を発見することで、最初の無素数年の上限を改良することにあった。なるべく小さい無素数年を探すゲーム。ところが、10の8乗オーダーの無素数年が見つかったとなると問題は違った局面に入る。ひょっとすると、0年から順に探せば最初の無素数年が見つかるのでは?
0から探す(8桁:13446204)
西暦0年から探していくだけ。特に工夫もない。
手元の1.8GHzのマシンで1秒に10万年調べられる(!)ので、実は2分程度で見つかる。10時間かかるとかならともかく……最初からこれを試すべきだったのでは?(後からでは何とでも言える)
コードと……
use primeless_year::*; use rug::Integer; fn main() { for y in 0.. { let y = Integer::from(y); if y.is_probably_primeless_year() == IsPrimelessYear::Yes { println!("{}", y); break; } } }
出力:
13446204
こうして西暦1344万6204年が最初の無素数年であることが分かった。閏年だ。
ここまで小さいと愚直な素因数分解も簡単にできて、実際にすべての月日が合成数日であることが確かめられる。しかも64ビット整数の範囲内で。
この中で、
あたりは2つの素因数がどちらも6桁でいい感じだ。ちなみに 自身は
と素因数分解される。
なぜこんなに小さいか?
13446204は小さい。こういう類の問題で、whileループで網羅的に調べられるというあまりにも「ちょうどよい」範囲に答えがあるとは予想していなかった。
無素数年はこの後にも
10億以下の無素数年のリスト(485個ある)
とおおよそ1000万年に1回のペースで続き、決して最初の無素数年だけが例外的に小さいわけではない。
答えが見つかったいま、新しい問題が付け加わる。「なぜこんなに小さいか?」
OEISに「n個以上の合成数が連続する区間の最小の数」の列が登録されている。たとえば、合成数が6個以上連続する最初の区間は90,91,92,93,94,95で、その次の96も合成数だから、。
Smallest start for a run of at least n composite numbers.
A030296 - OEIS
リストに登録されているように、 。最小の無素数年 に対して、 だから、ほぼオーダーは同じ。素数定理で大雑把には見積もれるはずだがちょっと自信がない。今後の課題。
*1:つまり、400で割り切れるか、4で割り切れて100で割り切れない年のみ2月29日が追加される。
*2:この小ささから察せられる通り、結果としては既知のものだった。https://math.stackexchange.com/questions/3488712/is-there-a-prime-every-year-if-yyyymmdd-is-treated-as-a-base-10-number やhttps://twitter.com/potetoichiro/status/1344307093503844352 など。
*3: https://twitter.com/tsujimotter/status/1475327097279512579