2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NestLog法〜logの近似計算アルゴリズムを考え、勢いでChatGPTにGUI化までしてもらった話〜

Last updated at Posted at 2025-04-23

はじめまして!今回が初投稿となります!

内容はほぼタイトル通りなのですが、logの近似アルゴリズムを考え、ChatGPTにGUI化までしてもらった記録です。ハイになってChatGPTと一緒にNestLog法という名前までつけてしまいました:sweat_smile:
(とても簡単なアルゴリズムなので、ほぼ間違いなく誰かが既に考えていると思うのですが)
正式名称あったら教えてください

📊 概要

このアルゴリズムは、底 > 真数の条件のもと、連分数の構造を用いて実際のlog値を近似的に求めます。

例:log_10(2)を近似しようとした場合、以下のようになります。
47019B98-3ED9-41A8-A9DF-F121CB8D26AA_1_201_a.jpeg

割る回数が少ないので精度は微妙ですが、これを繰り返せば、より精度良く近似できるはずだと考えました。

log_a(b)の近似手順としては、

  1. 底をbとして底の変換公式を用いる(分母はlog_b(a)の形になる)
  2. 真数を底の値で割っていく(真数を小さくしながら、+1を括り出すことができる)
  3. 真数<底になったら、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を使ってみる

では、実際のコードは以下のようになります。

GUI
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()

動作させてみると、以下のようになりました!!
nestlog.gif

ここまで簡単にGUIまでができると、手軽にアイデアを形にできそうですね!鍛錬します。

最後までご覧いただき、ありがとうございました🙇
今回の記事は、勢いのまま駆け抜けていったので、どこかミスや、もっと効率的な方法等ありましたら、教えてください!

2
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?