課題の講評#

ピタゴラスの三数とPlimpton 322に関する公式#

命題) Plimpton 322は、互いに素な正の整数 \(p\), \(q\) のうち、

\[ \frac{9}{5} \leq \frac{p}{q} \leq \frac{12}{5} \]

を満たす組み合わせについて、

\[ \left(\frac{d}{a}\right)^2 = \frac{1}{4}\left(\frac{p}{q}+\frac{q}{p}\right)^2 \]

をキーとして降順に並べたものである。

ここで、

\[\begin{split} \begin{align} a & = 2 p q \\ b & = p^2 - q^2\\ d & = p^2 + q^2\\ \end{align} \end{split}\]

である。なお、このとき\(a\), \(b\), \(d\)既約なピタゴラスの三数 (最小公倍数が\(1\)) となり、\(a^2+b^2 = d^2\) を満たす。

さらに、Plimpton 322の左カラムから次の式の値に対応する:

#1

#2

#3

$\(\left(\frac{d}{a}\right)^2\)$

$\(b\)$

$\(d\)$

$\(\frac{1}{4}\left(\frac{p}{q}+\frac{q}{p}\right)^2\)$

$\(p^2-q^2\)$

$\(p^2+q^2\)$

ピタゴラスの三数を探索する#

%%ai vioras
次の関数の問題点を指摘してください。
---
def find_pythagorean_triplets(limit):
    triplets = []
    
    for a in range(1, limit):
        for b in range(a, limit):
            c = (a**2 + b**2)**0.5
            if c.is_integer() and c <= limit:
                triplets.append((a, b, int(c)))
                
    return triplets

この関数find_pythagorean_tripletsはピタゴラスの三つ組を探すためのもので、概ね正しく動作します。ただし、以下の点について問題があり、改善が必要です。

  1. 範囲の設定: cの値がlimit以下であるかをチェックしていますが、このチェックはabのループ内で行われています。この結果、climitを超える組み合わせでも、その前にablimit以下であるためにループが続行され、不要な計算が行われます。climitを超えた時点で、内側のループを中断することで、計算の効率を向上させることができます。

  2. 整数の確認: cが整数であるかを確認するために、is_integer()メソッドを使用しています。これ自体は問題ではありませんが、浮動小数点数の精度の問題から算出結果が理想的な整数にならない可能性があります(例:2.999999999999999など)。math.isclose()関数を使用して、許容可能な誤差内にあるか確認する方が安全です。

  3. ユニットテスト: この関数にはユニットテストがないため、期待通りの動作をするかの確認が難しいです。ユニットテストを書くことで、関数が正しく動作することを保証することが可能になります。

したがって、上記の問題を修正したバージョンの関数は以下のようになります。

import math

def find_pythagorean_triplets(limit):
    triplets = []
    
    for a in range(1, limit):
        for b in range(a, limit):
            c = (a**2 + b**2)**0.5
            if c > limit:
                break
            if math.isclose(c, round(c), rel_tol=1e-9):
                triplets.append((a, b, int(round(c))))
                
    return triplets

この関数は次のようにテストできます。

def test_find_pythagorean_triplets():
    assert find_pythagorean_triplets(10) == [(3, 4, 5), (6, 8, 10)]
    assert find_pythagorean_triplets(20) == [(3, 4, 5), (6, 8, 10), (5, 12, 13), (9, 12, 15), (8, 15, 17), (12, 16, 20)]
    print("All tests passed.")
    
test_find_pythagorean_triplets()

Note

浮動小数で表現される実数が整数か否かを判定する際に理論的な曖昧さを排除できません。

help(float.is_integer)
Help on method_descriptor:

is_integer(self, /)
    Return True if the float is an integer.
x = 1/9
x, x*9, type(x*9), (x*9).is_integer()
(0.1111111111111111, 1.0, float, True)
x = 0.9999999999999999
x, type(x), (x).is_integer()
(0.9999999999999999, float, False)
x = 0.99999999999999999
x, type(x), (x).is_integer()
(1.0, float, True)

公式を元にピタゴラスの三数を探索する#

%%ai vioras
$p, q$を正の整数として、さらに $p > q$ とすると、
$a = 2 p q$, $b = p^2 - q^2$, $d = p^2 + q^2$と置いたとき、
$(a, b, d)$は既約なピタゴラスの三数であることが知られています。
この関係式を元に、ピタゴラスの三数を探索するプログラムを書いてください

Pythonを使って、ピタゴラスの三数を探索するプログラムを書くことができます。 以下のプログラムは、指定された範囲の$p$と$q$の値を用いて、ピタゴラスの三数を計算します。

