このページではPythonで分数を扱う方法について解説します。
Contents
fractionsモジュール
fractionsモジュールとFractionの生成
Pythonの標準ライブラリにはfractionsという分数を扱うモジュールが用意されています。
https://docs.python.org/ja/3/library/fractions.html
このモジュールのFraction型で分数を表現することが可能で、インスタンス生成時に引数で分子、分母をしていします。
例えば1/2と2/3を扱いたい場合、以下のようにFractionを生成します。
from fractions import Fraction # 1/2 frac1 = Fraction(1, 2) # 2/3 frac2 = Fraction(2, 3) print(frac1, frac2) # 1/2 2/3
なお、約分はインスタンス生成時に自動で実行されます。
my_frac = Fraction(2, 4) print(my_frac) # 1/2
分数の計算
生成したFraction型は演算が定義されているためそのまま計算できます。以下のコードは先程生成した分数に対して四則演算を行っています。
# 上のコードの続き frac3 = frac1 + frac2 print(frac3) # 7/6 frac4 = frac1 - frac2 print(frac4) # -1/6 frac5 = frac1 * frac2 print(frac5) # 1/3 frac6 = frac1 / frac2 print(frac6) # 3/4
無論以下のようにintやfloat等とも演算可能です。
num = frac1 * 1.2 print(num) # 0.6
分子と分母を取得
分子、分母を取得するためにプロパティが用意されています。
- numerator:分子
- denominator:分母
またPython3.8以降ではas_integer_ratioメソッドを使用して分子、分母のタプルを得ることができます。
from fractions import Fraction my_frac = Fraction(3, 7) print(my_frac.numerator, my_frac.denominator) # 3 7 print(my_frac.as_integer_ratio()) # (3, 7)
小数を分数に変換
Fractionの引数に小数を指定すると近い値の分数に変換されます。
from math import pi pfrac = Fraction(pi) # Fraction(884279719003555, 281474976710656)
円周率の有理数による近似値として紀元前3世紀にアルキメデスが発見した 22/7がよく知られています。こういった簡単な分数に直す方法としてlimit_denominatorメソッドを使用すると引数で指定した最大分母サイズで近似値を得ることが可能です。
limit_denominator(*max_denominator=1000000*)
円周率について最大分母を50以下で近似すると、アルキメデスの22/7が得られることが確認できます。
from fractions import Fraction pfrac2 = pfrac.limit_denominator(50) print(pfrac2) # Fraction(22, 7)
floatへの変換
また、逆にfloatの引数に指定するとfloat型を得ることが可能です。
float(pfrac2) # 3.142857142857143
NumPy配列の要素をFractionに変換
引数を指定していない場合、0/1、つまり0と同等のFractionオブジェクトが取得できます。これと演算することによりNumPyの要素をFractionに変換する方法があります。
以下のコードでは、行列にスカラーで割り算をしていますが要素を一旦0Fractionとの演算により変換し誤差を抑えています。
from fractions import Fraction import numpy as np z = Fraction() A = np.array([[1, 2],[3, 4]]) A2 = A + z # array([[Fraction(1, 1), Fraction(2, 1)], # [Fraction(3, 1), Fraction(4, 1)]], dtype=object) D = A2 / 2 print(D) # array([[Fraction(1, 2), Fraction(1, 1)], # [Fraction(3, 2), Fraction(2, 1)]], dtype=object)
補足1 処理の遅さ
有理数のみ扱う場合は誤差が一番なく簡単に扱える便利なFractionですが、当然ながら保有している情報量が多いためfloatと比較して遅いと言われています。10000回ランダムに生成し掛け算した場合の計測コードを補足として掲載します。
import time import random from fractions import Fraction t1 = time.time() for i in range(100000): float1 = random.randint(1, 100) / random.randint(1, 100) float2 = random.randint(1, 100) / random.randint(1, 100) float3 = float1 * float2 elapsed_time1 = time.time() - t1 print(f'float elapsed_time:{elapsed_time1}') t2 = time.time() for i in range(100000): frac1 = Fraction(random.randint(1, 100), random.randint(1, 100)) frac2 = Fraction(random.randint(1, 100), random.randint(1, 100)) frac3 = frac1 * frac2 elapsed_time2 = time.time() - t2 print(f'frac elapsed_time:{elapsed_time2}')
私の手元の環境では以下の違いがありました。
実行結果
float elapsed_time:0.21874618530273438 frac elapsed_time:0.5316271781921387
補足2 SymPy
標準ライブラリ以外にSymPyという数式演算のライブラリがあります。代数方程式を解いたり微積計算をすることができる高機能なライブラリで、分数を扱うことも可能なので補足として簡単に紹介します。
https://www.sympy.org/en/index.html
以下のコマンドでインストールすることができます
pip install sympy
Rationalで分数を扱うことができます。
from sympy import Rational my_frac = Rational(1, 3) print(my_frac)
前述の通り様々な機能がありますが、分数においては標準ライブラリのFractionよりリストなどの要素の場合でも文字列表現が簡潔で見やすいというメリットがあります。
print([my_frac]) # 1/3 # Fractionの場合は[Fraction(1, 3)]となる