はじめまして!今回が初投稿となります!
内容はほぼタイトル通りなのですが、logの近似アルゴリズムを考え、ChatGPTにGUI化までしてもらった記録です。ハイになってChatGPTと一緒にNestLog法という名前までつけてしまいました
(とても簡単なアルゴリズムなので、ほぼ間違いなく誰かが既に考えていると思うのですが)
正式名称あったら教えてください
📊 概要
このアルゴリズムは、底 > 真数の条件のもと、連分数の構造を用いて実際のlog値を近似的に求めます。
例:log_10(2)を近似しようとした場合、以下のようになります。
割る回数が少ないので精度は微妙ですが、これを繰り返せば、より精度良く近似できるはずだと考えました。
log_a(b)の近似手順としては、
- 底をbとして底の変換公式を用いる(分母はlog_b(a)の形になる)
- 真数を底の値で割っていく(真数を小さくしながら、+1を括り出すことができる)
- 真数<底になったら、1. を行い、繰り返す
を行います。
⚖️ アルゴリズム手順
さて、ここからはChatGPTさんの出番です。概要で示したアルゴリズムを文章で伝えたところ、プログラムにしてくれました。
(ここまで便利だと嬉しさ反面、自分で考えたアルゴリズムすら書き下せないのかと、ある種劣等感も覚えますね)
- 条件
・入力は底a,真数b
・a=1またはa=0, aまたはbが負の場合は定義されないとして例外とする
・a>bの場合、そのままNestLog法
・a< b のとき、底の変換公式を用いて、c をa > cかつb > cを満たすように選び、対数を変換する
・底>1, 真数<1 の場合と底<1, 真数>1 の場合は、真数を1/bとして符号を最後に反転する
これでどの値を入れても動作するようにしました。
# loga(b)の形で入力する
print("loga(b) の a と b を入力してください: ")
a = float(input("底 a: "))
b = float(input("真数 b: "))
# 元のa, bを保存
orig_a = a
orig_b = b
# 符号フラグ
sign = 1
# 底>1, 真数<1 の場合
# 底<1, 真数>1 の場合
if (a > 1 and b < 1) or (a < 1 and b > 1):
sign = -1
b = 1 / b
# ----ここから例外処理----
# 底が0の場合
# 底が1の場合
if a == 1 or a == 0:
print("底が0または1の場合は定義されません。")
exit(1)
# 真数が<0の場合, 底が<0の場合
if b < 0 or a < 0:
print("実数空間で考えましょうよ。")
exit(1)
# 真数が1の場合
if b == 1:
print(print(f"log{a}({b}) ≒ 0"))
exit(1)
# ----ここまで例外処理----
# 底 < 真数かつ符号が正のとき、底の変換公式を使い、底と真数を同じ新しい底に変換する
if a < b and sign == 1:
def approx_log(x, base):
n_list = []
current_base = base
current_value = x
for _ in range(10):
if current_value < current_base:
current_base, current_value = current_value, current_base
continue
count = 0
epsilon = 1e-10
while current_value - current_base > epsilon:
current_value /= current_base
count += 1
if count == 0:
n_list.append(1)
break
n_list.append(count)
current_base, current_value = current_value, current_base
ans = 0
for i in range(len(n_list)-1, -1, -1):
if ans == 0:
ans = 1 / n_list[i]
else:
ans = 1 / (n_list[i] + ans)
return ans
# 新しい底(bの平方根を使って new_base < a となるように)
# 正しいロジック:new_base は b よりも大きく、a よりも小さいもの
new_base = b + 1 # 例:b より大きくすれば必ず底 > 真数になる
log_a = approx_log(b, new_base) # ← aとbの位置が正しくなるように
log_b = approx_log(a, new_base)
ans = log_a / log_b
print(f"log{orig_a}({orig_b}) ≒ {ans}(底を変換して {new_base} にして近似)")
exit(0)
# 変数の初期化
n_list = []
current_base = a
current_value = b
# 割り算と底・真数の入れ替えを繰り返す
for _ in range(10): # 繰り返し回数は適当に設定(必要に応じて調整可)
if current_value < current_base:
# 底と真数を入れ替える
current_base, current_value = current_value, current_base
continue
count = 0
epsilon = 1e-10
while current_value - current_base > epsilon:
current_value /= current_base
count += 1
if count == 0:
n_list.append(1)
break
n_list.append(count)
current_base, current_value = current_value, current_base
# 近似値の計算
# 例: 1/n1 + 1/(n2 + 3) のような形で近似
ans = 0
for i in range(len(n_list)-1, -1, -1):
if ans == 0:
ans = 1 / n_list[i]
else:
ans = 1 / (n_list[i] + ans)
# 符号を元に戻す
ans *= sign
# 結果を表示
print(f"log{orig_a}({orig_b}) ≒ {ans}")
exit(0)
という感じになります。実行してみるといい感じに近似できました!
🔄 GUIで動くNestLog
さて、プログラムは完成したので、最後はGUI化しましょう!
GUI化にあたって、Tkinterを使用しました。
とは言っても、今回はChatGPTさんに頼っていたのでTkinter自体をこの機会に初めて知ったのですが、、、
新たな発見ということで、良しとしておきましょう。
Tkinterについては、こちらが参考になるかと思います。
PythonのTkinterを使ってみる
では、実際のコードは以下のようになります。
import tkinter as tk
# ---- 近似 log 計算ロジック ----
def approx_log(x, base):
n_list = []
current_base = base
current_value = x
for _ in range(10):
if current_value < current_base:
current_base, current_value = current_value, current_base
continue
count = 0
epsilon = 1e-10
while current_value - current_base > epsilon:
current_value /= current_base
count += 1
if count == 0:
n_list.append(1)
break
n_list.append(count)
current_base, current_value = current_value, current_base
ans = 0
for i in range(len(n_list)-1, -1, -1):
ans = 1 / n_list[i] if ans == 0 else 1 / (n_list[i] + ans)
return ans
# ---- GUIロジック ----
def calculate_log():
try:
a = float(entry_base.get())
b = float(entry_value.get())
orig_a, orig_b = a, b
sign = 1
if (a > 1 and b < 1) or (a < 1 and b > 1):
sign = -1
b = 1 / b
if a <= 0 or a == 1 or b < 0:
result_var.set("定義されていない入力です。")
return
if b == 1:
result_var.set(f"log{a}({b}) ≒ 0")
return
if a < b and sign == 1:
new_base = b + 1
log_a = approx_log(b, new_base)
log_b = approx_log(a, new_base)
ans = log_a / log_b
else:
n_list = []
current_base = a
current_value = b
for _ in range(10):
if current_value < current_base:
current_base, current_value = current_value, current_base
continue
count = 0
epsilon = 1e-10
while current_value - current_base > epsilon:
current_value /= current_base
count += 1
if count == 0:
n_list.append(1)
break
n_list.append(count)
current_base, current_value = current_value, current_base
ans = 0
for i in range(len(n_list)-1, -1, -1):
ans = 1 / n_list[i] if ans == 0 else 1 / (n_list[i] + ans)
ans *= sign
result_var.set(f"log{orig_a}({orig_b}) ≒ {ans}")
except:
result_var.set("数値を正しく入力してください。")
# ---- GUIの見た目部分 ----
root = tk.Tk()
root.title("log 近似計算機")
tk.Label(root, text="底 a:").grid(row=0, column=0)
entry_base = tk.Entry(root)
entry_base.grid(row=0, column=1)
tk.Label(root, text="真数 b:").grid(row=1, column=0)
entry_value = tk.Entry(root)
entry_value.grid(row=1, column=1)
tk.Button(root, text="計算", command=calculate_log).grid(row=2, column=0, columnspan=2)
result_var = tk.StringVar()
tk.Label(root, textvariable=result_var, font=("Arial", 14)).grid(row=3, column=0, columnspan=2)
root.mainloop()
ここまで簡単にGUIまでができると、手軽にアイデアを形にできそうですね!鍛錬します。
最後までご覧いただき、ありがとうございました🙇
今回の記事は、勢いのまま駆け抜けていったので、どこかミスや、もっと効率的な方法等ありましたら、教えてください!