def find_pythagorean_triples(n):
    triples = []
    for p in range(1, n+1):
        for q in range(1, p):
            a = 2 * p * q
            b = p**2 - q**2
            d = p**2 + q**2
            triples.append((a, b, d))
    return triples

n = 10  # ここではpとqの範囲を1から10までとしています。
triples = find_pythagorean_triples(n)
for triple in triples:
    print(triple)

このプログラムは、$p$と$q$が1から10までの範囲で変化するすべての組み合わせに対して、ピタゴラスの三数$(a, b, d)$を計算します。その後、計算された三数のリストを出力します。

このプログラムの実行結果は、$(a, b, d)$の組み合わせのリストとなります。これらの三数は全てピタゴラスの三数であり、それぞれの三数が$a^2 + b^2 = d^2$を満たすことが確認できます。

このプログラムを用いて、任意の範囲の$p$と$q$に対するピタゴラスの三数を計算することができます。

def find_pythagorean_triples(n):
    triples = []
    for p in range(1, n+1):
        for q in range(1, p):
            a = 2 * p * q
            b = p**2 - q**2
            d = p**2 + q**2
            triples.append((a, b, d))
    return triples

n = 10  # ここではpとqの範囲を1から10までとしています。
triples = find_pythagorean_triples(n)
for triple in triples:
    print(triple)
(4, 3, 5)
(6, 8, 10)
(12, 5, 13)
(8, 15, 17)
(16, 12, 20)
(24, 7, 25)
(10, 24, 26)
(20, 21, 29)
(30, 16, 34)
(40, 9, 41)
(12, 35, 37)
(24, 32, 40)
(36, 27, 45)
(48, 20, 52)
(60, 11, 61)
(14, 48, 50)
(28, 45, 53)
(42, 40, 58)
(56, 33, 65)
(70, 24, 74)
(84, 13, 85)
(16, 63, 65)
(32, 60, 68)
(48, 55, 73)
(64, 48, 80)
(80, 39, 89)
(96, 28, 100)
(112, 15, 113)
(18, 80, 82)
(36, 77, 85)
(54, 72, 90)
(72, 65, 97)
(90, 56, 106)
(108, 45, 117)
(126, 32, 130)
(144, 17, 145)
(20, 99, 101)
(40, 96, 104)
(60, 91, 109)
(80, 84, 116)
(100, 75, 125)
(120, 64, 136)
(140, 51, 149)
(160, 36, 164)
(180, 19, 181)
%%ai vioras
$p, q$も表示してください。

もちろんです。$p$と$q$の値も含めてピタゴラスの三数を探索するプログラムを以下に示します。

def find_pythagorean_triples(n):
    triples = []
    for p in range(1, n+1):
        for q in range(1, p):
            a = 2 * p * q
            b = p**2 - q**2
            d = p**2 + q**2
            triples.append((p, q, a, b, d))
    return triples

n = 10  # ここではpとqの範囲を1から10までとしています。
triples = find_pythagorean_triples(n)
for triple in triples:
    print(triple)

このプログラムは、$p$と$q$が1から10までの範囲で変化するすべての組み合わせに対して、ピタゴラスの三数$(a, b, d)$を計算します。その後、$p$, $q$と共に計算された三数のリストを出力します。

このプログラムの実行結果は、$(p, q, a, b, d)$の組み合わせのリストとなります。これらの三数は全てピタゴラスの三数であり、それぞれの三数が$a^2 + b^2 = d^2$を満たすことが確認できます。

このプログラムを用いて、任意の範囲の$p$と$q$に対するピタゴラスの三数を計算することができます。

def find_pythagorean_triples(n):
    triples = []
    for p in range(1, n+1):
        for q in range(1, p):
            a = 2 * p * q
            b = p**2 - q**2
            d = p**2 + q**2
            triples.append((p, q, a, b, d))
    return triples

n = 10  # ここではpとqの範囲を1から10までとしています。
triples = find_pythagorean_triples(n)
for triple in triples:
    print(triple)
