【小学校算数 × プログラミング】Pythonで「分数の計算(約分と通分)」の理解を深めよう!

分数の計算は、日常生活や科学技術の中で頻繁に使われる基本的な数学的スキルですが、小学校の算数で躓きやすい単元の一つでもあります。

分数の計算において「約分と通分」は切っても切り離せない関係にあります。さらに約分は最大公約数と、通分は最小公倍数と密接な関係があります。

約分は、分子と分母が共有する最大公約数で割ることにより、分数を最も簡単な形にすることです。一方、通分は、異なる分数を加減するために、最小公倍数を用いて同じ分母を持つようにすることです。

この記事では、普段頭の中や紙の上で行っている約分や通分を含む分数の計算をPythonのプログラミングを通して整理し、具体的なコード例を通じてそれらの概念を深く理解していきましょう。

Pythonにおける分数の取り扱い

Pythonでは、標準ライブラリや外部ライブラリを使用して分数の計算を行うことができますが、この記事では、Pythonのクラスを用いて分数(fraction)の表現を以下のように定義しています。

  • 真分数・仮分数(\(\frac{分子}{分母}\)):Frac(分子,分母)
  • 帯分数(\({整数}\frac{分子}{分母}\)):Frac(整数,分子,分母) 

Pythonのクラスは、オブジェクト指向プログラミングの一部であり、データと機能を一緒にまとめる手段を提供します。新しいクラスを作成すると、その型の新しいインスタンスが作成できます。各インスタンスは、状態を維持するための属性を持つことができます。また、インスタンスは、状態を変更するためのメソッド(クラスによって定義された)を持つこともできます。

オブジェクト指向プログラミングの詳細は、関連の書籍やブログを参照いただくことをお勧めします。

次のクラスでは、整数部分、分子、分母を引数として受け取り、それらを属性として保持します。また、整数部、分子、分母を返すメソッドも持っています。

# 分数の表現
class Frac:
    def __init__(self, *args):
        if len(args) == 2:
            # 真分数または仮分数
            self.integer = 0
            self.numerator, self.denominator = args
        elif len(args) == 3:
            # 帯分数
            self.integer, self.numerator, self.denominator = args
        else:
            raise ValueError("引数の数が無効です。")

    def __str__(self):
        if self.integer == 0:
            return f"{self.numerator}/{self.denominator}"
        else:
            return f"{self.integer} {self.numerator}/{self.denominator}"

    def seisu(self): # 整数部を返す
        return self.integer

    def bunsi(self): # 分子を返す
        return self.numerator

    def bunbo(self): #分母を返す
        return self.denominator

このクラスは、真分数・仮分数(\(\frac{分子}{分母}\))と帯分数(\({整数}\frac{分子}{分母}\))の両方を表現することができます。真分数・仮分数はFrac(分子, 分母)として、帯分数はFrac(整数, 分子, 分母)としてインスタンス化します。

__init__メソッドはクラスのコンストラクタで、インスタンスが作成されるときに自動的に呼び出されます。このメソッドでは、引数の数に応じて真分数・仮分数または帯分数を作成します。引数が2つの場合は真分数・仮分数、3つの場合は帯分数を作成します。それ以外の場合は、ValueError例外を発生させます。

__str__メソッドは、インスタンスを文字列として表現するためのメソッドです。このメソッドは、インスタンスが文字列に変換されるとき(例えば、print()関数で出力するとき)に自動的に呼び出されます。

seisubunsibunboメソッドはそれぞれ整数部、分子、分母を返すメソッドです。

次のコードでは、このクラスの使用例を示しています。

try:
    frac1 = Frac(2,3)  # 真分数または仮分数
    print(frac1)  # "2/3" を出力

    frac2 = Frac(1, 2, 3)  # 帯分数
    print(frac2)  # "1 2/3" を出力
    print(frac2.seisu()) # 整数部"1"を出力
    print(frac2.bunsi()) # 分子"2"を出力
    print(frac2.bunbo()) # 分母"3"を出力

except ValueError as e:
    print(f"例外が発生しました: {e}")

このコードではまず、真分数・仮分数(\(\frac{2}{3}\))のインスタンスを作成し、その文字列表現を出力します。次に、帯分数(\(1 \frac{2}{3}\))のインスタンスを作成し、その文字列表現と整数部、分子、分母をそれぞれ出力します。これらの操作はtryブロック内で行われているため、もし何らかの例外(この場合はValueError)が発生したら、その例外はexceptブロックにより、例外が発生したことを示すメッセージが出力されます。

次に仮分数と帯分数を相互に変換するコードを示します。

