2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonで素数定理を可視化する:π(n)/n の収束を確かめよう

2
Posted at

はじめに

「素数はどのくらいの頻度で現れるのか?」

この問いに対する答えは、19世紀に数学者たちが長年追い求めた素数定理として結実している。本記事では、Python を使って $n$ 以下の素数の個数 $\pi(n)$ を数え、その比 $\pi(n)/n$ をグラフに描くことで、素数定理が予言する振る舞いを視覚的に確かめる。ただし、今回の記事はClaudeCodeを用いた。


素数とは

素数とは、1 より大きい整数のうち、1 と自分自身以外に正の約数を持たない数のことである。

$$2,\ 3,\ 5,\ 7,\ 11,\ 13,\ 17,\ 19,\ 23,\ \ldots$$

素数は無限に存在する(ユークリッドによる古典的な証明が有名である)。しかし、その分布は一様ではなく、大きな数になるほど素数は「まばら」になっていく。


素数定理

$n$ 以下の素数の個数を $\pi(n)$(素数計数関数)と表す。素数定理は次を主張する。

$$\boxed{\pi(n) \sim \frac{n}{\ln n} \quad (n \to \infty)}$$

ここで $f(n) \sim g(n)$ は $n \to \infty$ のとき $f(n)/g(n) \to 1$ を意味する。

言い換えると、

$$\frac{\pi(n)}{n} \sim \frac{1}{\ln n} \quad (n \to \infty)$$

が成り立つ。つまり「$n$ 付近のランダムな整数が素数である確率はおよそ $\frac{1}{\ln n}$」と解釈できる。

素数定理は 1896 年にアダマールとド・ラ・ヴァレー・プサンが独立に証明した。証明にはリーマンのゼータ関数の解析的性質が用いられ、純粋に初等的な手段では 1949 年にエルデシュとセルバーグが独立に証明するまで待つことになる。


実装

アルゴリズムの選択:エラトステネスの篩

単純な試し割り法では 1 つの数 $m$ の素数判定に $O(\sqrt{m})$ の時間が必要で、$n (= 10^6)$ まで全て調べると合計 $O(n\sqrt{n})$ かかる。

代わりにエラトステネスの篩を使うと $O(n \log \log n)$ で $n$ 以下の全素数を一括で求められる。NumPy の配列演算と組み合わせることで、$n = 10^6$ でも非常に高速に動作する。

コード

import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib

N = 10**6


def sieve_of_eratosthenes(limit):
    """エラトステネスの篩で limit 以下の素数を求める"""
    is_prime = np.ones(limit + 1, dtype=bool)
    is_prime[0:2] = False
    for i in range(2, int(limit**0.5) + 1):
        if is_prime[i]:
            is_prime[i*i::i] = False
    return is_prime


is_prime = sieve_of_eratosthenes(N)

x = np.arange(1, N + 1)
prime_count = np.cumsum(is_prime[1:N + 1])  # π(n)
ratio = prime_count / x                      # π(n)/n

# 素数定理の理論値: π(n)/n ≈ 1/ln(n)
theoretical = 1.0 / np.log(x[1:])           # n=1 は log(1)=0 で除算不能のため除外
x_theory = x[1:]

plt.figure(figsize=(10, 6))
plt.plot(x, ratio, color='blue', linewidth=0.8, label=r'$\pi(n)/n$(実測値)')
plt.plot(x_theory, theoretical, color='red', linewidth=1.2, linestyle='--',
         label=r'$1/\ln(n)$(素数定理による近似)')
plt.xlabel('n')
plt.ylabel(r'$\pi(n)/n$')
plt.title(r'素数の個数のグラフ:$\pi(n)/n$ と素数定理')
plt.legend()
plt.tight_layout()
plt.savefig(f"prime_number_theorem_{N}.png", dpi=150)
plt.show()

コードのポイント

箇所 説明
np.ones(..., dtype=bool) 全要素を「素数候補」として初期化する
is_prime[i*i::i] = False $i$ の倍数を一括で篩い落とす($i^2$ 未満はすでに処理済みである)
np.cumsum(...) インデックス $n$ までの素数の個数 $\pi(n)$ を累積和で $O(n)$ で計算する
1.0 / np.log(x[1:]) 素数定理の理論曲線 $1/\ln(n)$($n=1$ は除外)

結果と考察

実行すると、以下のようなグラフが得られる。

prime_number_theorem_1000000.png

  • 青線(実測値):$\pi(n)/n$ の実際の値
  • 赤破線(理論値):$1/\ln n$

グラフから、$n$ が大きくなるにつれて両者が接近していく様子が確認できる。これは素数定理の主張そのものである。

なお、$n = 10^6$ 時点でも完全には一致していない。これは「$\sim$」が漸近的な等式(極限での比が 1 に収束)を意味しており、有限の $n$ では誤差が残るためである。理論値との差(相対誤差)は $n$ が大きいほど小さくなる。

より精密な近似としては、対数積分

$$\mathrm{Li}(n) = \int_2^n \frac{dt}{\ln t}$$

を用いると $\pi(n)$ との一致が格段に向上することが知られている。


まとめ

  • 素数定理 $\pi(n)/n \sim 1/\ln n$ を Python で可視化した
  • エラトステネスの篩と NumPy の累積和を組み合わせることで、$n = 10^6$ を高速に処理できた
  • グラフから、実測値が理論値に収束していく様子を視覚的に確認できた

素数の分布はシンプルな法則に従いながらも、その証明は深い解析学を要する。可視化によって直感的な理解の一助となれば幸いである。


参考文献

  • G.H. Hardy, E.M. Wright, An Introduction to the Theory of Numbers, Oxford University Press

  • 松坂和夫, 『解析入門』, 岩波書店

  • Wikipedia: 素数定理

  • エラトステネスの篩
    (https://manabitimes.jp/math/992)

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?