(2, 1, 4, 3, 5)
(3, 1, 6, 8, 10)
(3, 2, 12, 5, 13)
(4, 1, 8, 15, 17)
(4, 2, 16, 12, 20)
(4, 3, 24, 7, 25)
(5, 1, 10, 24, 26)
(5, 2, 20, 21, 29)
(5, 3, 30, 16, 34)
(5, 4, 40, 9, 41)
(6, 1, 12, 35, 37)
(6, 2, 24, 32, 40)
(6, 3, 36, 27, 45)
(6, 4, 48, 20, 52)
(6, 5, 60, 11, 61)
(7, 1, 14, 48, 50)
(7, 2, 28, 45, 53)
(7, 3, 42, 40, 58)
(7, 4, 56, 33, 65)
(7, 5, 70, 24, 74)
(7, 6, 84, 13, 85)
(8, 1, 16, 63, 65)
(8, 2, 32, 60, 68)
(8, 3, 48, 55, 73)
(8, 4, 64, 48, 80)
(8, 5, 80, 39, 89)
(8, 6, 96, 28, 100)
(8, 7, 112, 15, 113)
(9, 1, 18, 80, 82)
(9, 2, 36, 77, 85)
(9, 3, 54, 72, 90)
(9, 4, 72, 65, 97)
(9, 5, 90, 56, 106)
(9, 6, 108, 45, 117)
(9, 7, 126, 32, 130)
(9, 8, 144, 17, 145)
(10, 1, 20, 99, 101)
(10, 2, 40, 96, 104)
(10, 3, 60, 91, 109)
(10, 4, 80, 84, 116)
(10, 5, 100, 75, 125)
(10, 6, 120, 64, 136)
(10, 7, 140, 51, 149)
(10, 8, 160, 36, 164)
(10, 9, 180, 19, 181)

Plimpton 322の数表を探索する#

%%ai vioras
Plimpton 322について

Plimpton 322は、古代バビロニア(現在のイラク)から出土した粘土板で、紀元前1800年頃に書かれたとされています。この粘土板は、現在はコロンビア大学のレアブック&マニュスクリプト図書館に所蔵されています。

この粘土板は、特に数学史の観点から重要で、最初のピタゴラスの定理の一例として知られています。ピタゴラスの定理は、直角三角形の2つの直角辺の長さの二乗の和が、斜辺の長さの二乗と等しいという定理で、一般的には「$a^2 + b^2 = c^2$」と表現されます。

Plimpton 322は、その表が15行からなり、各行が特定の三つ組($a, b, c$)に対応していることで知られています。これらの三つ組は、すべてピタゴラスの三数(つまり、$a^2 + b^2 = c^2$を満たす正の整数の組)で、粘土板の各行は、その三つ組の$a, b, c$をバビロニアの60進数で表現しています。

この粘土板がどのような目的で作成されたのかについては、数学者や歴史家の間で多くの議論があります。一部の研究者は、この粘土板が教育的な目的で使用された可能性を示唆しています。具体的には、学生がピタゴラスの三数の概念を理解し、それを使用して具体的な数学的問題を解決するためのツールとして使用された可能性があります。

他の研究者は、この粘土板がより実用的な目的、例えば建築や土地測量に関連した計算を行うために使用された可能性を示唆しています。

%%ai vioras
Plimpton 322の左のカラムから三つは、
既約なピタゴラスの三数の探索に用いた式を使うと
$1/4 (p/q + q/p)^2$、$b$、$d$に対応すると考える事ができます。
さらに、数表は$9/5 <= p/q <= 12/5$に限定されており、最左のカラムで降順にソートされています。
Plimpton 322の数表を生成するプログラムを書いてください

Pythonを使って、指定された$p$と$q$の範囲でPlimpton 322の数表を生成するプログラムを書くことができます。

以下のプログラムは、$p/q$が$9/5$以上$12/5$以下の範囲で、最左のカラムが降順になるようにピタゴラスの三数を計算します。

def generate_plimpton_322_table():
    table = []
    for p in range(1, 100):  # pの範囲は適切に調整してください。
        for q in range(1, p):
            if 9/5 <= p/q <= 12/5:
                first_column = 1/4 * (p/q + q/p)**2
                b = p**2 - q**2
                d = p**2 + q**2
                table.append((first_column, b, d))
    # 最左のカラムで降順にソート
    table.sort(key=lambda x: x[0], reverse=True)
    return table

table = generate_plimpton_322_table()
for row in table:
    print(row)

このプログラムは、$p$と$q$が特定の範囲で変化するすべての組み合わせに対して、最初のカラム、$b$、および$d$を計算します。その後、最初のカラムで降順にソートされた表を出力します。

このプログラムの実行結果は、Plimpton 322の数表に対応する$(\frac{1}{4} (p/q + q/p)^2, b, d)$の組み合わせのリストとなります。

def generate_plimpton_322_table():
    table = []
    for p in range(1, 10):  # pの範囲は適切に調整してください。
        for q in range(1, p):
            if 9/5 <= p/q <= 12/5:
                first_column = 1/4 * (p/q + q/p)**2
                b = p**2 - q**2
                d = p**2 + q**2
                table.append((first_column, b, d))
    # 最左のカラムで降順にソート
    table.sort(key=lambda x: x[0], reverse=True)
    return table

