Python Deep Dive IV 第 5 節 Project 2

專案前導說明

* Project Description:
* 這一節的練習專案,與離散數學的模數(mod)有關 --> 所謂的[同餘(Congruence modulo)](https://zh.wikipedia.org/zh-tw/%E5%90%8C%E9%A4%98),搞懂了同餘的概念,這專案就完成了70%,剩下的部分只是一些 dunder method 的實作練習。

* 而關於模數(mod)部分,推薦大家可以先看看這篇 ITHome 上鐵人挑戰的系列文章 -- [[離散數學]同餘(Mod)是什麼?](https://ithelp.ithome.com.tw/articles/10205727)

* 基本上在 Python 中,% 這個運算子就是用來求餘數,4%3 --> 1; 10%3 --> 1,如前述例子,10 與 4 針對 3 去除,都餘1,因此我們就稱 10 與 4 為同餘,表示為: 4 ≡ 10  (mod 3)
* 同餘的概念在密碼學及加密演算法是一個很重要的數學推導,主要用來化繁為簡,比方說 131231312313131%7 我們可能要計算它會很費勁,但當我們知道 131231312313131 這個數與2 對 7 是同餘,那麼我們就可簡化把這個數看成是 2 % 7 --> 2,這樣就可輕易的把數字的運算化繁為簡~

* 同餘有許多基本定理,讓我們能用在一些複雜計算的推導,茲列舉如下:

    1. 若  a ≡ b (mod k) 則 k | a-b --> k可以整除a-b,換言之 k 是 a-b 的因數

    2. 同餘的相加性質: 若 a ≡ b (mod k) 且 c ≡ d (mod k),則 a+c ≡ b+d (mod k)

    3. 同餘的相乘性質: 若 a ≡ b (mod k) 且 c ≡ d (mod k),則 axc ≡ bxd (mod k)

    4. 同餘的N次方性質: 若 a ≡ b (mod k) 則 a^n ≡ b^n (mod k)

    5. 同餘的倍數性質: 若 a ≡ b (mod k) 則 ma ≡ mb (mod k)

* 有了這些同餘性質的推導,要把一個複雜的數學運算,如 15^8001x22^4781x11^11,就能容易的使用同餘來化簡算式: 15^8001x22^4781x11^11 ≡ 1^8001x1^4781x4^3 (mod 7) --> 15^8001x22^4781x11^11 ≡ 64 (mod 7) --> 64 ≡ 1 (mod 7),看,是不是變得很容易~

專案目標

* 說了半天,還沒進入主題,以下是這個練習專案的標的:

Image
(以上截圖自 Udemy Dr. Fred Baptiste Python Deep Dive IV 課程內容)

目標一
* 創建一個 Mod Class 來表現 a % n,其中 a --> value; n --> modulus:
    * 建構式(__init__)中須將 value 與 modulus 兩個屬性參數初始化 --> 確保此兩個屬性參數必須是唯讀屬性,並確保 modulus 與 value 均為數值,此外 modulus 必須是正整數

* 儲存餘數除法的結果到 value 中,例如: value = 8; modulus = 3 --> 8 % 3 = 2,則 2 將作為結果儲存到 value 中

    * 提供兩個 Mod 物件的比較子,例如: ==、>=、<=, etc.

        * 當兩個 Mod Object a 和 b 為同餘,則 a == b

            * a 和 b 須滿足為整數 --> 其餘數亦為整數

            * 確保 a == b 時,必須有相同的 Hash --> Hashable
目標二
* 如以下圖示~

Image2
(以上截圖自 Udemy Dr. Fred Baptiste Python Deep Dive IV 課程內容)

    * 在 Mod Class 中,創建一個 Function,使得 int(mod_obj) --> returm 這個 mod_obj 的 value (餘數) --> Implement __int__

    * Implement 這個 Mod Class 的 __repr__

    * Implement 這個 Mod Class 的其他運算功能 +, -, *, ** 等等

專案實作練習(程式碼摘錄)

from functools import total_ordering


@total_ordering
class Mod:
    def __init__(self, value, modulus):
        if not isinstance(modulus, int):
            raise TypeError('Unsupported type for modulus')
        if not isinstance(value, int):
            raise TypeError('Unsupported type for value')
        if modulus <= 0:
            raise ValueError('Modulus must be positive')


        self._modulus = modulus
        self._value = value % modulus  # store residue as the value
       
    @property
    def modulus(self):
        return self._modulus
   
    @property
    def value(self):
        return self._value
   
    @value.setter
    def value(self, value):
        self._value = value
   
    def __repr__(self):
        return f'Mod({self._value}, {self._modulus})'
   
    def __int__(self):
        # calculates the value (residue)
        return self.value


    def _get_value(self, other):
        if isinstance(other, int):
            return other % self.modulus  # return the residue
        if isinstance(other, Mod) and self.modulus == other.modulus:
            return other.value
        raise TypeError('Incompatible types.')
   
    def _perform_operation(self, other, op, *, in_place=False):
        other_value = self._get_value(other)
        new_value = op(self.value, other_value)
        if in_place:
            self.value = new_value % self.modulus
            return self
        else:
            return Mod(new_value, self.modulus)
   
    def __eq__(self, other):
        # calculates congruence (same equivalence class)
        other_value = self._get_value(other)
        return other_value == self.value
   
    def __hash__(self):
        return hash((self.value, self.modulus))
   
    def __neg__(self):
        return Mod(-self.value, self.modulus)
   
    def __add__(self, other):
        return self._perform_operation(other, operator.add)
   
    def __iadd__(self, other):
        return self._perform_operation(other, operator.add, in_place=True)
   
    def __sub__(self, other):
        return self._perform_operation(other, operator.sub)
   
    def __isub__(self, other):
        return self._perform_operation(other, operator.sub, in_place=True)
   
    def __mul__(self, other):
        return self._perform_operation(other, operator.mul)
   
    def __imul__(self, other):
        return self._perform_operation(other, operator.mul, in_place=True)
   
    def __pow__(self, other):
        return self._perform_operation(other, operator.pow)
       
    def __ipow__(self, other):
        return self._perform_operation(other, operator.pow, in_place=True)
   
    def __lt__(self, other):
        # here, raising a TypeError instead of returning NotImplemented
        # would result in Python not trying the reflection - which we DO want
        # although since we are using @total_ordering this does not really matter
        try:
            other_value = self._get_value(other)
            return self.value < other_value
        except TypeError:
            return NotImplemented

專案練習內容其實就是上一節課程的 dunder method 的實作,說穿了,沒什麼,因此我只簡單把最後老師的最終結果貼在筆記中,只是做為一個上課的記錄。不過倒是老師講解過程中,一次次的慢慢將程式精簡化 Clean Code 的過程,很能讓人學到一些寫程式的技巧,希望大家也能從老師課程中學習到老師的程式技巧,與大家共勉~

1個讚