Pythonでコラッツ予想を計算する&一部修正(3/10)
またまたpythonです。今回はコラッツ予想です。一部、記事作成から変更した場所があります。
コラッツ予想
だそうです。これをpythonで演算します。ちなみに未解決問題です。
wikipedia曰く
だそうです。数学ってやっぱよく分かんない…
設計
ざっとした設計を。
パスの設定
csvファイルの作成
値の入力
演算と表示
グラフ出力
csv出力
繰り返し(y/n)
github
import
import datetime
import sys
import os
import pandas as pd
import matplotlib.pyplot as plt
日付取得(datetime)、sys,os(なんかパスとか)、pandas(csv)、matplotlib(グラフ表示)用に色々と使用。
パスの設定
パスの設定
datetime_now = datetime.datetime.now().strftime('%m_%d_%H:%M:%S')
log_file_name = f"log_{datetime_now}"
datetime_now:実行時の時刻を月日から秒まで取得。ファイル名にするため1分以内に複数回実行した際にファイル名が重複しないように、秒まで取得。
log_file_name:csvファイル用のパス
パスの設定(最新版_24/3/10)
date_now = datetime.datetime.now().strftime('%m_%d_%H:%M:%S')
ちょっと単一の変数に複数の意味を持たせすぎたと感じたので、日付は日付として扱うことにします。今までlog_file_name変数につけていた文字列「log」は使用する際に逐一記述します。
if getattr(sys, 'frozen', False):/実行ファイル対策
if getattr(sys, 'frozen', False):
os.makedirs(f"{os.path.dirname(sys.executable)}/csv_files/{log_file_name}", exist_ok=True)
log_file_name = f"{os.path.dirname(sys.executable)}/csv_files/{log_file_name}/{log_file_name}.csv"
else:
os.makedirs(f"csv_files/{log_file_name}", exist_ok=True)
log_file_name = f"csv_files/{log_file_name}/{log_file_name}.csv"
with open(log_file_name, 'w') as f:
f.write("time,value\n")
基本的にはmakedirsでcsvファイルを作成している。
pyinstallerで実行ファイルにした場合、パスの扱いがスクリプトとの際と変わるため、個別に設定。if文がFalseの場合は実行ファイルでないということ。
Trueの場合(実行ファイルの場合)
os.makedirs(f"{os.path.dirname(sys.executable)}/csv_files/{log_file_name}", exist_ok=True)
親フォルダまで作成。実行ファイルで実行されている際の処理のため、実行ファイルの親フォルダのパス(os.path.dirname)を指定し、その下に/csv_files/{log_file_name}というフォルダを作成。この際、log_file_nameは現在日付です。
ちなみにpythonで文字列""にfをつけてf""とすると文字列内で、{}で囲むことで変数を挿入できます。以下例
fuga = "fuga"
print(f"hoge{fuga}")
# hogefuga
log_file_name = f"{os.path.dirname(sys.executable)}/csv_files/{log_file_name}/{log_file_name}.csv
log_file_nameにcsvファイルまで含んだパスを代入。本当は1つのパスに複数の意味をもたすのはバグの温床になるので、推奨されないかと思うがとりあえず放置。
Falseの場合(実行ファイルでない場合)
os.makedirs(f"csv_files/{log_file_name}", exist_ok=True)
log_file_name = f"csv_files/{log_file_name}/{log_file_name}.csv"
先ほどと同じように親パスを作成。ただ今回は実行ファイルのパスとか関係なく、そのまま記述できます。またそのパスにcsvを付けます。前述の通りこういう再代入はおそらく非推奨。
ファイルの作成
with open(log_file_name, 'w') as f:
f.write("time,value\n")
先ほどのif文内でパスを設定したので、そのままファイルを作成。
2024/03/10:新機能のため追記
後から初期値などのデータを簡単に参照できるようにtxtファイルに基本情報を載せる。
with open(f"{os.path.dirname(log_csv_file_path)}/operation_log.txt", 'w') as f:
f.write(f"実行日時:{date_now}\n初期値:{n}\n")
最初は実行日時と初期値、最大最小値もいれときます。
with open(f"{os.path.dirname(log_csv_file_path)}/operation_log.txt", 'a') as f:
f.write(f"演算回数:{count}\n最大値:{input_csv['value'].max()}\n最小値:{input_csv['value'].min()}")
演算
値の入力
print("値を入力してください")
n = int(input())
ユーザに値の入力を要求&変数nに値を代入(intに変換)。
変数の設定
repat = False
count = 1
repeat:繰り返し確認用の変数。初期値はFalse。
count:書き込み回数表示用。
csv書き込み
with open(log_file_name, 'a') as f:
f.write(str(count) + "," + str(n) + "\n")
print("\r" + str(count) + "回書き込み", end="")
作成したcsvファイルに初期値のみ追記。'a'にすることで追記モードになる。
while n != 1:
if(n%2 == 0): #偶数
n /= 2
else: #奇数
n *= 3
n += 1
with open(log_file_name, 'a') as f:
count += 1
f.write(str(count) + "," + str(n) + "\n")
print("\r" + str(count) + "回書き込み", end="")
nが1でない限り(=nが1になるまで)繰り返す。
ここは定義通り、nがn%2 == 0がTrue(つまりnが偶数)かFalse(つまりnが奇数)の場合に分けて計算する。
計算が終了するたび、with openでcount、カンマ、値、改行文字(\n)を追記していく。その後、printでその値を表示。"\r"とend=""でその行(コンソール)を書き換え続ける。
print(f"\n{n}になりました")
おそらく1になっているはずなので、そのまま表示。
グラフ
せっかくなのでグラフ表示もしてみます。
input_csv = pd.read_csv(log_file_name)
先ほどの書き込みの終了しているcsvファイルをinput_csvに代入。
plt.plot(input_csv["time"], input_csv["value"])
plt.xlabel("time")
plt.ylabel("value")
書いたのが結構まで怪しい記憶ですが、timeよvalue(csvを作成した際の"time,value\n")を縦軸横軸として参照するように設定。
plt.savefig(f"{os.path.dirname(log_file_name)}/graph_{datetime_now}.png", format="png", dpi=300)
print(f"{os.path.dirname(log_file_name)}に画像を保存しました。")
グラフ画像の保存と保存ダイアログ(cuiでもダイアログという?)。
print("グラフを表示しますか?(y/n)")
graph_show = input()
if graph_show == "y":
plt.show()
グラフの表示。一応、グラフの表示は任意にしました。
999をコラッツ予想で計算すると以下のようになります。1/2倍だとか3倍とかするので結構グラフの起伏が激しいですね。
一応csvも。
繰り返し確認
print("もう一度実行しますか?(y/n)")
if input() != "y":
break
もう一度計算するかの選択です。pythonではdo-whileがないので、while Tureで無限ループにして、繰り返さない場合(input() != y)にbreakでループから抜けてプログラムを終了します。
まとめ
テレビでコラッツ予想を見て面白そうなので、pythonでやってみました。
99999でやるとこんな感じ。
この記事が気に入ったらサポートをしてみませんか?