質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

447閲覧

Python: 底を指定した対数計算が行われる自作クラスにおいて、かけ算とわり算を実装したい

kensoon

総合スコア48

Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2022/06/25 05:51

前提

DjangoとSympy, Mathjaxを併用して、ランダムな計算プリントを出力するシステムを作っています。そのシステム上で簡単な対数の計算を行えるようにしたいと思い試作を行っている最中です。初めはPythonの組み込みモジュールmathや、外部ライブラリのSympyをそのまま利用しようとしたのですが、いずれも指定した底を保ちながら計算するようにはなっていなかったため、クラスを自作することにしました。

実現したいこと

自作クラス同士の足し算と引き算、自作クラスと整数とのかけ算までは真数部分の計算を行うことで実装ができたのですが、以下の計算の実装にてこずっています。

  • 底が異なるため、そのままの結果になる足し算と引き算

log_2(3) + log_3(5) = log_2(3) + log_3(5)
log_2(3) - log_3(5) = log_2(3) - log_3(5)

  • 自作クラス同士のかけ算と割り算

log_2(3) * log_5(4) = log_2(3) * log_5(4)
log_2(3) / log_5(4) = log_2(3) / log_5(4)
log_2(3) / log_2(3) = 1

イメージとしては、計算ができるときは実行して、できないときはそのまま値を保有しておいて欲しいのですが、それを実装するための方法が思い浮かびません。自作クラスの修正案でもよいですし、前述のmath.logやsympy.logを使って底を保って計算する方法でも構いません。何かしらのご教示をいただけると幸いです。

該当のソースコード

python

1import sympy as sy 2 3class Log: 4 def __init__(self, base_numerator, antilog_numerator, base_denominator=1, antilog_denominator=1): 5 base = sy.Rational(base_numerator, base_denominator) 6 antilog = sy.Rational(antilog_numerator, antilog_denominator) 7 if (base <= 0) or (antilog < 0): 8 raise ValueError(f"base must be more than 0, and antilog must be 0 or more than 0.") 9 10 self.base = base 11 self.antilog = antilog 12 self.other_log_part = 0 # = 0 13 14 def __str__(self): 15 return f"\log_{{{self.base}}} {self.antilog}" 16 17 def __add__(self, other_num): 18 if isinstance(other_num, Log): 19 if self.base == other_num.base: 20 return Log(self.base, self.antilog * other_num.antilog) 21 else: 22 new_antilog = self.base ** other_num 23 return Log(self.base, self.antilog * new_antilog) 24 25 def __radd__(self, other_num): 26 new_antilog = self.base ** other_num 27 return Log(self.base, new_antilog * self.antilog) 28 29 def __sub__(self, other_num): 30 if isinstance(other_num, Log): 31 if self.base == other_num.base: 32 return Log(self.base, self.antilog / other_num.antilog) 33 else: 34 new_antilog = self.base ** other_num 35 return Log(self.base, self.antilog / new_antilog) 36 37 def __rsub__(self, other_num): 38 new_antilog = self.base ** other_num 39 return Log(self.base, new_antilog / self.antilog) 40 41 def __mul__(self, other_num): 42 if not(isinstance(other_num, Log)): 43 new_antilog = self.antilog ** other_num 44 return Log(self.base, new_antilog) 45 46 def __rmul__(self, other_num): 47 new_antilog = self.antilog ** other_num 48 return Log(self.base, new_antilog)

動作確認とその結果

python

1num1 = Log(2, 3) 2num2 = Log(2, 5) 3print(num1 + num2) # \log_{2} 15 4print(num1 - num2) # \log_{2} 3/5 5print(num1 + 2) # \log_{2} 12 6print(2 + num1) # \log_{2} 12 7print(num1 - 2) # \log_{2} 3/4 8print(2 - num1) # \log_{2} 4/3 9print(2 * num1) # \log_{2} 9 10print(num1 * 2) # \log_{2} 9

補足情報(FW/ツールのバージョンなど)

python 3.8.12
sympy 1.9

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

sympyでは、log(3)+log(5)は明示的にlogcombineなどで変換しないとlog(15)になりません。