table = generate_plimpton_322_table()
for row in table:
    print(row)
(1.90702947845805, 40, 58)
(1.815007716049383, 65, 97)
(1.5625, 3, 5)
(1.5625, 12, 20)
(1.5625, 27, 45)
(1.5625, 48, 80)
(1.3871604938271607, 56, 106)

合同な値 (最左カラムが同じ値) を除く#

Note

\((2, 3, 5)\)は60進法の基数の約数の部分集合です。

%%ai vioras
$p, q$を互いに素な$(2, 3, 5)$の合成数に限定してください。

指定された条件を満たすように、$p$と$q$が互いに素な$(2, 3, 5)$の合成数であることを検証する関数を追加します。以下のプログラムは、その条件を満たすようにピタゴラスの三数を計算します。

import math

def is_composite_of_2_3_5(n):
    for prime in [2, 3, 5]:
        while n % prime == 0:
            n /= prime
    return n == 1

def generate_plimpton_322_table():
    table = []
    for p in range(2, 100):  # pの範囲は適切に調整してください。
        if is_composite_of_2_3_5(p):
            for q in range(2, p):
                if is_composite_of_2_3_5(q) and math.gcd(p, q) == 1 and 9/5 <= p/q <= 12/5:
                    first_column = 1/4 * (p/q + q/p)**2
                    b = p**2 - q**2
                    d = p**2 + q**2
                    table.append((first_column, b, d))
    # 最左のカラムで降順にソート
    table.sort(key=lambda x: x[0], reverse=True)
    return table

table = generate_plimpton_322_table()
for row in table:
    print(row)

このプログラムは、$p$と$q$が互いに素な$(2, 3, 5)$の合成数で、かつ$p > q$かつ$p/q$が$9/5$以上$12/5$以下の範囲で変化するすべての組み合わせに対して、最初のカラム、$b$、および$d$を計算します。その後、最初のカラムで降順にソートされた表を出力します。このプログラムの実行結果は、Plimpton 322の数表に対応する$(\frac{1}{4} (p/q + q/p)^2, b, d)$の組み合わせのリストとなります。

import math

def is_composite_of_2_3_5(n):
    for prime in [2, 3, 5]:
        while n % prime == 0:
            n /= prime
    return n == 1

def generate_plimpton_322_table():
    table = []
    for p in range(2, 100):  # pの範囲は適切に調整してください。
        if is_composite_of_2_3_5(p):
            for q in range(2, p):
                if is_composite_of_2_3_5(q) and math.gcd(p, q) == 1 and 9/5 <= p/q <= 12/5:
                    first_column = 1/4 * (p/q + q/p)**2
                    b = p**2 - q**2
                    d = p**2 + q**2
                    table.append((first_column, b, d))
    # 最左のカラムで降順にソート
    table.sort(key=lambda x: x[0], reverse=True)
    return table

table = generate_plimpton_322_table()
for row in table:
    print(row)
(1.9834027777777774, 119, 169)
(1.9491585520886916, 3367, 4825)
(1.9188021267361113, 4601, 6649)
(1.815007716049383, 65, 97)
(1.7851929012345682, 319, 481)
(1.7199836762688614, 2291, 3541)
(1.6927094184027778, 799, 1249)
(1.6426694444444445, 481, 769)
(1.5861225661103489, 4961, 8161)
(1.4894168402777779, 1679, 2929)
(1.450017361111111, 161, 289)
(1.4302388203017835, 1771, 3229)
(1.3871604938271607, 56, 106)
!pip install -U lxml
Collecting lxml
  Using cached lxml-5.2.2-cp311-cp311-manylinux_2_28_x86_64.whl.metadata (3.4 kB)
Using cached lxml-5.2.2-cp311-cp311-manylinux_2_28_x86_64.whl (5.0 MB)
Installing collected packages: lxml
Successfully installed lxml-5.2.2
import pandas as pd
dfs = pd.read_html('https://en.wikipedia.org/wiki/Plimpton_322')
dfs[2]
or Short Side Diagonal Row #
0 (1).9834028 119 169 1
1 (1).9491586 3367 4825 2
2 (1).9188021 4601 6649 3
3 (1).8862479 12709 18541 4
4 (1).8150077 65 97 5
5 (1).7851929 319 481 6
6 (1).7199837 2291 3541 7
7 (1).6927094 799 1249 8
8 (1).6426694 481 769 9
9 (1).5861226 4961 8161 10
10 (1).5625 45* 75* 11
11 (1).4894168 1679 2929 12
12 (1).4500174 161 289 13
13 (1).4302388 1771 3229 14
14 (1).3871605 56* 106* 15