最近在看到了 LeetCode 1922. Count Good Numbers 的題目後,發現幾乎是一題純數學的題目,需要理解 Modular Exponentiation(模冪運算)這個技巧才能夠順利解題。
想當初在學習非對稱式加密的核心技術 RSA 時,所接觸到的 Euler’s Theorem(歐拉定理)就大量用到模(Mod,取餘數)運算。
這次的題目正巧也需要對於模運算性質有所理解,才能得出有效率的解法。在理解完題意之後,結論是需要透過程式來計算以下算式:
(5x⋅4y)mod(109+7)
其中題目限制 x,y≤1015,這也就意味 5x⋅4y 的數值可能相當大,遠大於絕大部分的程式語言中單一變數能儲存的數字上限。
那要如何計算出結果呢?既然最終要取模 mod(109+7),這個模數(109+7)也在變數的可儲存範圍內,那我們只要確保每次的中間計算結果都不會超出變數上限即可,因此解法的關鍵就在於「拆」。
怎麼拆呢?可以像是 430=415⋅415 將較大的數字拆成兩個較小的數字相乘,然後分別取模。
(xa⋅xb)modm=((xamodm)⋅(xbmodm))modm(★)
上述公式便是將大數拆小再分別取模的等式,但在寫出此等式的當下我其實不太確定是否能成立,不過只要能證明它,我們就有理論支持,能透過一般程式語言來得出計算結果。
乘法同餘、餘數定理
上述要證明的式子,可以將其簡化成:
(a⋅b)modm=((amodm)⋅(bmodm))modm(1)
式子 (1) 稱作乘法同餘性質(Multiplicative Congruence),舉例來說,(14⋅6)mod5=4,也等同於分別取模相乘後,再將結果取模:
((14mod5)⋅(6mod5))mod5=(4⋅1)mod5=4
但這是否就能說我們想證明的等式成立了呢?也不然,這僅僅是舉一個例子而已。
要適當嚴謹的得出結論,我們需要先了解**餘數定理(Division Theorem)**這個概念:
任意整數 x 與正整數 m 必定存在唯一整數對 (q,r) 使得
x=q⋅m+r, 0≤r<m
舉例來說,14=2⋅5+4,其中被除數 x=14,商數 q=2、除數 m=5,餘數 r=4,而餘數 r 便是 x 對 m 取模的結果,記為 r=xmodm。
證明乘法同餘
接著,我們可以根據餘數定理,來定義 (a⋅b)modm 中的 a=q1⋅m+r1, b=q2⋅m+r2,並把 a,b 在式子 (1) 的左式 (a⋅b)modm 展開:
(a⋅b)modm=((q1m+r1)⋅(q2m+r2))modm
=(q1q2m2+q1r2m+q2r1m+r1r2)modm
=((q1q2m+q1r2+q2r1)m+r1r2)modm
由於 q1,q2,r1,r2,m 都是整數,因此他們的乘積及加總的組合也會是整數,將其簡化成 K=q1q2m+q1r2+q2r1,K 會是一個整數,所以將 a,b 帶入式子 (1) 的左式結果能再簡化成下面式子 (2)
(a⋅b)modm=(Km+r1r2)modm(2)
根據剛剛從餘數定理出發來定義的 a,b,我們能得出式子 (1) 右式的 amodm 和 bmodm 就等同 r1 及 r2 這兩個餘數,因此式子 (1) 右式就能推導出下述式子 (3)。
((amodm)⋅(bmodm))modm=(r1r2)modm(3)
只要能證明 (2) 和 (3) 兩個式子的右式等價,整個乘法同餘的證明就大功告成,我們繼續拆解下去:
(Km+r1r2)modm=(r1r2)modm(4)
再另外定義一個整數 c=k⋅m(c 為除數 m 的倍數,k 是個整數),然後令 x=r1r2,想證明的式子 (4) 就等價於下面式子 (5)
(x+c)modm=xmodm(5)
根據餘數定理令 x=q⋅m+r 及 c=k⋅m 展開帶入式子 (5) 的左式:
(x+c)modm(5 Left)
=(q⋅m+r+c)modm
=(q⋅m+r+k⋅m)modm
=((q+k)⋅m+r)modm
其中 (q+k)⋅m+r 剛好就符合餘數定理的架構,當此式對 m 取餘,答案就為 r,所以得出
(x+c)modm(5 Left)
=r
=xmodm(5 Right)
如此便得證,將 (5) 式的左式和右式之間的等號連接起來。
參考資料
- Wiki - 歐拉定理
- Wiki - 模算數
- LeetCode 1922. Count Good Numbers
- Wiki - 分配律
- Centennial College - Modular Numbers and Cryptography
- Wiki - 餘數定理