I am trying to calculate the cube root of a many-hundred digit number modulo P in Python, and failing miserably.
I found code for the Tonelli-Shanks algorithm which supposedly is simple to modify from square roots to cube roots, but this eludes me. I've scoured the web and math libraries and a few books to no avail. Code would be wonderful, so would an algorithm explained in plain English.
Here is the Python (2.6?) code for finding square roots:
def modular_sqrt(a, p):
""" Find a quadratic residue (mod p) of 'a'. p
must be an odd prime.
Solve the congruence of the form:
x^2 = a (mod p)
And returns x. Note that p - x is also a root.
0 is returned is no square root exists for
these a and p.
The Tonelli-Shanks algorithm is used (except
for some simple cases in which the solution
is known from an identity). This algorithm
runs in polynomial time (unless the
generalized Riemann hypothesis is false).
"""
# Simple cases
#
if legendre_symbol(a, p) != 1:
return 0
elif a == 0:
return 0
elif p == 2:
return n
elif p % 4 == 3:
return pow(a, (p + 1) / 4, p)
# Partition p-1 to s * 2^e for an odd s (i.e.
# reduce all the powers of 2 from p-1)
#
s = p - 1
e = 0
while s % 2 == 0:
s /= 2
e += 1
# Find some 'n' with a legendre symbol n|p = -1.
# Shouldn't take long.
#
n = 2
while legendre_symbol(n, p) != -1:
n += 1
# Here be dragons!
# Read the paper "Square roots from 1; 24, 51,
# 10 to Dan Shanks" by Ezra Brown for more
# information
#
# x is a guess of the square root that gets better
# with each iteration.
# b is the "fudge factor" - by how much we're off
# with the guess. The invariant x^2 = ab (mod p)
# is maintained throughout the loop.
# g is used for successive powers of n to update
# both a and b
# r is the exponent - decreases with each update
#
x = pow(a, (s + 1) / 2, p)
b = pow(a, s, p)
g = pow(n, s, p)
r = e
while True:
t = b
m = 0
for m in xrange(r):
if t == 1:
break
t = pow(t, 2, p)
if m == 0:
return x
gs = pow(g, 2 ** (r - m - 1), p)
g = (gs * gs) % p
x = (x * gs) % p
b = (b * g) % p
r = m
def legendre_symbol(a, p):
""" Compute the Legendre symbol a|p using
Euler's criterion. p is a prime, a is
relatively prime to p (if p divides
a, then a|p = 0)
Returns 1 if a has a square root modulo
p, -1 otherwise.
"""
ls = pow(a, (p - 1) / 2, p)
return -1 if ls == p - 1 else ls
Source: Computing modular square roots in Python
Note added later: In the Tonelli-Shanks algorithm and here it is assumed that p is prime. If we could compute modular square roots to composite moduli quickly in general we could factor numbers quickly. I apologize for assuming that you knew that p was prime.
See here or here. Note that the numbers modulo p are the finite field with p elements.
Edit: See this also (this is the grandfather of those papers.)
The easy part is when p = 2 mod 3, then everything is a cube and athe cube root of a is just a**((2*p-1)/3) %p
Added: Here is code to do all but the primes 1 mod 9. I'll try to get to it this weekend. If no one else gets to it first
#assumes p prime returns cube root of a mod p
def cuberoot(a, p):
if p == 2:
return a
if p == 3:
return a
if (p%3) == 2:
return pow(a,(2*p - 1)/3, p)
if (p%9) == 4:
root = pow(a,(2*p + 1)/9, p)
if pow(root,3,p) == a%p:
return root
else:
return None
if (p%9) == 7:
root = pow(a,(p + 2)/9, p)
if pow(root,3,p) == a%p:
return root
else:
return None
else:
print "Not implemented yet. See the second paper"
Here is a complete code in pure python. By considering special cases first, it is almost as fast as the Peralta algoritm.
#assumes p prime, it returns all cube roots of a mod p
def cuberoots(a, p):
#Non-trivial solutions of x**r=1
def onemod(p,r):
sols=set()
t=p-2
while len(sols)<r:
g=pow(t,(p-1)//r,p)
while g==1: t-=1; g=pow(t,(p-1)//r,p)
sols.update({g%p,pow(g,2,p),pow(g,3,p)})
t-=1
return sols
def solutions(p,r,root,a):
todo=onemod(p,r)
return sorted({(h*root)%p for h in todo if pow(h*root,3,p)==a})
#---MAIN---
a=a%p
if p in [2,3] or a==0: return [a]
if p%3 == 2: return [pow(a,(2*p - 1)//3, p)] #One solution
#There are three or no solutions
#No solution
if pow(a,(p-1)//3,p)>1: return []
if p%9 == 7: #[7, 43, 61, 79, 97, 151]
root = pow(a,(p + 2)//9, p)
if pow(root,3,p) == a: return solutions(p,3,root,a)
else: return []
if p%9 == 4: #[13, 31, 67, 103, 139]
root = pow(a,(2*p + 1)//9, p)
print(root)
if pow(root,3,p) == a: return solutions(p,3,root,a)
else: return []
if p%27 == 19: #[19, 73, 127, 181]
root = pow(a,(p + 8)//27, p)
return solutions(p,9,root,a)
if p%27 == 10: #[37, 199, 307]
root = pow(a,(2*p +7)//27, p)
return solutions(p,9,root,a)
#We need a solution for the remaining cases
return tonelli3(a,p,True)
An extension of Tonelli-Shank algorithm.
def tonelli3(a,p,many=False):
def solution(p,root):
g=p-2
while pow(g,(p-1)//3,p)==1: g-=1 #Non-trivial solution of x**3=1
g=pow(g,(p-1)//3,p)
return sorted([root%p,(root*g)%p,(root*g**2)%p])
#---MAIN---
a=a%p
if p in [2,3] or a==0: return [a]
if p%3 == 2: return [pow(a,(2*p - 1)//3, p)] #One solution
#No solution
if pow(a,(p-1)//3,p)>1: return []
#p-1=3**s*t
s=0
t=p-1
while t%3==0: s+=1; t//=3
#Cubic nonresidu b
b=p-2
while pow(b,(p-1)//3,p)==1: b-=1
c,r=pow(b,t,p),pow(a,t,p)
c1,h=pow(c,3**(s-1),p),1
c=pow(c,p-2,p) #c=inverse modulo p
for i in range(1,s):
d=pow(r,3**(s-i-1),p)
if d==c1: h,r=h*c,r*pow(c,3,p)
elif d!=1: h,r=h*pow(c,2,p),r*pow(c,6,p)
c=pow(c,3,p)
if (t-1)%3==0: k=(t-1)//3
else: k=(t+1)//3
r=pow(a,k,p)*h
if (t-1)%3==0: r=pow(r,p-2,p) #r=inverse modulo p
if pow(r,3,p)==a:
if many:
return solution(p,r)
else: return [r]
else: return []
You can test it using:
test=[(17,1459),(17,1000003),(17,10000019),(17,1839598566765178548164758165715596714561757494507845814465617175875455789047)]
for a,p in test:
print "y^3=%s modulo %s"%(a,p)
sol=cuberoots(a,p)
print "p%s3=%s"%("%",p%3),sol,"--->",map(lambda t: t^3%p,sol)
which should yield (fast):
y^3=17 modulo 1459
p%3=1 [483, 329, 647] ---> [17, 17, 17]
y^3=17 modulo 1000003
p%3=1 [785686, 765339, 448981] ---> [17, 17, 17]
y^3=17 modulo 10000019
p%3=2 [5188997] ---> [17]
y^3=17 modulo 1839598566765178548164758165715596714561757494507845814465617175875455789047
p%3=1 [753801617033579226225229608063663938352746555486783903392457865386777137044, 655108821219252496141403783945148550782812009720868259303598196387356108990, 430688128512346825798124773706784225426198929300193651769561114101322543013] ---> [17, 17, 17]
I converted the code by Rolandb above into python3. If you put this into a file, you can import it and run it in python3, and if you run it standalone it will validate that it works.
#! /usr/bin/python3
def ts_cubic_modular_roots (a, p):
""" python3 version of cubic modular root code posted
by Rolandb on stackoverflow. With new formatting.
https://stackoverflow.com/questions/6752374/cube-root-modulo-p-how-do-i-do-this
"""
#Non-trivial solution of x**r = 1
def onemod (p, r):
t = p - 2
while pow (t, (p - 1) // r, p) == 1:
t -= 1
return pow (t, (p - 1) // r, p)
def solution(p, root):
g = onemod (p, 3)
return [root % p, (root * g) % p, (root * (g ** 2)) % p]
#---MAIN---
a = a % p
if p in [2, 3] or a == 0:
return [a]
if p % 3 == 2:
return [pow (a, ((2 * p) - 1) // 3, p)] #Eén oplossing
#There are 3 or no solutions
#No solution
if pow (a, (p-1) // 3, p) > 1:
return []
if p % 9 == 4: #[13, 31, 67]
root = pow (a, ((2 * p) + 1) // 9, p)
if pow (root, 3, p) == a:
return solution (p, root)
else:
return []
if p % 9 == 7: #[7, 43, 61, 79, 97
root = pow (a, (p + 2) // 9, p)
if pow (root, 3, p) == a:
return solution (p, root)
else:
return []
if p % 27 == 10: #[37, 199]
root = pow (a, ((2 * p) + 7) // 27, p)
h = onemod (p, 9)
for i in range (0,9):
if pow (root, 3, p) == a:
return solution (p, root)
root *= h
return []
if p % 27 == 19: #[19, 73, 127, 181]
root = pow (a, (p + 8)//27, p)
h = onemod (p, 9)
for i in range (0, 9):
if pow (root, 3, p) == a:
return solution (p, root)
root *= h
return []
#We need an algorithm for the remaining cases
return tonelli3 (a, p, True)
def tonelli3 (a, p, many = False):
#Non-trivial solution of x**r = 1
def onemod (p, r):
t = p - 2
while pow (t, (p - 1) // r, p) == 1:
t -= 1
return pow (t, (p - 1) // r, p)
def solution (p, root):
g = onemod (p, 3)
return [root % p, (root * g) % p, (root * (g**2)) % p]
#---MAIN---
a = a % p
if p in [2, 3] or a == 0:
return [a]
if p % 3 == 2:
return [pow (a, ((2 * p) - 1) // 3, p)] #Eén oplossing
#No solution
if pow (a, (p - 1) // 3, p) > 1:
return []
#p-1 = 3^s*t
s = 0
t = p - 1
while t % 3 == 0:
s += 1
t //= 3
#Cubic nonresidu b
b = p - 2
while pow (b, (p - 1) // 3, p) == 1:
b -= 1
c, r = pow (b, t, p), pow (a, t, p)
c1, h = pow (c, 3 ^ (s - 1), p), 1
c = pow (c, p - 2, p) #c=inverse_mod(Integer(c), p)
for i in range (1, s):
d = pow (r, 3 ^ (s - i - 1), p)
if d == c1:
h, r = h * c, r * pow (c, 3, p)
elif d != 1:
h, r = h * pow (c, 2, p), r * pow (c, 6, p)
c = pow (c, 3, p)
if (t - 1) % 3 == 0:
k = (t - 1) // 3
else:
k = (t + 1) // 3
r = pow (a, k, p) * h
if (t - 1) % 3 == 0:
r = pow (r, p - 2, p) #r=inverse_mod(Integer(r), p)
if pow (r, 3, p) == a:
if many:
return solution(p, r)
else: return [r]
else: return []
if '__name__' == '__main__':
import ts_cubic_modular_roots
tscr = ts_cubic_modular_roots.ts_cubic_modular_roots
test=[(17,1459),(17,1000003),(17,10000019),(17,1839598566765178548164758165715596714561757494507845814465617175875455789047)]
for a,p in test:
print ("y**3=%s modulo %s"%(a,p))
sol = tscr (a,p)
print ("p%s3=%s"%("%",p % 3), sol, [pow (t,3,p) for t in sol])
# results of the above
#y**3=17 modulo 1459
#p%3=1 [] []
#y**3=17 modulo 1000003
#p%3=1 [785686, 765339, 448981] [17, 17, 17]
#y**3=17 modulo 10000019
#p%3=2 [5188997] [17]
#y**3=17 modulo 1839598566765178548164758165715596714561757494507845814465617175875455789047
#p%3=1 [753801617033579226225229608063663938352746555486783903392457865386777137044, 655108821219252496141403783945148550782812009720868259303598196387356108990, 430688128512346825798124773706784225426198929300193651769561114101322543013] [17, 17, 17]
Sympy has a nice implementation for arbitrary integer modulo and arbitrary power: https://docs.sympy.org/latest/modules/ntheory.html#sympy.ntheory.residue_ntheory.nthroot_mod
from sympy.ntheory.residue_ntheory import nthroot_mod
a = 17
n = 3
modulo = 10000019
roots = nthroot_mod(a, n, modulo)
print(roots)
# 5188997
Related
I implemented a RSA Cryptography program, using python, and it works perfectly using prime numbers with aproximally 10 digits. But when I use numbers with 25 digits or more, for example, it does not work.
It worked with the following keys:
p = 2324731
q = 186647
e = 433899328297
n = 433904066957
It not worked with:
p = 3673864730662357928718503
q = 2127738717256957618781057
e = 7817024229395103552360986476332293342120062315901
n = 7817024229395103552360993847944520620136941797671
here's the code:
inverse (d key):
#classmethod
def __linearOperation(cls, a, b, mdc, i):
t = -int(a / b)
r = a % b
mdc.append([1, a, t, b])
if r == 1:
return mdc
inverseLine = cls.__linearOperation(b, r, mdc, i + 1)
s = inverseLine[i][0]
t = inverseLine[i][2]
inverseLine[i - 1][0] *= t
inverseLine[i - 1][2] *= t
inverseLine[i - 1][2] += s
inverseLine.remove(inverseLine[i])
return inverseLine
def __inverse(self, e, φ):
inverseLine = self.__linearOperation(e, φ, [], 1)
inverse = inverseLine[0][0]
if inverse < 0:
return inverse + φ
if inverse > φ:
return inverse % φ
else:
return inverse
Modular Exponentiation:
#staticmethod
def __QuickMod(base, exp, n):
result = 1
while exp > 0:
if exp & 1:
result = (result * base) % n
base = (base ** 2) % n
exp = exp >> 1
return result
encrypt/decrypt:
def encryptChar(self, n: int, e: int, M: int) -> int:
C = self.__QuickMod(M, e, n) # C = M^e mod n
return C
def decryptChar(self, p: int, q: int, e: int, C: int) -> int:
d = self.__inverse(e, (p - 1) * (q - 1))
M = self.__QuickMod(C, d, p * q) # M = C^d mod n
return M
By trying to encrypt the number 109 ("m" char in ascII), the encryptChar function returns 6825028446539883496812231478440519650519629664279.
By trying to decrypt the number above the decryptChar function should returns the number 109 back. But that's not what happens.
In my conception, by using python we do not have number size restriction.
Is there something i'm not seeing? Thank you for your help.
The problem is in my inverse algorithm. I'll try to re-implement it. But using pow() function with -1 as expoent works.
Description:
Given two positive integers N and R, how many different ways are there to cut a rod of length N into R pieces, such that the length of each piece is a positive integer? Output this answer modulo 1,000,000,007.
Example:
With N = 7 and R = 3, there are 15 ways to cut a rod of length 7 into 3 pieces: (1,1,5) , (1,5,1), (5,1,1) , (1,2,4) , (1,4,2) (2,1,4), (2,4,1) , (4,1,2), (4,2,1) , (1,3,3), (3,1,3), (3,3,1), (2,2,3), (2,3,2), (3,2,2).
Constraints:
1 <= R <= N <= 200,000
Testcases:
N R Output
7 3 15
36 6 324632
81 66 770289477
96 88 550930798
My approach:
I know that the answer is (N-1 choose R-1) mod 1000000007. I have tried all different ways to calculate it, but always 7 out of 10 test cases went time limit exceeded. Here is my code, can anyone tell me what other approach I can use to make it in O(1) time complexity.
from math import factorial
def new(n, r):
D = factorial(n - 1) // (factorial(r - 1) * factorial(n - r))
return (D % 1000000007)
if __name__ == '__main__':
N = [7, 36, 81, 96]
R = [3, 6, 66, 88]
answer = [new(n, r) for n,r in zip(N,R)]
print(answer)
I think there's two big optimizations that the problem is looking for you to exploit. The first being to cache intermediate values of factorial() to save computational effort across large batches (large T). The second optimization being to reduce your value mod 1000000007 incrementally, so your numbers stay small, and multiplication stays a constant-time. I've updated the below example to precompute a factorial table using a custom function and itertools.accumulate, instead of merely caching the calls in a recursive implementation (which will eliminate the issues with recursion depth you were seeing).
from itertools import accumulate
MOD_BASE = 1000000007
N_BOUND = 200000
def modmul(m):
def mul(x, y):
return x * y % m
return mul
FACTORIALS = [1] + list(accumulate(range(1, N_BOUND+1), modmul(MOD_BASE)))
def nck(n, k, m):
numerator = FACTORIALS[n]
denominator = FACTORIALS[k] * FACTORIALS[n-k]
return numerator * pow(denominator, -1, m) % m
def solve(n, k):
return nck(n-1, k-1, MOD_BASE)
Running this against the example:
>>> pairs = [(36, 6), (81, 66), (96, 88)]
>>> print([solve(n, k) for n, k in pairs])
[324632, 770289477, 550930798]
I literally translated code from accepted answer of Ivaylo Strandjev here and it works much faster:
def get_degree(n, p):# { // returns the degree with which p is in n!
degree_num = 0
u = p
temp = n
while (u <= temp):
degree_num += temp // u
u *= p
return degree_num
def degree(a, k, p):
res = 1
cur = a
while (k):
if (k % 2):
res = (res * cur) % p
k //= 2
cur = (cur * cur) % p
return res
def CNKmodP( n, k, p):
num_degree = get_degree(n, p) - get_degree(n - k, p)
den_degree = get_degree(k, p)
if (num_degree > den_degree):
return 0
res = 1
for i in range(n, n - k, -1):
ti = i
while(ti % p == 0):
ti //= p
res = (res * ti) % p
denom = 1
for i in range(1, k + 1):
ti = i
while(ti % p == 0):
ti //= p
denom = (denom * ti) % p
res = (res * degree(denom, p-2, p)) % p
return res
To apply this approach, you just need to call
result = CNKmodP(n-1, r-1, 1000000007)
In Java we can use BigInteger because the value of factorials that we calculate may not fit in integer. Additionally BigInteger provides built in methods multiply and divide.
static int CNRmodP(int N, int R, int P) {
BigInteger ret = BigInteger.ONE;
for (int i = 0; i < R; i++) {
ret = ret.multiply(BigInteger.valueOf(N - i))
.divide(BigInteger.valueOf(i + 1));
}
BigInteger p = BigInteger.valueOf(P);
//Calculate Modulus
BigInteger answer = ret.mod(p);
//Convert BigInteger to integer and return it
return answer.intValue();
}
To apply the above approach, you just need to call
result = CNRmodP(N-1, R-1, 1000000007);
I have a working script (Powered by Python 2.7) :
import sys
a=0
b=7
p=0xB12D
x2=0x38F
if (len(sys.argv)>1):
x1=int(sys.argv[1])
if (len(sys.argv)>2):
x2=int(sys.argv[2])
if (len(sys.argv)>3):
p=int(sys.argv[3])
if (len(sys.argv)>4):
a=int(sys.argv[4])
if (len(sys.argv)>5):
b=int(sys.argv[5])
def modular_sqrt(a, p):
""" Find a quadratic residue (mod p) of 'a'. p
must be an odd prime.
Solve the congruence of the form:
x^2 = a (mod p)
And returns x. Note that p - x is also a root.
0 is returned is no square root exists for
these a and p.
The Tonelli-Shanks algorithm is used (except
for some simple cases in which the solution
is known from an identity). This algorithm
runs in polynomial time (unless the
generalized Riemann hypothesis is false).
"""
# Simple cases
#
if legendre_symbol(a, p) != 1:
return 0
elif a == 0:
return 0
elif p == 2:
return p
elif p % 4 == 3:
return pow(a, (p + 1) / 4, p)
# Partition p-1 to s * 2^e for an odd s (i.e.
# reduce all the powers of 2 from p-1)
#
s = p - 1
e = 0
while s % 2 == 0:
s /= 2
e += 1
# Find some 'n' with a legendre symbol n|p = -1.
# Shouldn't take long.
#
n = 2
while legendre_symbol(n, p) != -1:
n += 1
x = pow(a, (s + 1) / 2, p)
b = pow(a, s, p)
g = pow(n, s, p)
r = e
while True:
t = b
m = 0
for m in xrange(r):
if t == 1:
break
t = pow(t, 2, p)
if m == 0:
return x
gs = pow(g, 2 ** (r - m - 1), p)
g = (gs * gs) % p
x = (x * gs) % p
b = (b * g) % p
r = m
def legendre_symbol(a, p):
""" Compute the Legendre symbol a|p using
Euler's criterion. p is a prime, a is
relatively prime to p (if p divides
a, then a|p = 0)
Returns 1 if a has a square root modulo
p, -1 otherwise.
"""
ls = pow(a, (p - 1) / 2, p)
return -1 if ls == p - 1 else ls
def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y)
def modinv(a, m):
g, x, y = egcd(a, m)
if g != 1:
print ("x")
else:
return x % m
def hexint(i): return int(i,0)
print "a=",a
print "b=",b
print "p=",p
print "x-point=",x2
# Read numbers from file and put them in an array
with open("List.txt","r") as f:
# arrX1 = list(map(int,f.readlines()))
arrX1 = list(map(hexint,f.readlines()))
f.close()
# Open the result file to write to
f = open('Result.txt', 'w')
# Now get x1 for each item in the list of numbers from the file
# then do the calculations
# and write the result
for x1 in arrX1:
z=(x1**3 + a*x1 +b) % p
y1=modular_sqrt(z, p)
z=(x2**3 + a*x2 +b) % p
y2=modular_sqrt(z, p)
print "\nP1\t(%d,%d)" % (x1,y1)
print "P2\t(%d,%d)" % (x2,y2)
s=((-y2)-y1)* modinv(x2-x1,p)
x3=(s**2-x2-x1) % p
y3=((s*(x2-x3)+y2)) % p
result = "\nQ(%d\n,%d)" % (x3,y3)
f.write(result)
f.close()
But errors occur in this script due to a negative value during processing.
(That is, when performing calculations using the "s =" formula, the value becomes negative and the script stops.)
Here is the error:
Traceback (most recent call last):
File "E: \ 005.py", line 148, in <module>
s = ((- y2) -y1) * modinv (x2-x1, p)
TypeError: unsupported operand type (s) for *: 'long' and 'NoneType'
>>>
I need my script not to stop, but to write only the correct result to the file: "Result.txt".
And what was not correctly ignored and continued to work!
Is it possible to ignore this stop?
That is, if an error occurs, do not stop the process and execute other sequential commands?
I am not very strong in the Python language and cannot fix the script so that the function skips this error.
If you expect that function can return wrong value - None - then you should get it separtelly and use if/else to skip it
value = modinv(x2-x1, p)
if value is not None:
s = (-y2-y1) * value
x3 = (s**2-x2-x1) % p
y3 = (s*(x2-x3)+y2) % p
result = "\nQ(%d\n,%d)" % (x3, y3)
f.write(result)
else:
print('TypeError for:', x2, x1, p)
#f.write("\nNo Result")
Eventually you can use try/except to catch this error
try:
s = (-y2-y1) * modinv(x2-x1, p)
x3 = (s**2-x2-x1) % p
y3 = (s*(x2-x3)+y2) % p
result = "\nQ(%d\n,%d)" % (x3, y3)
f.write(result)
except TypeError:
print('TypeError for:', x2, x1, p)
#f.write("\nNo Result")
I am trying to implement the function fast modular exponentiation(b, k, m) which computes:
b(2k) mod m using only around 2k modular multiplications.
I tried this method:
def FastModularExponentiation(b, k, m):
res = 1
b = b % m
while (k > 0):
if ((k & 1) == 1):
res = (res * b) % m
k = k >> 1
b = (b * b) % m
return res
but I am still stuck in same problem which is if I try b = 2, k = 1, m = 10, my code returns 22. However, the correct answer is:
2^(2^1) mod 10 = 2^2 mod 10 = 4
and I cannot find the reason why.
Update: I finally understood that you do not want regular modular exponentiation (i.e., b^k mod m), but b^(2^k) mod m (as you plainly stated).
Using the regular built-in Python function pow this would be:
def FastModularExponentiation(b, k, m):
return pow(b, pow(2, k), m)
Or, without using pow:
def FastModularExponentiation(b, k, m):
b %= m
for _ in range(k):
b = b ** 2 % m
return b
If you know r = phi(m) (Euler's totient function), you could reduce the exponent first: exp = pow(2, k, r) and then calculate pow(b, exp, m). Depending on the input values, this might speed things up.
(This was the original answer when I thought you wanted, b^k mod m)
This is what works for me:
def fast_mod_exp(b, exp, m):
res = 1
while exp > 1:
if exp & 1:
res = (res * b) % m
b = b ** 2 % m
exp >>= 1
return (b * res) % m
The only significant differences I spot is in the last line: return (b * res) % m and that my while loop terminates earlier: while exp > 1 (which should be the same thing you do - except it saves an unnecessary squaring operation).
Also note that the built-in function pow will do all that for free (if you supply a third argument):
pow(4, 13, 497)
# 445
def fast_exponentiation(k, x, q):
# make sure all variables are non-negative
assert (k >= 0 and x >= 0 and q >=1)
result = 1 # define a counter
while x:
if x % 2 == 1:
result = (result * k) % q
k = (k ^ 2) % q
x >> = 1 # bit shift operator, dividing x by 2 ** y thus x >> 2 ** 1 = x / 2
return result
In factoring methods based on the quadratic sieve, finding the left null space of a binary matrix (values computed mod 2) is a crucial step. (This is also the null space of the transpose.) Does numpy or scipy have tools to do this quickly?
For reference, here is my current code:
# Row-reduce binary matrix
def binary_rr(m):
rows, cols = m.shape
l = 0
for k in range(min(rows, cols)):
print(k)
if l >= cols: break
# Swap with pivot if m[k,l] is 0
if m[k,l] == 0:
found_pivot = False
while not found_pivot:
if l >= cols: break
for i in range(k+1, rows):
if m[i,l]:
m[[i,k]] = m[[k,i]] # Swap rows
found_pivot = True
break
if not found_pivot: l += 1
if l >= cols: break # No more rows
# For rows below pivot, subtract row
for i in range(k+1, rows):
if m[i,l]: m[i] ^= m[k]
l += 1
return m
It is pretty much a straightforward implementation of Gaussian elimination, but since it's written in python it is very slow.
qwr, I found a very fast gaussian elimination routine that finishes so qiuckly that the slow point is the Quadratic Sieving or SIQS Sieving step. The gaussian elimination functions were taken from skollmans factorise.py at https://raw.githubusercontent.com/skollmann/PyFactorise/master/factorise.py
I'll soon be working on a SIQS/GNFS implementation from scratch, and hope to write something super quick for python with multithreading and possiblly cython. In the meantime, if you want something that compiles C (Alpertons ECM Engine) but uses python, you can use: https://github.com/oppressionslayer/primalitytest/ which requires you to cd into calculators directory and run make before importing p2ecm with from sfactorint import p2ecm. With that you can factorise 60 digit numbers in a few seconds.
# Requires sympy and numpy to be installed
# Adjust B and I accordingly. Set for 32 length number
# Usage:
# N=1009732533765251*1896182711927299
# factorise(N, 5000, 25000000) # Takes about 45-60 seconds on a newer computer
# N=1009732533765251*581120948477
# Linear Algebra Step finishes in 1 second, if that
# N=factorise(N, 5000, 2500000) # Takes about 5 seconds on a newer computer
# #Out[1]: 581120948477
import math
import numpy as np
from sympy import isprime
#
# siqs_ functions are the Gaussian Elimination routines right from
# skollmans factorise.py. It is the fastest Gaussian Elimination that i have
# found in python
#
def siqs_factor_from_square(n, square_indices, smooth_relations):
"""Given one of the solutions returned by siqs_solve_matrix_opt,
return the factor f determined by f = gcd(a - b, n), where
a, b are calculated from the solution such that a*a = b*b (mod n).
Return f, a factor of n (possibly a trivial one).
"""
sqrt1, sqrt2 = siqs_calc_sqrts(square_indices, smooth_relations)
assert (sqrt1 * sqrt1) % n == (sqrt2 * sqrt2) % n
return math.gcd(abs(sqrt1 - sqrt2), n)
def siqs_find_factors(n, perfect_squares, smooth_relations):
"""Perform the last step of the Self-Initialising Quadratic Field.
Given the solutions returned by siqs_solve_matrix_opt, attempt to
identify a number of (not necessarily prime) factors of n, and
return them.
"""
factors = []
rem = n
non_prime_factors = set()
prime_factors = set()
for square_indices in perfect_squares:
fact = siqs_factor_from_square(n, square_indices, smooth_relations)
if fact != 1 and fact != rem:
if isprime(fact):
if fact not in prime_factors:
print ("SIQS: Prime factor found: %d" % fact)
prime_factors.add(fact)
while rem % fact == 0:
factors.append(fact)
rem //= fact
if rem == 1:
break
if isprime(rem):
factors.append(rem)
rem = 1
break
else:
if fact not in non_prime_factors:
print ("SIQS: Non-prime factor found: %d" % fact)
non_prime_factors.add(fact)
if rem != 1 and non_prime_factors:
non_prime_factors.add(rem)
for fact in sorted(siqs_find_more_factors_gcd(non_prime_factors)):
while fact != 1 and rem % fact == 0:
print ("SIQS: Prime factor found: %d" % fact)
factors.append(fact)
rem //= fact
if rem == 1 or sfactorint_isprime(rem):
break
if rem != 1:
factors.append(rem)
return factors
def add_column_opt(M_opt, tgt, src):
"""For a matrix produced by siqs_build_matrix_opt, add the column
src to the column target (mod 2).
"""
M_opt[tgt] ^= M_opt[src]
def find_pivot_column_opt(M_opt, j):
"""For a matrix produced by siqs_build_matrix_opt, return the row of
the first non-zero entry in column j, or None if no such row exists.
"""
if M_opt[j] == 0:
return None
return lars_last_powers_of_two_trailing(M_opt[j] + 1)
def siqs_build_matrix_opt(M):
"""Convert the given matrix M of 0s and 1s into a list of numbers m
that correspond to the columns of the matrix.
The j-th number encodes the j-th column of matrix M in binary:
The i-th bit of m[i] is equal to M[i][j].
"""
m = len(M[0])
cols_binary = [""] * m
for mi in M:
for j, mij in enumerate(mi):
cols_binary[j] += "1" if mij else "0"
return [int(cols_bin[::-1], 2) for cols_bin in cols_binary], len(M), m
def siqs_solve_matrix_opt(M_opt, n, m):
"""
Perform the linear algebra step of the SIQS. Perform fast
Gaussian elimination to determine pairs of perfect squares mod n.
Use the optimisations described in [1].
[1] Koç, Çetin K., and Sarath N. Arachchige. 'A Fast Algorithm for
Gaussian Elimination over GF (2) and its Implementation on the
GAPP.' Journal of Parallel and Distributed Computing 13.1
(1991): 118-122.
"""
row_is_marked = [False] * n
pivots = [-1] * m
for j in range(m):
i = find_pivot_column_opt(M_opt, j)
if i is not None:
pivots[j] = i
row_is_marked[i] = True
for k in range(m):
if k != j and (M_opt[k] >> i) & 1: # test M[i][k] == 1
add_column_opt(M_opt, k, j)
perf_squares = []
for i in range(n):
if not row_is_marked[i]:
perfect_sq_indices = [i]
for j in range(m):
if (M_opt[j] >> i) & 1: # test M[i][j] == 1
perfect_sq_indices.append(pivots[j])
perf_squares.append(perfect_sq_indices)
return perf_squares
def sqrt_int(N):
Nsqrt = math.isqrt(N)
assert Nsqrt * Nsqrt == N
return Nsqrt
def siqs_calc_sqrts(square_indices, smooth_relations):
"""Given on of the solutions returned by siqs_solve_matrix_opt and
the corresponding smooth relations, calculate the pair [a, b], such
that a^2 = b^2 (mod n).
"""
res = [1, 1]
for idx in square_indices:
res[0] *= smooth_relations[idx][0]
res[1] *= smooth_relations[idx][1]
res[1] = sqrt_int(res[1])
return res
def quad_residue(a,n):
l=1
q=(n-1)//2
x = q**l
if x==0:
return 1
a =a%n
z=1
while x!= 0:
if x%2==0:
a=(a **2) % n
x//= 2
else:
x-=1
z=(z*a) % n
return z
def STonelli(n, p):
assert quad_residue(n, p) == 1, "not a square (mod p)"
q = p - 1
s = 0
while q % 2 == 0:
q //= 2
s += 1
if s == 1:
r = pow(n, (p + 1) // 4, p)
return r,p-r
for z in range(2, p):
#print(quad_residue(z, p))
if p - 1 == quad_residue(z, p):
break
c = pow(z, q, p)
r = pow(n, (q + 1) // 2, p)
t = pow(n, q, p)
m = s
t2 = 0
while (t - 1) % p != 0:
t2 = (t * t) % p
for i in range(1, m):
if (t2 - 1) % p == 0:
break
t2 = (t2 * t2) % p
b = pow(c, 1 << (m - i - 1), p)
r = (r * b) % p
c = (b * b) % p
t = (t * c) % p
m = i
return (r,p-r)
def build_smooth_relations(smooth_base, root_base):
smooth_relations = []
for xx in range(len(smooth_base)):
smooth_relations.append((root_base[xx], smooth_base[xx], xx))
return smooth_relations
def strailing(N):
return N>>lars_last_powers_of_two_trailing(N)
def lars_last_powers_of_two_trailing(N):
p,y=1,2
orign = N
#if orign < 17: N = N%16
N = N&15
if N == 1:
if ((orign -1) & (orign -2)) == 0: return orign.bit_length()-1
while orign&y == 0:
p+=1
y<<=1
return p
if N in [3, 7, 11, 15]: return 1
if N in [5, 13]: return 2
if N == 9: return 3
return 0
def build_matrix(factor_base, smooth_base):
factor_base = factor_base.copy()
factor_base.insert(0, 2)
sparse_matrix = []
col = 0
for xx in smooth_base:
sparse_matrix.append([])
for fx in factor_base:
count = 0
factor_found = False
while xx % fx == 0:
factor_found = True
xx=xx//fx
count+=1
if count % 2 == 0:
sparse_matrix[col].append(0)
continue
else:
if factor_found == True:
sparse_matrix[col].append(1)
else:
sparse_matrix[col].append(0)
col+=1
return np.transpose(sparse_matrix)
def get_mod_congruence(root, N, withstats=False):
r = root - N
if withstats==True:
print(f"{root} ≡ {r} mod {N}")
return r
def primes_sieve2(limit):
a = np.ones(limit, dtype=bool)
a[0] = a[1] = False
for (i, isprime) in enumerate(a):
if isprime:
yield i
for n in range(i*i, limit, i):
a[n] = False
def remove_singletons(XX):
no_singletons = []
for xx in XX:
if len(xx) != 1:
no_singletons.append(xx)
return no_singletons
def fb_sm(N, B, I):
factor_base, sieve_base, sieve_list, smooth_base, root_base = [], [], [], [], []
primes = list(primes_sieve2(B))
i,root=-1,math.isqrt(N)
for x in primes[1:]:
if quad_residue(N, x) == 1:
factor_base.append(x)
for x in range(I):
xx = get_mod_congruence((root+x)**2, N)
sieve_list.append(xx)
if xx % 2 == 0:
xx = strailing(xx+1) # using lars_last_modulus_powers_of_two(xx) bit trick
sieve_base.append(xx)
for p in factor_base:
residues = STonelli(N, p)
for r in residues:
for i in range((r-root) % p, len(sieve_list), p):
while sieve_base[i] % p == 0:
sieve_base[i] //= p
for o in range(len(sieve_list)):
# This is set to 350, which is only good for numbers
# of len < 32. Modify
# to be more dynamic for larger numbers.
if len(smooth_base) >= 350:
break
if sieve_base[o] == 1:
smooth_base.append(sieve_list[o])
root_base.append(root+o)
return factor_base, smooth_base, root_base
def isSquare(hm):
cr=math.isqrt(hm)
if cr*cr == hm:
return True
return False
def find_square(smooth_base):
for x in smooth_base:
if isSquare(x):
return (True, smooth_base.index(x))
else:
return (False, -1)
t_matrix=[]
primes=list(primes_sieve2(1000000))
def factorise(N, B=10000, I=10000000):
global primes, t_matrix
if isprime(N):
return N
for xx in primes:
if N%xx == 0:
return xx
factor_base, smooth_base, root_base = fb_sm(N,B,I)
issquare, t_matrix = find_square(smooth_base)
if issquare == True:
return math.gcd(math.isqrt(smooth_base[t_matrix])+get_mod_congruence(root_base[t_matrix], N), N)
t_matrix = build_matrix(factor_base, smooth_base)
smooth_relations = build_smooth_relations(smooth_base, root_base)
M_opt, M_n, M_m = siqs_build_matrix_opt(np.transpose(t_matrix))
perfect_squares = remove_singletons(siqs_solve_matrix_opt(M_opt, M_n, M_m))
factors = siqs_find_factors(N, perfect_squares, smooth_relations)
return factors