專案前導說明
* 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),看,是不是變得很容易~
專案目標
* 說了半天,還沒進入主題,以下是這個練習專案的標的:
(以上截圖自 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
目標二
* 如以下圖示~
(以上截圖自 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 的過程,很能讓人學到一些寫程式的技巧,希望大家也能從老師課程中學習到老師的程式技巧,與大家共勉~