python

1>>> log(3) + log(5) 2log(3) + log(5) 3>>> logcombine(log(3) + log(5)) 4log(15)

こちらの考え方に従えば、以下のように書くことはできます。

python

1from collections import defaultdict 2from sympy import * 3 4class Log(Function): 5 nargs = 2, 6 @property 7 def base(self): 8 return self.args[0] 9 @property 10 def antilog(self): 11 return self.args[1] 12 13def LogCombine(expr): 14 def f(rv): 15 if rv.is_Add: 16 logs = defaultdict(lambda: S.One) 17 other = [] 18 for arg in rv.args: 19 if isinstance(arg, Log): 20 logs[arg.base] *= arg.antilog 21 else: 22 other.append(arg) 23 for (base, antilog) in logs.items(): 24 if antilog.is_rational and antilog.numerator == 1: 25 other.append(-Log(base, antilog.denominator)) 26 else: 27 other.append(Log(base, antilog)) 28 return Add(*other) 29 if rv.is_Mul and len(rv.args) == 2: 30 if rv.args[0].is_rational and isinstance(rv.args[1], Log): 31 return Log(rv.args[1].base, rv.args[1].antilog ** rv.args[0]) 32 if rv.args[1].is_rational and isinstance(rv.args[0], Log): 33 return Log(rv.args[0].base, rv.args[0].antilog ** rv.args[1]) 34 return rv 35 return bottom_up(expr, f)

実行例は以下の通りです。色々抜けているところはありますが、必要に応じて修正してください。

python

1num1 = Log(2, 3) 2num2 = Log(2, 5) 3num3 = Log(3, 5) 4num4 = Log(5, 4) 5print(LogCombine(num1 + num2)) # Log(2, 15) 6print(LogCombine(num1 - num2)) # Log(2, 3/5) 7print(LogCombine(num1 + 2)) # Log(2, 3) + 2 8print(LogCombine(2 + num1)) # Log(2, 3) + 2 9print(LogCombine(num1 - 2)) # Log(2, 3) - 2 10print(LogCombine(2 - num1)) # 2 - Log(2, 3) 11print(LogCombine(2 * num1)) # Log(2, 9) 12print(LogCombine(num1 * 2)) # Log(2, 9) 13print(LogCombine(num1 + num3)) # Log(2, 3) + Log(3, 5) 14print(LogCombine(num1 - num3)) # Log(2, 3) - Log(3, 5) 15print(LogCombine(num1 * num4)) # Log(2, 3)*Log(5, 4) 16print(LogCombine(num1 / num4)) # Log(2, 3)/Log(5, 4) 17print(LogCombine(num1 / num1)) # 1

投稿2022/07/09 01:20

編集2022/07/09 20:25
actorbug

総合スコア2212

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kensoon

2022/07/11 01:02

コメントありがとうございます。結局自力では解決できず、場当たり的な解決法でとりあえずの実装をしていましたが、ご教示いただいた内容を元に書き直してみたいと思います。かなり煩雑な実装になっていたので本当に助かりました。
guest

0

そのクラスで何をさせたいのかによると思うのですが、「できないときはそのまま値を保有しておいて欲しい」とは、どういう意味でしょう。

「x = Log(2, 3) * Log(3, 5)」としたときに、xに何を入れたいのですか?
もし、Log(2, 3) * Log(3, 5) という式そのものを入れたいとしたら実現不能です。
文字列"Log(2, 3) * Log(3, 5)"なら入れられますが、無意味ですよね。

その計算は結局はできないのですから、そのクラスに「計算できない」という状態を持てるようにしてそれを返すようにするのがいいのではないかと思います。
浮動小数演算のIEEE 754にも、NaN(Not a number)や±∞(無限大/小)があります。
そして、解が何になるのかは、 呼び出した側が決めることだと思います。

投稿2022/06/25 13:39

TakaiY

総合スコア12657

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kensoon

2022/06/27 00:37

コメントありがとうございます。 おっしゃられている通り、文字列だと結局計算できなくなるので意味がなくなります。ご提案いただいた方向含め、少し練り直してみようと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問