# 仮分数を帯分数に変換する関数
def improper_to_mixed(frac):
    integer = frac.numerator // frac.denominator
    numerator = frac.numerator % frac.denominator
    return Frac(integer, numerator, frac.denominator)

# 帯分数を仮分数に変換する関数
def mixed_to_improper(frac):
    numerator = frac.integer * frac.denominator + frac.numerator
    return Frac(numerator, frac.denominator)

# 使用例
frac1 = Frac(7, 2)  # 仮分数 7/2
frac2 = improper_to_mixed(frac1)
print(frac2)  # 帯分数 3 1/2

frac3 = Frac(3, 1, 2)  # 帯分数 3 1/2
frac4 = mixed_to_improper(frac3)
print(frac4)  # 仮分数 7/2

このコードは、仮分数と帯分数を相互に変換するための2つの関数を定義しています。

  1. improper_to_mixed関数は、仮分数を帯分数に変換します。この関数では、分子を分母で割った商を整数部とし、分子を分母で割った余りを新しい分子とします。そして、新しい整数部、分子、元の分母を持つFracオブジェクトを返します。
  2. mixed_to_improper関数は、帯分数を仮分数に変換します。この関数では、整数部と分母を掛けた値に分子を加えて新しい分子を作ります。そして、新しい分子と元の分母を持つFracオブジェクトを返します。

使用例では、まずFrac(7, 2)で仮分数(\(\frac{7}{2}\))を作成し、それをimproper_to_mixed(frac1)で帯分数(\(3 \frac{1}{2}\))に変換しています。次に、Frac(3, 1, 2)で帯分数(\(3 \frac{1}{2}\))を作成し、それをmixed_to_improper(frac3)で仮分数(\(\frac{7}{2}\))に変換しています。これらの結果は、print関数で表示されます。

約分とは

約分とは、分数の分子と分母が共通の約数で割れる場合に、それを使って分数を簡単な形にすることです。例えば、\(\frac{8}{12}\)という分数があるとします。この分数の分子(8)と分母(12)は共通の約数(4)で割ることができます。したがって、この分数は8÷4 / 12÷4 = \(\frac{2}{3}\)と約分することができます。Pythonで約分を行う関数を作成してみましょう。

# 最大公約数を求める関数
def get_gcd(a, b):
    while b != 0:
        a, b = b, a % b
    return a

# 約分を行う関数
def reduction_of_a_fraction(frac):
    gcd = get_gcd(frac.bunsi(), frac.bunbo())
    reduced_numerator = frac.bunsi() // gcd
    reduced_denominator = frac.bunbo() // gcd
    return Frac(frac.seisu(), reduced_numerator, reduced_denominator)

# 使用例
frac1 = reduction_of_a_fraction(Frac(4,8))
print(frac1)

このコードは、分数を約分するための reduction_of_a_fraction関数と、そのために必要な最大公約数を求める get_gcd関数を定義しています。

  1. get_gcd関数は、2つの数 ab最大公約数を求めます。この関数では、ユークリッドの互除法を用いて最大公約数を求めています。ユークリッドの互除法は、2つの数のうち大きい方から小さい方を引き続けることで最大公約数を求める方法ですが、この関数では引き算の代わりに余りの計算を行っています。
  2. reduction_of_a_fraction関数は、分数 frac約分します。この関数では、まず get_gcd関数を使って分数の分子と分母の最大公約数を求めます。次に、その最大公約数で分子と分母をそれぞれ割ることで、約分後の分子と分母を求めます。そして、その約分後の分子と分母を持つ新しいFracオブジェクトを作成し、それを返します。

使用例では、まずFrac(4, 8)で\(\frac{4}{8}\)を作成し、それをreduction_of_a_fraction関数で約分しています。そして、その結果をprint関数で表示しています。

最大公約数については次の記事をご覧ください。

関連記事

最大公約数は、2つ以上の整数の共通の約数のうち最も大きなものを指します。例えば、整数48と36の最大公約数は12です。これは、48と36の共通の約数であり、それ以上の数では割り切れないことを意味しています。 こちらの記事では、効率的[…]

最大公約数の求め方をプログラミングで理解しよう

通分とは

通分とは、2つ以上の分数が足し算や引き算できるように、同じ分母にすることです。例えば、\(\frac{2}{3}\) と \(\frac{1}{2}\) という2つの異なる分数があるとします。これらの足し算や引き算を行うためには、まずこれらの分数を通分する必要があります。これらの分数の通分後の形は \(\frac{4}{6}\) と \(\frac{3}{6}\) です。Pythonで通分を行う関数を作成してみましょう。

