algorithm-in-python/math/numberTheory/modulo_equation.py

142 lines
4.0 KiB
Python
Raw Permalink Normal View History

2018-10-02 21:24:06 +08:00
''' mbinary
#########################################################################
# File : modulo_equation.py
2018-10-02 21:24:06 +08:00
# Author: mbinary
# Mail: zhuheqin1@gmail.com
2019-01-31 12:09:46 +08:00
# Blog: https://mbinary.xyz
2018-10-02 21:24:06 +08:00
# Github: https://github.com/mbinary
# Created Time: 2018-3-4 21:14
2018-10-02 21:24:06 +08:00
# Description:
`--` represents modulo symbol
2018-10-02 21:24:06 +08:00
#########################################################################
'''
import re
from gcd import xgcd
from euler import phi
from isPrime import isPrime
from factor import factor
2018-10-02 21:24:06 +08:00
2020-04-15 12:28:20 +08:00
def ind(m, g):
2018-10-02 21:24:06 +08:00
''' mod m ,primary root g -> {n:indg n}'''
2020-04-15 12:28:20 +08:00
return {j: i for i in range(m-1)
for j in range(m) if (g**i-j) % m == 0}
2018-10-02 21:24:06 +08:00
2020-04-15 12:28:20 +08:00
def gs(m, num=100):
'''return list of m's primary roots below num'''
p = phi(m)
mp = factor(p)
checkLst = [p//i for i in mp]
2020-04-15 12:28:20 +08:00
return [i for i in range(2, num) if all((i**n-1) % m != 0 for n in checkLst)]
2018-10-02 21:24:06 +08:00
def minG(m):
p = phi(m)
mp = factor(p)
checkLst = [p//i for i in mp]
2020-04-15 12:28:20 +08:00
i = 2
while 1:
if all((i**n-1) % m != 0 for n in checkLst):
return i
i += 1
2018-10-02 21:24:06 +08:00
class solve:
2020-04-15 12:28:20 +08:00
def __init__(self, equ=None):
self.linearPat = re.compile(r'\s*(\d+)\s*--\s*(\d+)[\s\(]*mod\s*(\d+)')
self.sol = []
2018-10-02 21:24:06 +08:00
#self.m = m
#self.ind_mp = ind(m,minG(m))
2020-04-15 12:28:20 +08:00
2018-10-02 21:24:06 +08:00
def noSol(self):
print('equation {equ} has no solution'.format(equ=self.equ))
2020-04-15 12:28:20 +08:00
2018-10-02 21:24:06 +08:00
def error(self):
print("Error! The divisor m must be postive integer")
2020-04-15 12:28:20 +08:00
def solveLinear(self, a, b, m):
2018-10-02 21:24:06 +08:00
'''ax--b(mod m): solve linear equation with one unknown
return ([x1,x2,...],m)
'''
2020-04-15 12:28:20 +08:00
a, b, m = self.check(a, b, m)
g, x, y = xgcd(a, m)
if a*b % g != 0:
2018-10-02 21:24:06 +08:00
self.noSol()
return None
2020-04-15 12:28:20 +08:00
sol = x*b//g
2018-10-02 21:24:06 +08:00
m0 = m//g
2020-04-15 12:28:20 +08:00
sols = [(sol+i*m0) % m for i in range(g)]
print('{}x--{}(mod {}), solution: {} mod {}'.format(a, b, m, sols, m))
return (sols, m)
def check(self, a, b, m):
if m <= 0:
2018-10-02 21:24:06 +08:00
self.error()
return None
2020-04-15 12:28:20 +08:00
if a < 0:
a, b = -a, -b # important
if b < 0:
b += -b//m * m
return a, b, m
2018-10-02 21:24:06 +08:00
2020-04-15 12:28:20 +08:00
def solveHigh(self, a, n, b, m):
2018-10-02 21:24:06 +08:00
''' ax^n -- b (mod m) ind_mp is a dict of m's {n: indg n}'''
2020-04-15 12:28:20 +08:00
ind_mp = ind(m, minG(m))
2018-10-02 21:24:06 +08:00
tmp = ind_mp[b] - ind_mp[a]
2020-04-15 12:28:20 +08:00
if tmp < 0:
tmp += m
sol = self.solveLinear(n, tmp, phi(m))
re_mp = {j: i for i, j in ind_mp.items()}
sols = [re_mp[i] for i in sol[0]]
2020-04-15 12:28:20 +08:00
print('{}x^{}--{}(mod {}), solution: {} mod {}'.format(a, n, b, m, sols, m))
return sols, m
2018-10-02 21:24:06 +08:00
2020-04-15 12:28:20 +08:00
def solveGroup(self, tups):
2018-10-02 21:24:06 +08:00
'''tups is a list of tongyu equation groups, like
[(a1,b1,m1),(a2,b2,m2)...]
and, m1,m2... are all primes
'''
mp = {}
print('solving group of equations: ')
2020-04-15 12:28:20 +08:00
for a, b, m in tups:
print('{}x--{}(mod {})'.format(a, b, m))
2018-10-02 21:24:06 +08:00
if m in mp:
2020-04-15 12:28:20 +08:00
if mp[m][0]*b != mp[m][1]*a:
2018-10-02 21:24:06 +08:00
self.noSol()
return
2020-04-15 12:28:20 +08:00
else:
mp[m] = (a, b)
product = 1
for i in mp.keys():
2020-04-15 12:28:20 +08:00
product *= i
sol = [0]
2018-10-02 21:24:06 +08:00
for i in mp:
2020-04-15 12:28:20 +08:00
xs, m = self.solveLinear(product//i*mp[i][0], 1, i)
new = []
for x in xs:
cur = x*product//i*mp[i][1]
for old in sol:
new.append(old+cur)
sol = new
2020-04-15 12:28:20 +08:00
sol = [i % product for i in sol]
print('final solution: {} mod {}'.format(sol, product))
return sol, product
2018-10-02 21:24:06 +08:00
def __call__(self):
2020-04-15 12:28:20 +08:00
s = input('输入同余方程,用--代表同于号形如3--5(mod 7)代表3x模7同余于5')
li = self.linearPat.findall(s)
li = [(int(a), int(b), int(m)) for a, b, m in li]
2018-10-02 21:24:06 +08:00
print(self.solveLinear(li[0]))
if __name__ == '__main__':
2020-04-15 12:28:20 +08:00
solver = solve()
res = solver.solveLinear(3, 6, 9)
print()
2020-04-15 12:28:20 +08:00
res = solver.solveHigh(1, 8, 3, 11)
print()
2020-04-15 12:28:20 +08:00
res = solver.solveGroup([(5, 11, 2), (3, 8, 5), (4, 1, 7)])