"""
ランダムな値の分配による指数分布(ボルツマン分布)への収束シミュレーション。
概要:
多数の参加者間でランダムにリソース(値)を分配し、システム全体の統計的な分布が
いかにして指数分布(Boltzmann分布)に近づくかをリアルタイムで可視化します。
詳細説明:
1. 初期状態では全員が同じ値(平均値)を持っています。
2. 各ステップで、参加者にランダムな値を割り振り直し、全体の総和が一定になるよう正規化します。
3. 反復計算を繰り返す中で、以下の3つのグラフを表示します:
- 個々の参加者の現在の値(ランダムな揺らぎ)。
- 値を昇順にソートしたプロファイル。
- 頻度分布(ヒストグラム)と理論的な指数分布 $f(x) = A e^{-x/\langle x \rangle}$ の比較。
4. 統計力学における「最大エントロピー」の状態がいかに指数分布として現れるかを体験的に理解できます。
関連リンク: :doc:`randomdistribution_usage`
"""
import sys
import random
import numpy as np
from math import exp
import matplotlib.pyplot as plt
#=============================
# デフォルトパラメータ
#=============================
NP_DEF = 50 # 参加人数
VAL_DEF = 50 # 初期値(および平均値)
NITER_DEF = 500 # 最大反復回数
NPLOT_DEF = 100 # グラフ更新間隔
NFX_DEF = 21 # 分布関数のメッシュ数
[ドキュメント]
def main():
"""
シミュレーションの主実行ルーチン。
"""
# 引数処理
argv = sys.argv
npersons = int(argv[1]) if len(argv) >= 2 else NP_DEF
avg_val = int(argv[2]) if len(argv) >= 3 else VAL_DEF
niter = int(argv[3]) if len(argv) >= 4 else NITER_DEF
nplotinterval = int(argv[4]) if len(argv) >= 5 else NPLOT_DEF
nfx = int(argv[5]) if len(argv) >= 6 else NFX_DEF
# 初期化
x_index = np.arange(npersons)
v = np.full(npersons, float(avg_val))
# 理論的な指数分布の計算
# f(x) = (N/avg) * exp(-x/avg)
beta = 1.0 / avg_val
coeff_a = npersons * beta
x_max_theory = avg_val * 10.0
ftx = np.linspace(0, x_max_theory, 101)
fty = [coeff_a * exp(-beta * x) for x in ftx]
print(f"\nSimulation started: n={npersons}, avg={avg_val}")
print(f"Theoretical distribution: f(x) = {coeff_a:.4g} * exp(-{beta:.4g} * x)")
# 描画の準備
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(8, 10))
plt.subplots_adjust(hspace=0.4)
# シミュレーションループ
for i in range(niter):
# 1. ランダムな割り振り
raw_values = np.random.rand(npersons) * avg_val
# 2. 総和一定のための正規化
current_sum = np.sum(raw_values)
v = raw_values * (avg_val * npersons / current_sum)
# ソート(分布確認用)
v_sorted = np.sort(v)
# 指定間隔でのプロット更新
if i == 0 or (i + 1) % nplotinterval == 0 or i == niter - 1:
ax1.cla()
ax2.cla()
ax3.cla()
# グラフ1: 生の分配データ
ax1.bar(x_index, v, color='skyblue', alpha=0.7)
ax1.axhline(avg_val, color='red', linestyle='--', linewidth=0.8, label='Average')
ax1.set_title(f'Step {i+1}/{niter}: Random Resource Allocation')
ax1.set_ylabel('Value')
ax1.set_xlim(-1, npersons)
# グラフ2: ソートされたデータ(ローレンツ曲線に近い表現)
ax2.bar(x_index, v_sorted, color='steelblue', alpha=0.7)
ax2.axhline(avg_val, color='red', linestyle='--', linewidth=0.8)
ax2.set_title('Sorted Distribution Profile')
ax2.set_ylabel('Value (Sorted)')
ax2.set_xlim(-1, npersons)
# グラフ3: 分布関数(ヒストグラム)と理論曲線
counts, bins = np.histogram(v, bins=nfx, range=(0, max(v)), density=False)
bin_centers = (bins[:-1] + bins[1:]) / 2.0
bin_width = bins[1] - bins[0]
# 頻度を「値の幅」で割って分布関数にする
y_dist = counts / bin_width
ax3.step(bin_centers, y_dist, where='mid', label='Simulation', color='black')
ax3.plot(ftx, fty, label='Theory (Boltzmann)', color='red', linestyle='dashed')
ax3.set_title('Probability Distribution Function')
ax3.set_xlabel('Value')
ax3.set_ylabel('Frequency Density')
ax3.set_xlim(0, max(v) * 1.1)
ax3.legend()
plt.pause(0.05)
print("\nSimulation completed.")
plt.show(block=False)
input("Press ENTER to exit>>")
if __name__ == '__main__':
main()