# 最小公倍数を求める関数
def get_lcm(a,b):
    lcm = int((a * b) / get_gcd(a, b))
    return lcm

# 通分を行う関数
def reduction_to_a_common_denominator(frac1,frac2):
    lcm = get_lcm(frac1.bunbo(),frac2.bunbo())
    frac1_numerator = (frac1.bunsi() + frac1.seisu() * frac1.bunbo()) * (lcm // frac1.bunbo())
    frac2_numerator = (frac2.bunsi() + frac2.seisu() * frac2.bunbo()) * (lcm // frac2.bunbo())
    if frac1.seisu() != 0:
        frac1 = improper_to_mixed(Frac(frac1_numerator, lcm))
    else:
        frac1 = Frac(frac1_numerator, lcm)
    if frac2.seisu() != 0:
        frac2 = improper_to_mixed(Frac(frac2_numerator, lcm))
    else:
        frac2 = Frac(frac2_numerator, lcm)
    return frac1, frac2

# 使用例
frac1, frac2 = reduction_to_a_common_denominator(Frac(1,2,3), Frac(4,5))
print(frac1)
print(frac2)

このコードは、2つの分数を通分するための reduction_to_a_common_denominator関数を定義しています。また、そのために必要な最小公倍数を求める get_lcm関数も定義しています。

  1. get_lcm関数は、2つの数 ab最小公倍数を求めます。最小公倍数は、2つの数の積をその最大公約数で割ったもので求めることができます。この関数では、その計算を行い、最小公倍数を返しています。
  2. reduction_to_a_common_denominator関数は、2つの分数 frac1frac2通分します。この関数では、まず get_lcm関数を使って2つの分数の分母の最小公倍数を求めます。次に、その最小公倍数と各分数の分母との比を求め、それを各分数の分子(整数部を分母で掛けたものと分子を足したもの)に掛けることで、通分後の分子を求めます。そして、その通分後の分子と最小公倍数を持つ新しい Fracオブジェクトを2つ作成し、それらを返します。

通分後の分子が分母より大きい場合(つまり、通分後の分数が仮分数になる場合)、その分数を帯分数に変換しています。そのため、元の分数が帯分数であった場合は帯分数で、元の分数が仮分数であった場合は仮分数で結果を返します。

使用例では、まずFrac(1, 2, 3)Frac(3, 4)で2つの分数を作成し、それらをreduction_to_a_common_denominator関数で通分しています。そして、その結果をprint関数で表示しています。

最小公倍数については次の記事をご覧ください。

関連記事

数学を学ぶ上で、最小公倍数という概念は重要な役割を果たします。最小公倍数とは、複数の整数に共通の倍数のうち、最も小さいもののことです。例えば、 6 と 9 の最小公倍数は 18 です。 最小公倍数は、分数の通分や方程式の解法など、さ[…]

最小公倍数の求め方をPythonプログラミングで理解しよう

分数の計算

前述の分数を表すクラスや約分、通分などの関連する関数を用いてPythonで分数の計算(四則演算)を行う具体的なコードを作成します。

# 分数の加算
def add_fractions(frac1, frac2):
    frac1, frac2 = reduction_to_a_common_denominator(frac1, frac2)
    numerator = frac1.bunsi() + frac2.bunsi()
    return reduction_of_a_fraction(Frac(numerator, frac1.bunbo()))

# 分数の減算
def subtract_fractions(frac1, frac2):
    frac1, frac2 = reduction_to_a_common_denominator(frac1, frac2)
    numerator = frac1.bunsi() - frac2.bunsi()
    return reduction_of_a_fraction(Frac(numerator, frac1.bunbo()))

# 分数の乗算
def multiply_fractions(frac1, frac2):
    numerator = (frac1.bunsi() + frac1.seisu() * frac1.bunbo()) * (frac2.bunsi() + frac2.seisu() * frac2.bunbo())
    denominator = frac1.bunbo() * frac2.bunbo()
    return reduction_of_a_fraction(Frac(numerator, denominator))

# 分数の除算
def divide_fractions(frac1, frac2):
    numerator = (frac1.bunsi() + frac1.seisu() * frac1.bunbo()) * frac2.bunbo()
    denominator = (frac2.bunsi() + frac2.seisu() * frac2.bunbo()) * frac1.bunbo()
    return reduction_of_a_fraction(Frac(numerator, denominator))

# 使用例
frac1 = Frac(3, 4)
frac2 = Frac(5, 6)

print("加算: ", add_fractions(frac1, frac2))
print("減算: ", subtract_fractions(frac1, frac2))
print("乗算: ", multiply_fractions(frac1, frac2))
print("除算: ", divide_fractions(frac1, frac2))

このコードは、2つの分数の加算、減算、乗算、除算を行うための4つの関数を定義しています。

  1. add_fractions関数は、2つの分数 frac1frac2加算を行います。この関数では、まずreduction_to_a_common_denominator関数を使って2つの分数を通分し、その後で分子を足し合わせて新しい分数を作成します。最後に、その分数をreduction_of_a_fraction関数で約分して結果を得ます。
  2. subtract_fractions関数は、2つの分数 frac1frac2減算を行います。この関数では、まずreduction_to_a_common_denominator関数を使って2つの分数を通分し、その後で分子を引き算して新しい分数を作成します。最後に、その分数をreduction_of_a_fraction関数で約分して結果を得ます。
  3. multiply_fractions関数は、2つの分数 frac1frac2乗算を行います。この関数では、まず各分数の整数部と分母を掛けたものと分子を足したもの(つまり、各分数を仮分数に変換したもの)同士を掛け合わせて新しい分子を作成します。そして、各分数の分母同士を掛け合わせて新しい分母を作成します。最後に、その新しい分子と新しい分母からなる新しい分数を作成し、それをreduction_of_a_fraction関数で約分して結果を得ます。
  4. divide_fractions関数は、2つの分数 frac1frac2除算を行います。この関数では、まず各分数の整数部と分母を掛けたものと分子を足したもの(つまり、各分数を仮分数に変換したもの)と逆数(つまり、元々の仮分数の分子と分母を入れ替えたもの)を掛け合わせて新しい分子を作成します。そして、各分数の分母と逆数(つまり、元々の仮分数の分子と分母を入れ替えたもの)を掛け合わせて新しい分母を作成します。最後に、その新しい分子と新しい分母からなる新しい分数を作成し、それをreduction_of_a_fraction関数で約分して結果を得ます。

使用例では、まず Frac(3, 4)Frac(5, 6) で2つの分数を作成し、それらを各関数で計算しています。そして、その結果をprint関数で表示しています。

これらの関数はすべて約分された結果を返すようになっています。そのため、これらの関数で計算した結果は最も簡単な形になっています。

以上がPythonで実装する基本的な四則演算になります。これらはすべてPythonプログラム上で動作するため、手計算や電卓で行うよりも高速に計算することが可能です。さらにPythonプログラムでは計算過程も保存することが可能なため、計算過程を確認しながら学習することも可能です。

プログラム全体

プログラムの全文を掲載します。このプログラムを「Google Colaboratory」にコピペして実行することができます。「Google Colaboratory」については次の記事もご参照ください。

関連記事

Pythonの開発と実行には様々なツールがありますが、、初心者にとって特におすすめなのが「Google Colab(正式名称「Colaboratory」)」です。 「Google Colab」は、ブラウザ上で動作するクラウドベースの[…]

Pythonの開発と実行におすすめのツールを比較
# 分数のクラス
class Frac:
    def __init__(self, *args):
        if len(args) == 2:
            # 真分数または仮分数
            self.integer = 0
            self.numerator, self.denominator = args
        elif len(args) == 3:
            # 帯分数
            self.integer, self.numerator, self.denominator = args
        else:
            raise ValueError("引数の数が無効です。")

    def __str__(self):
        if self.integer == 0:
            return f"{self.numerator}/{self.denominator}"
        else:
            return f"{self.integer} {self.numerator}/{self.denominator}"

    def seisu(self): # 整数部を返す
        return self.integer

    def bunsi(self): # 分子を返す
        return self.numerator

    def bunbo(self): #分母を返す
        return self.denominator

# 仮分数を帯分数に変換する関数
def improper_to_mixed(frac):
    integer = frac.numerator // frac.denominator
    numerator = frac.numerator % frac.denominator
    return Frac(integer, numerator, frac.denominator)

# 帯分数を仮分数に変換する関数
def mixed_to_improper(frac):
    numerator = frac.integer * frac.denominator + frac.numerator
    return Frac(numerator, frac.denominator)

# 最大公約数を求める関数
def get_gcd(a, b):
    while b != 0:
        a, b = b, a % b
    return a

# 約分を行う関数
def reduction_of_a_fraction(frac):
    gcd = get_gcd(frac.bunsi(), frac.bunbo())
    reduced_numerator = frac.bunsi() // gcd
    reduced_denominator = frac.bunbo() // gcd
    return Frac(frac.seisu(), reduced_numerator, reduced_denominator)

# 最小公倍数を求める関数
def get_lcm(a,b):
    lcm = int((a * b) / get_gcd(a, b))
    return lcm

# 通分を行う関数
def reduction_to_a_common_denominator(frac1,frac2):
    lcm = get_lcm(frac1.bunbo(),frac2.bunbo())
    frac1_numerator = (frac1.bunsi() + frac1.seisu() * frac1.bunbo()) * (lcm // frac1.bunbo())
    frac2_numerator = (frac2.bunsi() + frac2.seisu() * frac2.bunbo()) * (lcm // frac2.bunbo())
    if frac1.seisu() != 0:
        frac1 = improper_to_mixed(Frac(frac1_numerator, lcm))
    else:
        frac1 = Frac(frac1_numerator, lcm)
    if frac2.seisu() != 0:
        frac2 = improper_to_mixed(Frac(frac2_numerator, lcm))
    else:
        frac2 = Frac(frac2_numerator, lcm)
    return frac1, frac2

# 分数の加算
def add_fractions(frac1, frac2):
    frac1, frac2 = reduction_to_a_common_denominator(frac1, frac2)
    numerator = frac1.bunsi() + frac2.bunsi()
    return reduction_of_a_fraction(Frac(numerator, frac1.bunbo()))

# 分数の減算
def subtract_fractions(frac1, frac2):
    frac1, frac2 = reduction_to_a_common_denominator(frac1, frac2)
    numerator = frac1.bunsi() - frac2.bunsi()
    return reduction_of_a_fraction(Frac(numerator, frac1.bunbo()))

# 分数の乗算
def multiply_fractions(frac1, frac2):
    numerator = (frac1.bunsi() + frac1.seisu() * frac1.bunbo()) * (frac2.bunsi() + frac2.seisu() * frac2.bunbo())
    denominator = frac1.bunbo() * frac2.bunbo()
    return reduction_of_a_fraction(Frac(numerator, denominator))

# 分数の除算
def divide_fractions(frac1, frac2):
    numerator = (frac1.bunsi() + frac1.seisu() * frac1.bunbo()) * frac2.bunbo()
    denominator = (frac2.bunsi() + frac2.seisu() * frac2.bunbo()) * frac1.bunbo()
    return reduction_of_a_fraction(Frac(numerator, denominator))

# 使用例

print("◆仮分数と帯分数の変換")
frac1 = Frac(7, 2)
frac2 = improper_to_mixed(frac1)
print("仮分数:",frac1," → 帯分数:",frac2)
frac3 = Frac(3, 1, 2)  # 帯分数 3 1/2
frac4 = mixed_to_improper(frac3)
print("帯分数:",frac3," → 仮分数:",frac4)
print("\n")

print("◆約分")
frac1=Frac(4,8)
print("分数(約分前):",frac1)
print("分数(約分後):",reduction_of_a_fraction(frac1))

print("◆通分")
frac1=Frac(1,2,3)
frac2=Frac(4,5)
print("分数1(通分前):",frac1)
print("分数2(通分前):",frac2)
frac3, frac4 = reduction_to_a_common_denominator(Frac(1,2,3), Frac(4,5))
print("分数1(通分後):",frac3)
print("分数2(通分後):",frac4)
print("\n")

print("◆分数の四則演算")
frac1 = Frac(3, 4)
frac2 = Frac(5, 6)
print("分数1:",frac1)
print("分数2:",frac2)
print("加算: ", add_fractions(frac1, frac2))
print("減算: ", subtract_fractions(frac1, frac2))
print("乗算: ", multiply_fractions(frac1, frac2))
print("除算: ", divide_fractions(frac1, frac2))

このプログラムを実行した結果は次の通りです。分数を構成する数値を input関数で入力させるコードを追加するなどして試してみてください。なお、このプログラムは0で割るエラーを防ぐためのエラーチェックを含んでいませんのでご注意ください。

プログラムの実行結果

 おわりに

この記事では、Pythonプログラミングを通じて小学校算数の分数の計算、特に約分と通分の理解を深める方法を学びました。さらに、最大公約数と最小公倍数という重要な概念についても触れ、それらが約分と通分にどのように関連しているかを説明しました。

また、Pythonのクラスを用いて分数を表現し、その上で約分や通分を行う方法を具体的なコード例とともに示しました。それらの概念を用いて分数の加算、減算、乗算、除算を行うPythonプログラムの作成方法についても紹介しました。

これらの知識が、Pythonプログラミングだけでなく、日常生活や学習の中で頻繁に使われる基本的な数学的スキルの理解を深めるために役立つことを願っています。

【Pythonの入門におすすめの書籍3選】