【Python】対数計算
対数関数 (logarithm) は指数関数 $y=a^x$ の逆関数として定義されます。
上図のように、ある正数 $R$ について、
\[R=a^r\tag{1}\]
を満たすような $r$ を
\[r=\log_{a}R\tag{2}\]
のように書き表し、$r$ を $R$ の 対数 とよびます。このとき、$a$ を 底、$R$ を 真数 とよびます。
簡単なケースでは、(1) の定義に戻って値を計算できます。たとえば、$\log_{2}16$ は、$16=2^r$ を満たすような $r$ を考えて、$r=4$ を得ます。とはいえ、ほとんどの場合、$R$ から $r$ を得るのは手計算では極めて困難です。たとえば、$\log_3 100$ のように求める値が整数でない場合、昔は対数表を用いて値を探しました。現代では一般に計算機 (コンピュータ) を用いて値を得ます。Python 標準ライブラリの math モジュールには、対数を計算するための関数が揃っています。
たとえば、math.log(x [, base]) は base を底とする対数の値を返します。$2$ を底とする $5$ の対数を計算してみましょう。
# PYTHON_LOG
# In[1]
# 2を底とする5の対数
val = math.log(5, 2)
print(val)
# 2.321928094887362
ネイピア数とよばれる数学定数 $e=2.718...$ を底とする対数
\[\log_{e}x\tag{3}\]
を $x$ の自然対数とよび、数学や科学の広い分野で使用されています。math.log(x [, base]) の base を省略すると底として $e$ を指定したことになり、自然対数の値を返します。$\log_{e} e=1$ を確認してみましょう。ネイピア数 $e$ は math.e で呼び出せます。
# In[2]
# eを底とするeの対数
val = math.log(math.e)
print(val)
# 1.0
底を $a=10$ とする
\[\log_{10}x\tag{4}\]
を常用対数 (common logarithm) とよび、科学技術計算で広く使われています。私たちは $10$ 進法に慣れているので、常用対数は感覚的にわかりやすいという利点があります。たとえば、$\log_{10} 1000$ は $3$ ですが、$\log_{10} 1050$ のような対数を見たときにも、$3$ より少し大きい数値であると推測できます。x の常用対数は math.log(x, 10) でも計算できますが、Python には常用対数専用の math.log10() が用意されているので、それを使って $\log_{10} 1050$ を計算してみましょう。
# In[3]
# 1050の常用対数
val = math.log10(1050)
print(val)
# 3.0211892990699383
$2$ を底とする対数を二進対数とよび、情報理論や計算機科学の分野でよく用いられます。x の二進対数は math.log(x, 2) で計算できますが、二進対数専用の math.log2() も用意されています。コンピューターのバイト計算でおなじみの $2^{10}=1024$ から、$1024$ の二進対数 $\log_{2}1024$ が $10$ となることを確認してみましょう。
# In[4]
# 1024の二進対数
val = math.log2(1024)
print(val)
# 10.0
対数関数 $y = \log_{a} x$ が $x \rightarrow \infty$ の極限で $+\infty$ となることを反映し、x に無限大 inf を渡すと、inf が返るようになっています。
# In[5]
# 3を底とする+∞の対数
val = math.log(float("inf"), 3)
print(val)
# inf
実数の対数には真数を正の値に限定するという「真数条件」があります。math.log() の第1引数に 0 や負の数を渡すと ValueError が返ります。
# In[6]
# -1の常用対数
val = math.log(-1)
print(val)
# ValueError: math domain error
戻り値を複素数に拡大すれば (複素解析学の分野では)、負数の対数を計算できますが、math モジュールでは扱えません。 負数の対数を計算するときは、複素数計算用モジュール cmath の対数関数を使います。
# In[7]
# -1の常用対数
val = cmath.log(-1)
print(val)
# 3.141592653589793j
あるいは外部パッケージ NumPy を用いて負数の対数を計算することもできます。ただし、真数 x に負の浮動小数点数をそのまま入れるとエラーになるので、x には必ず complex 型の値を渡すようにしてください。たとえば、-1 の自然対数を計算したいときは numpy.log() に -1 + 0j を渡します。
# In[8]
import numpy as np
# -1の自然対数を計算
val = np.log(-1+0j)
print(val)
# 3.141592653589793j
cmath や NumPy の対数計算関数には complex 型数値(複素数)を引数に渡すことができます。複素解析学において、複素変数 $z$ の対数 $w = \log z$ は複素変数の指数関数 $z = \exp(w)$ の逆関数として定義されます。これは無限個の分枝
\[w_k=\log r+i(\theta+2k\pi)\]
をもつ無限多価関数ですが、$z$ 平面を切断して、その平面上で1つの分枝を選択することによって一価関数として扱えます。たとえば $w = \log i$ の値は $(2k + 1/2)\pi i$ であり、任意定数 $k$ によって無限の値を取り得ますが、Python では一意の値 $\pi i/2$ を返すようになっています。cmath.log() を使って虚数単位 $i$ の対数を求めてみます。
# In[9]
# eを底とする虚数iの対数
val = cmath.log(1j)
print(val)
# 1.5707963267948966j
対数関数 y=logx のグラフ
NumPy の対数関数には配列を渡せるので、ループ処理なしで、複数変数に対してまとめて対数の値を計算できます。この機能はグラフをプロットするのに適しています。以下に Matplotlib を使って対数関数 $y=\log{x}$ のグラフを描画させるサンプルコードを載せておきます。
# LOG_PLOT
# In[1]
import matplotlib.pyplot as plt
# フィギュアを設定
fig = plt.figure()
# グリッド線を表示
plt.style.use("ggplot")
# グラフ描画領域を追加
ax = fig.add_subplot(111)
# グラフタイトルを設定
ax.set_title("y = log(x)", fontsize = 16)
# x軸, y軸のラベルを設定
ax.set_xlabel("x", fontsize = 16)
ax.set_ylabel("y", fontsize = 16)
# xのデータを用意
x = np.arange(0.1, 4, 0.1)
# yのデータを用意
y = np.log(x)
# データをプロット
ax.plot(x, y)
底の変換
NumPy には e, 2, 10 という 3 種類の底の対数関数しか用意されていません。任意の底 $a$ の対数を計算するときには底の変換公式
\[\log_a x=\frac{\log x}{\log a}\]を利用します。
# CHANGE_BASE
# In[1]
import numpy as np
# 3を底とする9の対数を計算
val = np.log(9) / np.log(3)
print(val)
# 2.0
以下は Python の各種ライブラリで扱える対数計算用関数の一覧です。
math.log()
math.log(x [, base]) は base を底とした真数 x の対数を返します。
# MATH_LOG
# In[1]
# eを底とするeの対数
val = math.log(math.e)
print(val)
# 1.0
オプション引数の base を省略した場合、ネイピア数 e を指定したことになります。
# In[2]
import math
# eを底とする5の対数
val = math.log(5)
print(val)
# 1.6094379124341003
math.log10()
math.log10(x) は x の常用対数を返します。
# MATH_LOG10
# In[1]
import math
# 10を底とする100の対数を計算
val = math.log10(100)
print(val)
# 2.0
math.log2()
math.log2(x) は 2 を底とした x の対数を返します。
# MATH_LOG2
# In[1]
import math
# 2を底とした8の対数
val = math.log2(8)
print(val)
# 3.0
math.log1p()
math.log1p(x) は、e を底とした 1 + x の対数を計算します。この関数は真数が 1 に近い値のときの対数を正確に計算するために用意されたものです。たとえば、math.log1p(0.01) は math.log(1.01) よりも精度の良い値を返します。
# MATH_LOG1P
# In[1]
import math
# eを底とする1.01の対数
val = math.log1p(0.01)
print(val)
# 0.009950330853168083
cmath.log()
cmath.log(x[, base]) は base を底とする x の対数を計算します。math ではサポートしていない負数の対数を計算できます。引数の型に関わらず戻り値はすべて複素数型です。
# CMATH_LOG
# In[1]
# 3を底とする19の対数
val = cmath.log(19, 3)
print(val)
# (2.680143859246375+0j)
base を省略すると自然対数を計算します。
# In[2]
# 100の自然対数
val = cmath.log(100)
print(val)
# (4.605170185988092+0j)
負数の対数も計算できます。
# In[3]
# -10の常用対数
val = cmath.log(-10,10)
print(val)
# (1+1.3643763538418412j)
複素数の対数も計算できます。
# In[4]
# eを底とする複素数1+iの対数
val = cmath.log(1+1j)
print(val)
# (0.34657359027997264+0.7853981633974483j)
cmath.log10()
cmath.log10(x) は x の常用対数を返します。戻り値はすべて複素数型です。
# CMATH_LOG10
# In[1]
# 5の常用対数
val = cmath.log10(5)
print(val)
# (0.6989700043360187+0j)
x に負の数を渡すこともできます。
# In[2]
# -100の常用対数
val = cmath.log10(-100)
print(val)
# (2+1.3643763538418412j)
複素数の対数も計算できます。
# In[3]
# 2+3iの常用対数
val = cmath.log10(2+3j)
print(val)
# (0.5569716761534184+0.42682189085546657j)
numpy.log()
numpy.log(x) はネイピア数 e を底とした x の対数を返します。
# NUMPY_LOG
# In[1]
import numpy as np
# 変数の入った配列を作成
arr = np.array([1, np.e, 10])
# 配列の各要素に対してeを底とする対数を計算
log_arr = np.log(arr)
print(log_arr)
# [0. 1. 2.30258509]
numpy.log10()
numpy.log(x) は x の常用対数を返します。
# NUMPY_LOG10
# In[1]
import numpy as np
# 変数の入った配列を作成
arr = np.array([1, 10, 0.1])
# 配列の各要素に対して常用対数を計算
log_arr = np.log10(arr)
print(log_arr)
# [0. 1. -1.]
numpy.log2()
numpy.log2(x) は 2 を底とした x の対数を返します。
# NUMPY_LOG2
# In[1]
import numpy as np
# 変数の入った配列を作成
arr = np.array([1, 2, 4, 0.5])
# 配列の各要素に対して2を底とした対数を計算
log_arr = np.log2(arr)
print(log_arr)
# [0. 1. 2. -1.]
numpy.log1p()
numpy.log1p(x) は e を底とした 1 + x の対数を計算します。この関数は真数が 1 に近い値のときの対数を正確に計算するために用意されています。たとえば、numpy.log1p(0.001) は numpy.log(1.001) よりも高い精度の値を返します。
# NUMPY_LOG1P
# In[1]
import numpy as np
# eを底とする1.001の対数を計算
val = np.log1p(0.001)
print(val)
# 0.0009995003330835331
sympy.log()
sympy.log(x, b) は b を底とする対数関数を計算します。ただし、計算結果はネイピア数 e を底とする対数に変換して表示されます。引数 b を省略した場合は底としてネイピア数 e を指定したことになります。
# SYMPY_LOG
# In[1]
import sympy
# 記号xを定義
sympy.var('x y')
# 2を底とするx*yの対数
expr = sympy.log(x*y, 2)
print(expr)
# log(x*y)/log(2)
第 1 引数に複素数を渡すこともできます。
# In[2]
# 虚数単位の記号を定義
i = sympy.I
# f(x,y)=log(ix+y)
f = sympy.log(i*x+y)
# f(-i,1)を計算
val = f.subs([(x,-i),(y,1)])
print(val)
# log(2)
sympy.logcombine()
sympy.logcombine() は対数関数の公式を使って数式をまとめます。
# SYMPY_LOG_COMBINE
# In[1]
from sympy import *
var ("a b c x", positive=True)
expr = log(a) + log(b) + c*log(x)
# 対数関数の公式を使って数式をまとめる
expr = logcombine(expr, force=True)
print(expr)
# log(a*b*x**c)
下記は誤植と思われますので、ご確認ください。
(4) 式の下の文で、3 より少し多い数値 → 3 より少し大きい数値
「多い」はおかしな表現ですね。
修正しておきました m(_ _)m
ChatGPTが対数関数 log(x) を説明します
[ChatGPT] 対数関数 log(x) は、ある数値 x を別の数値に変換する関数です。具体的には、底となる数値を指定して x の対数を求めることができます。例えば、log(100) を考えてみましょう。一般的に、対数関数は底を省略すると、底が10であるとみなされます。つまり、log(100) は、底が10であると解釈されます。対数関数は、指数と逆の操作を行うと考えることができます。指数関数では、10 を 2 乗すると 100 になりますが、対数関数では、逆に 100 を 10 を底として対数を取ることで、指数が 2 になります。具体的には、log(100) の解は 2 になります。これは、10 を何乗したら 100 になるかを求めていることになります。また、対数関数は、数値の大きさを比較するためにも使われます。例えば、log(100) と log(1000) を比較すると、前者は 2 であり、後者は 3 なので、対数関数を使って 100 と 1000 の大小関係を知ることができます。つまり、対数関数 log(x) は、ある数値 x を別の数値に変換する関数であり、数値の大きさの比較や指数と逆の操作を行うために使われるものです。