Code for 大数取模 modulo arithmetic
2024年5月19日
/// <summary>
/// Find b^p % m.
/// </summary>
/// <param name="b"></param>
/// <param name="p"></param>
/// <param name="m"></param>
/// <returns></returns>
public static int FindModularExponentiation(int b, int p, int m)
{
int p1 = (int)Math.Ceiling(Math.Log(m) / Math.Log(b));
if (p1 <= p)
logger.LogInformation($"{b}^{p1} >= {m}. ({p1} <= {p})");
else
return (int)NumberService.PowerWithSwitch(b, p);
int q = Math.DivRem(p, p1, out int r);
logger.LogInformation($"{b}^{p} mod {m} = {b}^{{{p1}*{q}+{r}}} mod {m} = {b}^{{{p1}*{q}}} * {b}^{r} mod {m} ");
Debug.Assert(NumberService.PowerWithSwitch(b, r) < m);
logger.LogInformation($"= ({b}^{p1} mod {m})^{q} * {b}^{r} mod {m} ");
// Even thought p1 is miminal power that makes b^p1 >= m,
// b^p1 can still be multiples of m, if b is large.
int res = (int)(NumberService.PowerWithSwitch(b, p1) % m);
logger.LogInformation($"= {res}^{q} * {b}^{r} mod {m} ");
Dictionary<int, int> factors = NumberService.Factor(res);
SortedDictionary<int, int> sortedFactors = new SortedDictionary<int, int>(factors.ToDictionary(kv => kv.Key, kv => kv.Value * q));
if (r > 0)
{
if (sortedFactors.TryGetValue(b, out int c))
sortedFactors[b] = c + r;
else
sortedFactors[b] = r;
}
logger.LogInformation("= {0} mod {1}",
string.Join(" * ", sortedFactors.Select(kv => kv.Value == 1 ? kv.Key.ToString() : kv.Key + "^" + kv.Value)),
m);
logger.LogInformation("Then we divide the expression in {0} parts.", sortedFactors.Count);
List<int> remainders = new List<int>(sortedFactors.Count);
int result = 1;
foreach (var f in sortedFactors)
{
int remainder;
using (logger.BeginScope(" "))
{
remainder = FindModularExponentiation(f.Key, f.Value, m);
}
remainders.Add(remainder);
result = (int)((long)result * remainder % m);
}
logger.LogInformation(string.Join(" * ", remainders) + " mod " + m + " = " + result);
logger.LogInformation("\n");
return result;
}
There are a couple of places to watch out.
To start with, you can’t use Math.Pow()
because it works on double
and double
is inherently inaccurate. [1] You can use BigInteger.Pow()
or use a switch-based solution[2] (the return value must be long
).
Next, as I talked about in the previous article, you have to do prime factorization. There is certainly room to optimize the factorization.
The following is the log.
2^30 >= 1000000007. (30 <= 3500) 2^3500 mod 1000000007 = 2^{30*116+20} mod 1000000007 = 2^{30*116} * 2^20 mod 1000000007 = (2^30 mod 1000000007)^116 * 2^20 mod 1000000007 = 73741817^116 * 2^20 mod 1000000007 = 2^20 * 101^116 * 491^116 * 1487^116 mod 1000000007 Then we divide the expression in 4 parts. 101^5 >= 1000000007. (5 <= 116) 101^116 mod 1000000007 = 101^{5*23+1} mod 1000000007 = 101^{5*23} * 101^1 mod 1000000007 = (101^5 mod 1000000007)^23 * 101^1 mod 1000000007 = 510100431^23 * 101^1 mod 1000000007 = 3^23 * 101 * 617^23 * 275581^23 mod 1000000007 Then we divide the expression in 4 parts. | 3^19 >= 1000000007. (19 <= 23) | 3^23 mod 1000000007 = 3^{19*1+4} mod 1000000007 = 3^{19*1} * 3^4 mod 1000000007 | = (3^19 mod 1000000007)^1 * 3^4 mod 1000000007 | = 162261460^1 * 3^4 mod 1000000007 | = 2^2 * 3^4 * 5 * 251 * 32323 mod 1000000007 | Then we divide the expression in 5 parts. | 4 * 81 * 5 * 251 * 32323 mod 1000000007 = 143178169 | | 617^4 >= 1000000007. (4 <= 23) | 617^23 mod 1000000007 = 617^{4*5+3} mod 1000000007 = 617^{4*5} * 617^3 mod 1000000007 | = (617^4 mod 1000000007)^5 * 617^3 mod 1000000007 | = 924113713^5 * 617^3 mod 1000000007 | = 617^3 * 4127^5 * 223919^5 mod 1000000007 | Then we divide the expression in 3 parts. | | 4127^3 >= 1000000007. (3 <= 5) | | 4127^5 mod 1000000007 = 4127^{3*1+2} mod 1000000007 = 4127^{3*1} * 4127^2 mod 1000000007 | | = (4127^3 mod 1000000007)^1 * 4127^2 mod 1000000007 | | = 291595893^1 * 4127^2 mod 1000000007 | | = 3 * 4127^2 * 97198631 mod 1000000007 | | Then we divide the expression in 3 parts. | | 3 * 17032129 * 97198631 mod 1000000007 = 830680711 | | | | 223919^2 >= 1000000007. (2 <= 5) | | 223919^5 mod 1000000007 = 223919^{2*2+1} mod 1000000007 = 223919^{2*2} * 223919^1 mod 1000000007 | | = (223919^2 mod 1000000007)^2 * 223919^1 mod 1000000007 | | = 139718211^2 * 223919^1 mod 1000000007 | | = 3^2 * 1693^2 * 27509^2 * 223919 mod 1000000007 | | Then we divide the expression in 4 parts. | | 9 * 2866249 * 756745081 * 223919 mod 1000000007 = 649279648 | | | 234885113 * 830680711 * 649279648 mod 1000000007 = 773529357 | | 275581^2 >= 1000000007. (2 <= 23) | 275581^23 mod 1000000007 = 275581^{2*11+1} mod 1000000007 = 275581^{2*11} * 275581^1 mod 1000000007 | = (275581^2 mod 1000000007)^11 * 275581^1 mod 1000000007 | = 944887036^11 * 275581^1 mod 1000000007 | = 2^22 * 275581 * 236221759^11 mod 1000000007 | Then we divide the expression in 3 parts. | | 236221759^2 >= 1000000007. (2 <= 11) | | 236221759^11 mod 1000000007 = 236221759^{2*5+1} mod 1000000007 = 236221759^{2*5} * 236221759^1 mod 1000000007 | | = (236221759^2 mod 1000000007)^5 * 236221759^1 mod 1000000007 | | = 34449048^5 * 236221759^1 mod 1000000007 | | = 2^15 * 3^10 * 478459^5 * 236221759 mod 1000000007 | | Then we divide the expression in 4 parts. | | | 478459^2 >= 1000000007. (2 <= 5) | | | 478459^5 mod 1000000007 = 478459^{2*2+1} mod 1000000007 = 478459^{2*2} * 478459^1 mod 1000000007 | | | = (478459^2 mod 1000000007)^2 * 478459^1 mod 1000000007 | | | = 923013085^2 * 478459^1 mod 1000000007 | | | = 5^2 * 907^2 * 203531^2 * 478459 mod 1000000007 | | | Then we divide the expression in 4 parts. | | | | 203531^2 >= 1000000007. (2 <= 2) | | | | 203531^2 mod 1000000007 = 203531^{2*1+0} mod 1000000007 = 203531^{2*1} * 203531^0 mod 1000000007 | | | | = (203531^2 mod 1000000007)^1 * 203531^0 mod 1000000007 | | | | = 424867674^1 * 203531^0 mod 1000000007 | | | | = 2 * 3 * 7 * 11 * 311 * 2957 mod 1000000007 | | | | Then we divide the expression in 6 parts. | | | | 2 * 3 * 7 * 11 * 311 * 2957 mod 1000000007 = 424867674 | | | | | | | 25 * 822649 * 424867674 * 478459 mod 1000000007 = 549840858 | | | | | 32768 * 59049 * 549840858 * 236221759 mod 1000000007 = 476999544 | | | 4194304 * 275581 * 476999544 mod 1000000007 = 256423819 | 143178169 * 101 * 773529357 * 256423819 mod 1000000007 = 390957170 491^4 >= 1000000007. (4 <= 116) 491^116 mod 1000000007 = 491^{4*29+0} mod 1000000007 = 491^{4*29} * 491^0 mod 1000000007 = (491^4 mod 1000000007)^29 * 491^0 mod 1000000007 = 120048155^29 * 491^0 mod 1000000007 = 5^29 * 23^29 * 1043897^29 mod 1000000007 Then we divide the expression in 3 parts. | 5^13 >= 1000000007. (13 <= 29) | 5^29 mod 1000000007 = 5^{13*2+3} mod 1000000007 = 5^{13*2} * 5^3 mod 1000000007 | = (5^13 mod 1000000007)^2 * 5^3 mod 1000000007 | = 220703118^2 * 5^3 mod 1000000007 | = 2^2 * 3^2 * 5^3 * 36783853^2 mod 1000000007 | Then we divide the expression in 4 parts. | | 36783853^2 >= 1000000007. (2 <= 2) | | 36783853^2 mod 1000000007 = 36783853^{2*1+0} mod 1000000007 = 36783853^{2*1} * 36783853^0 mod 1000000007 | | = (36783853^2 mod 1000000007)^1 * 36783853^0 mod 1000000007 | | = 832054252^1 * 36783853^0 mod 1000000007 | | = 2^2 * 208013563 mod 1000000007 | | Then we divide the expression in 2 parts. | | 4 * 208013563 mod 1000000007 = 832054252 | | | 4 * 9 * 125 * 832054252 mod 1000000007 = 244107792 | | 23^7 >= 1000000007. (7 <= 29) | 23^29 mod 1000000007 = 23^{7*4+1} mod 1000000007 = 23^{7*4} * 23^1 mod 1000000007 | = (23^7 mod 1000000007)^4 * 23^1 mod 1000000007 | = 404825426^4 * 23^1 mod 1000000007 | = 2^4 * 23 * 97^4 * 1409^4 * 1481^4 mod 1000000007 | Then we divide the expression in 5 parts. | | 1409^3 >= 1000000007. (3 <= 4) | | 1409^4 mod 1000000007 = 1409^{3*1+1} mod 1000000007 = 1409^{3*1} * 1409^1 mod 1000000007 | | = (1409^3 mod 1000000007)^1 * 1409^1 mod 1000000007 | | = 797260915^1 * 1409^1 mod 1000000007 | | = 5 * 11 * 127 * 157 * 727 * 1409 mod 1000000007 | | Then we divide the expression in 6 parts. | | 5 * 11 * 127 * 157 * 727 * 1409 mod 1000000007 = 340621374 | | | | 1481^3 >= 1000000007. (3 <= 4) | | 1481^4 mod 1000000007 = 1481^{3*1+1} mod 1000000007 = 1481^{3*1} * 1481^1 mod 1000000007 | | = (1481^3 mod 1000000007)^1 * 1481^1 mod 1000000007 | | = 248367620^1 * 1481^1 mod 1000000007 | | = 2^2 * 5 * 17 * 19 * 1481 * 38447 mod 1000000007 | | Then we divide the expression in 6 parts. | | 4 * 5 * 17 * 19 * 1481 * 38447 mod 1000000007 = 832442651 | | | 16 * 23 * 88529281 * 340621374 * 832442651 mod 1000000007 = 453927012 | | 1043897^2 >= 1000000007. (2 <= 29) | 1043897^29 mod 1000000007 = 1043897^{2*14+1} mod 1000000007 = 1043897^{2*14} * 1043897^1 mod 1000000007 | = (1043897^2 mod 1000000007)^14 * 1043897^1 mod 1000000007 | = 720938986^14 * 1043897^1 mod 1000000007 | = 2^14 * 1043897 * 360469493^14 mod 1000000007 | Then we divide the expression in 3 parts. | | 360469493^2 >= 1000000007. (2 <= 14) | | 360469493^14 mod 1000000007 = 360469493^{2*7+0} mod 1000000007 = 360469493^{2*7} * 360469493^0 mod 1000000007 | | = (360469493^2 mod 1000000007)^7 * 360469493^0 mod 1000000007 | | = 474109271^7 * 360469493^0 mod 1000000007 | | = 43^7 * 1297^7 * 8501^7 mod 1000000007 | | Then we divide the expression in 3 parts. | | | 43^6 >= 1000000007. (6 <= 7) | | | 43^7 mod 1000000007 = 43^{6*1+1} mod 1000000007 = 43^{6*1} * 43^1 mod 1000000007 | | | = (43^6 mod 1000000007)^1 * 43^1 mod 1000000007 | | | = 321363007^1 * 43^1 mod 1000000007 | | | = 7 * 29 * 43 * 1153 * 1373 mod 1000000007 | | | Then we divide the expression in 5 parts. | | | 7 * 29 * 43 * 1153 * 1373 mod 1000000007 = 818609210 | | | | | | 1297^3 >= 1000000007. (3 <= 7) | | | 1297^7 mod 1000000007 = 1297^{3*2+1} mod 1000000007 = 1297^{3*2} * 1297^1 mod 1000000007 | | | = (1297^3 mod 1000000007)^2 * 1297^1 mod 1000000007 | | | = 181825059^2 * 1297^1 mod 1000000007 | | | = 3^2 * 13^2 * 1297 * 4662181^2 mod 1000000007 | | | Then we divide the expression in 4 parts. | | | | 4662181^2 >= 1000000007. (2 <= 2) | | | | 4662181^2 mod 1000000007 = 4662181^{2*1+0} mod 1000000007 = 4662181^{2*1} * 4662181^0 mod 1000000007 | | | | = (4662181^2 mod 1000000007)^1 * 4662181^0 mod 1000000007 | | | | = 931524616^1 * 4662181^0 mod 1000000007 | | | | = 2^3 * 11 * 101 * 311 * 337 mod 1000000007 | | | | Then we divide the expression in 5 parts. | | | | 8 * 11 * 101 * 311 * 337 mod 1000000007 = 931524616 | | | | | | | 9 * 169 * 1297 * 931524616 mod 1000000007 = 63530421 | | | | | | 8501^3 >= 1000000007. (3 <= 7) | | | 8501^7 mod 1000000007 = 8501^{3*2+1} mod 1000000007 = 8501^{3*2} * 8501^1 mod 1000000007 | | | = (8501^3 mod 1000000007)^2 * 8501^1 mod 1000000007 | | | = 341771203^2 * 8501^1 mod 1000000007 | | | = 41^2 * 8501 * 8335883^2 mod 1000000007 | | | Then we divide the expression in 3 parts. | | | | 8335883^2 >= 1000000007. (2 <= 2) | | | | 8335883^2 mod 1000000007 = 8335883^{2*1+0} mod 1000000007 = 8335883^{2*1} * 8335883^0 mod 1000000007 | | | | = (8335883^2 mod 1000000007)^1 * 8335883^0 mod 1000000007 | | | | = 944903287^1 * 8335883^0 mod 1000000007 | | | | = 71 * 97 * 137201 mod 1000000007 | | | | Then we divide the expression in 3 parts. | | | | 71 * 97 * 137201 mod 1000000007 = 944903287 | | | | | | | 1681 * 8501 * 944903287 mod 1000000007 = 904205081 | | | | | 818609210 * 63530421 * 904205081 mod 1000000007 = 524796728 | | | 16384 * 1043897 * 524796728 mod 1000000007 = 982403768 | 244107792 * 453927012 * 982403768 mod 1000000007 = 580316551 1487^3 >= 1000000007. (3 <= 116) 1487^116 mod 1000000007 = 1487^{3*38+2} mod 1000000007 = 1487^{3*38} * 1487^2 mod 1000000007 = (1487^3 mod 1000000007)^38 * 1487^2 mod 1000000007 = 288008282^38 * 1487^2 mod 1000000007 = 2^38 * 1487^2 * 144004141^38 mod 1000000007 Then we divide the expression in 3 parts. | 2^30 >= 1000000007. (30 <= 38) | 2^38 mod 1000000007 = 2^{30*1+8} mod 1000000007 = 2^{30*1} * 2^8 mod 1000000007 | = (2^30 mod 1000000007)^1 * 2^8 mod 1000000007 | = 73741817^1 * 2^8 mod 1000000007 | = 2^8 * 101 * 491 * 1487 mod 1000000007 | Then we divide the expression in 4 parts. | 256 * 101 * 491 * 1487 mod 1000000007 = 877905026 | | 144004141^2 >= 1000000007. (2 <= 38) | 144004141^38 mod 1000000007 = 144004141^{2*19+0} mod 1000000007 = 144004141^{2*19} * 144004141^0 mod 1000000007 | = (144004141^2 mod 1000000007)^19 * 144004141^0 mod 1000000007 | = 479987537^19 * 144004141^0 mod 1000000007 | = 17^19 * 131^19 * 215531^19 mod 1000000007 | Then we divide the expression in 3 parts. | | 17^8 >= 1000000007. (8 <= 19) | | 17^19 mod 1000000007 = 17^{8*2+3} mod 1000000007 = 17^{8*2} * 17^3 mod 1000000007 | | = (17^8 mod 1000000007)^2 * 17^3 mod 1000000007 | | = 975757399^2 * 17^3 mod 1000000007 | | = 17^3 * 59^2 * 16538261^2 mod 1000000007 | | Then we divide the expression in 3 parts. | | | 16538261^2 >= 1000000007. (2 <= 2) | | | 16538261^2 mod 1000000007 = 16538261^{2*1+0} mod 1000000007 = 16538261^{2*1} * 16538261^0 mod 1000000007 | | | = (16538261^2 mod 1000000007)^1 * 16538261^0 mod 1000000007 | | | = 74989523^1 * 16538261^0 mod 1000000007 | | | = 7 * 19 * 563831 mod 1000000007 | | | Then we divide the expression in 3 parts. | | | 7 * 19 * 563831 mod 1000000007 = 74989523 | | | | | 4913 * 3481 * 74989523 mod 1000000007 = 286765645 | | | | 131^5 >= 1000000007. (5 <= 19) | | 131^19 mod 1000000007 = 131^{5*3+4} mod 1000000007 = 131^{5*3} * 131^4 mod 1000000007 | | = (131^5 mod 1000000007)^3 * 131^4 mod 1000000007 | | = 579489385^3 * 131^4 mod 1000000007 | | = 5^3 * 131^4 * 115897877^3 mod 1000000007 | | Then we divide the expression in 3 parts. | | | 115897877^2 >= 1000000007. (2 <= 3) | | | 115897877^3 mod 1000000007 = 115897877^{2*1+1} mod 1000000007 = 115897877^{2*1} * 115897877^1 mod 1000000007 | | | = (115897877^2 mod 1000000007)^1 * 115897877^1 mod 1000000007 | | | = 799080910^1 * 115897877^1 mod 1000000007 | | | = 2 * 5 * 19 * 4205689 * 115897877 mod 1000000007 | | | Then we divide the expression in 5 parts. | | | 2 * 5 * 19 * 4205689 * 115897877 mod 1000000007 = 371945610 | | | | | 125 * 294499921 * 371945610 mod 1000000007 = 316393257 | | | | 215531^2 >= 1000000007. (2 <= 19) | | 215531^19 mod 1000000007 = 215531^{2*9+1} mod 1000000007 = 215531^{2*9} * 215531^1 mod 1000000007 | | = (215531^2 mod 1000000007)^9 * 215531^1 mod 1000000007 | | = 453611639^9 * 215531^1 mod 1000000007 | | = 13^9 * 215531 * 34893203^9 mod 1000000007 | | Then we divide the expression in 3 parts. | | | 13^9 >= 1000000007. (9 <= 9) | | | 13^9 mod 1000000007 = 13^{9*1+0} mod 1000000007 = 13^{9*1} * 13^0 mod 1000000007 | | | = (13^9 mod 1000000007)^1 * 13^0 mod 1000000007 | | | = 604499303^1 * 13^0 mod 1000000007 | | | = 37 * 89 * 183571 mod 1000000007 | | | Then we divide the expression in 3 parts. | | | 37 * 89 * 183571 mod 1000000007 = 604499303 | | | | | | 34893203^2 >= 1000000007. (2 <= 9) | | | 34893203^9 mod 1000000007 = 34893203^{2*4+1} mod 1000000007 = 34893203^{2*4} * 34893203^1 mod 1000000007 | | | = (34893203^2 mod 1000000007)^4 * 34893203^1 mod 1000000007 | | | = 607076464^4 * 34893203^1 mod 1000000007 | | | = 2^16 * 37^4 * 733^4 * 1399^4 * 34893203 mod 1000000007 | | | Then we divide the expression in 5 parts. | | | | 733^4 >= 1000000007. (4 <= 4) | | | | 733^4 mod 1000000007 = 733^{4*1+0} mod 1000000007 = 733^{4*1} * 733^0 mod 1000000007 | | | | = (733^4 mod 1000000007)^1 * 733^0 mod 1000000007 | | | | = 679467505^1 * 733^0 mod 1000000007 | | | | = 5 * 8543 * 15907 mod 1000000007 | | | | Then we divide the expression in 3 parts. | | | | 5 * 8543 * 15907 mod 1000000007 = 679467505 | | | | | | | | 1399^3 >= 1000000007. (3 <= 4) | | | | 1399^4 mod 1000000007 = 1399^{3*1+1} mod 1000000007 = 1399^{3*1} * 1399^1 mod 1000000007 | | | | = (1399^3 mod 1000000007)^1 * 1399^1 mod 1000000007 | | | | = 738124185^1 * 1399^1 mod 1000000007 | | | | = 3 * 5 * 1399 * 49208279 mod 1000000007 | | | | Then we divide the expression in 4 parts. | | | | 3 * 5 * 1399 * 49208279 mod 1000000007 = 635727591 | | | | | | | 65536 * 1874161 * 679467505 * 635727591 * 34893203 mod 1000000007 = 435840952 | | | | | 604499303 * 215531 * 435840952 mod 1000000007 = 400832125 | | | 286765645 * 316393257 * 400832125 mod 1000000007 = 685365020 | 877905026 * 2211169 * 685365020 mod 1000000007 = 779596752 1048576 * 390957170 * 580316551 * 779596752 mod 1000000007 = 676888799
Simple Looping
Of course, another simple and navie solution is to loop power times.
public static int FindModularExponentiationLoop(int b, int p, int m)
{
int result = 1;
while (p-- >= 1)
result = (int) ((long)result * b % m);
return result;
}
The following is the performance evaluation by BenchmarkDotNet. Unfortunately my "smart" method is terribly slow.
Method | Mean | Error | StdDev |
---|---|---|---|
Smart | 299.70 us | 0.446 us | 0.396 us |
Loop | 47.46 us | 0.081 us | 0.075 us |
参考资料
- Will Calderwood. Am I going crazy or is Math.Pow broken?. . 2011-03-23 [2024-05-20].↑
- SunsetQuest. How do you do *integer* exponentiation in C#?. . 2014-02-13 [2024-05-20].↑
[…] Up to now, we have to use a computer. See Code for 大数取模 modulo arithmetic. […]