メインコンテンツへスキップ
  1. 第09講 クラス(2/2)/

第09講 週次課題

週次課題 特殊メソッド カプセル化 モジュール
目次

課題09-1 点と線分
#

難易度 ⭐⭐

点(Point)と線分(Line)を表す2つのクラスを作成してください.

  • Point クラスは,2次元平面上の点を表すクラスです.
    • x 座標と y 座標を持ち,コンストラクタで初期化します.
    • 他のPointオブジェクトからの距離を計算するメソッド distance を持ちます.
      • 他のPointオブジェクトが渡されなかった場合は,原点 (0, 0) からの距離を計算します.
    • __str__ メソッドでは,f"({self._x}, {self._y})" という文字列表現を返します.
  • Line クラスは,2つの Point オブジェクトからなる線分を表すクラスです.
    • 2つの Point オブジェクトを持ち,コンストラクタで初期化します.
    • 線分の長さを計算するメソッド distance を持ちます.
    • __str__ メソッドでは,f"({self._p1}, {self._p2})" という文字列表現を返します.
    • is_on メソッドは,引数に Point オブジェクトを受け取り,その点が線分上にあれば True を返し,そうでなければ False を返します.
      • 直線の方程式を求めて,引数の Point オブジェクトの座標が方程式を満たすかどうかで判断すると良いでしょう.
      • ただし,浮動小数点変数(float)の場合,誤差により必ずしも一致するわけではないので,誤差を許容する必要があります.
        • 求めた値got_xと,一致してほしい値want_xの差の絶対値が 0.00001 より小さければ等しいとみなすと良いでしょう(abs(got_x - want_x) < 0.00001).

以下のプログラム(assignment09-1.py)を実行したときに,テストが全て通るように plane_figures.py を作成してください.

import plane_figures as pf

p1 = pf.Point(3, 4)
assert p1.distance() == 5.0, "原点からの距離が正しくありません"
assert p1.distance(p1) == 0.0, "同じ点からの距離が正しくありません"
assert str(p1) == "(3, 4)", "文字列表現が正しくありません"

p2 = pf.Point(1, 1)
assert p2.distance() == 2 ** 0.5, "原点からの距離が正しくありません"

l1 = pf.Line(p2, pf.Point(5, 5))
assert l1.distance() == 32 ** 0.5, "線分の長さが正しくありません"
assert str(l1) == "((1, 1), (5, 5))", "文字列表現が正しくありません"
assert l1.is_on(pf.Point(3, 3)), "点 (3, 3) が線分上にないと判断されています"
assert not l1.is_on(pf.Point(0, 0)), "点 (0, 0) は線分上にありません"

課題09-2 乱数ジェネレータ
#

難易度 ⭐⭐

以下の特徴を持つ乱数生成クラス MyRandommy_random.py)を作成してください.

  • MyRandom クラスは,線形合同法(Linear Congruential Generator; LCG)による乱数生成器です.
    • 線形合同法は \(X_{n+1} = (a * X_n + c) \mod m\) という漸化式で表されます.
      • 上記の a は乗数(multiplier),c は加数(increment),m は剰余(modulus)と呼びます.
  • コンストラクタは,seedmodulusmultiplierincrement を受け取り,初期化します.
    • seed は乱数の初期値です.デフォルト値は 0 です.
    • modulus は剰余演算に使う値です.デフォルト値は 2 ** 32 です.
    • multiplier は乗算に使う値です.デフォルト値は 1664525 です.
    • increment は加算に使う値です.デフォルト値は 1013904223 です.
  • next メソッドは,次の0以上1未満の乱数を生成し,返します.
    • 得られた\(X_{n+1}\)は\(0\)以上\(m\)未満の整数となりますので,範囲を \(0\)以上\(1\)未満に変換するために,\(X_{n+1} / m\) を返します.
class MyRandom:
    def __init__(self): # 引数(seed,modulus, multiplier, increment)を追加してください.
        pass            # seed, modulus, multiplier, increment はデフォルト引数を持つようにしてください.

    def next(self, count = 0): # count が指定された場合,count 回乱数を生成します.
        result = self._next_impl()
        if count > 0:
            return self.next(count - 1)
        return result

    def _next_impl(self):
        pass        # 線形合同法による乱数生成を実装してください.

以下のテストコード(assignment09-2.py)を通るようにしてください.

import my_random as mr

rg = mr.MyRandom(0, 2 ** 32, 1664525, 1013904223)
assert rg.next() - 0.236067972844466 < 0.000001, "乱数が正しくありません"
assert rg.next() - 0.278566908556968 < 0.000001, "乱数が正しくありません"
assert rg.next(2) - 0.384077370865270 < 0.000001, "乱数が正しくありません"
# assert rg.next() - 0.819533759960904 < 0.000001, "乱数が正しくありません" # このコメントアウトした
# assert rg.next() - 0.667866897769272 < 0.000001, "乱数が正しくありません" # 2つの乱数が飛ばされる.
assert rg.next() - 0.621807487215846 < 0.000001, "乱数が正しくありません"

課題09-3 ベクトル
#

難易度 ⭐⭐⭐

次のプログラム(assignment09-3.py)を実行したときに,テストが全て通るように vector.py を作成してください. vector.py 中で Vectorクラスを定義し,以下のメソッドを用意してください.

  • __init__ コンストラクタ.n個の引数(可変長)を受け取り,ベクトルを表す.
  • __add__ ベクトル同士の足し算を計算し,新しいベクトルを返す.
  • __mul__ ベクトル同士の内積を計算し,スカラー値を返す.
  • __getitem__ ベクトルの要素を取得する.
  • __len__ ベクトルの長さを返す.
  • __str__ ベクトルの文字列表現を返す.
  • norm ベクトルのノルム(大きさ)を計算し,返す.
import vector as v

v1 = v.Vector(3, 4, 5)
v2 = v.Vector(1, 2, 3)

v3 = v1 + v2
assert v3[0] == 4 and v3[1] == 6 and v3[2] == 8, "ベクトルの足し算が正しくありません"
assert v1 * v2 == 26, "ベクトルの内積の計算が正しくありません"
assert str(v1) == "(3, 4, 5)", "ベクトルの文字列表現が正しくありません"
assert len(v1) == 3, "ベクトルの長さが正しくありません"
assert v1.norm() == 50 ** 0.5, "ベクトルのノルム(大きさ)の計算が正しくありません."

v4 = v.Vector(1, 1, 1, 1, 1)
assert len(v4) == 5, "ベクトルの長さが正しくありません"
assert v4.norm() == 5 ** 0.5, "ベクトルのノルム(大きさ)の計算が正しくありません."
assert v4 + v1 == None, "要素数の異なるベクトル同士の足し算は計算できないので,None が返るはずです."
assert v1 * v4 == None, "要素数の異なるベクトル同士の内積は計算できないので,None が返るはずです."

なお,リストを可変長引数として受け取る方法は以下の通りです.

>>> list = [1, 2, 3]
>>> v1 = Vector(list)  # これは,[1, 2, 3] という1つの要素を持つベクトルになってしまう.
>>> v1.elements
([1, 2, 3],)           # ベクトルの要素は [1, 2, 3] という値一つ.
>>> v2 = Vector(*list) # このようにリスト変数の前に * をつけると,リストの要素を展開して引数として渡すことができる.
>>> v2.elements
(1, 2, 3)