funKatz
funKatz

Reputation: 33

Implementing Miller-Rabin Algorithm on Python

I'm trying to implement the Miller-Rabin algorithm on Python.
I have coded just as my textbook's pseudocode says, but for some reason, it it not working as I have expected.
To be sepcific, the function 'test' returns sometimes return 'true' when it goes to Fermat test.

def miller_rabin(n, s):
    if n == 2:
        return Prime
    elif n % 2 == 0:
        return Composite

    for _ in range(s):
        a = random.randint(1, n-1)
        if test(a, n) == True:
            return Composite

    return Prime

def test(a, n):
    t, u = 0, n-1
    while (u % 2 == 0):
        t += 1                  #t >= 1, u is odd, n=1 = 2^t * u
        u //= 2                 #initialization
    x = exp(a, u, n)           #initializing x0 = a^u mod n

    for _ in range(t-1):        #for i = 1 to t
        x_prev = x              #xi-1
        x = exp(x_prev, 2, n)   #xi = (xi-1)^2 mod n
        if x == 1 and x_prev != 1 and x_prev != (n-1):      #NSR test
            return True

    if x != 1:                  #Fermat test
        return True
    return False

I have been struggling because of this for few hours, and still cannot find which part of the code is the problem. Please let me know if you have any advice. P.S. exp (a,b,c) returns a^b mod c.

Upvotes: 1

Views: 544

Answers (2)

Michel
Michel

Reputation: 107

The implementation of the Miller-Rabin algorithm in Python is:

# Check for primality using the Miller-Rabin algorithm
def is_prime(n, k = 40):
    import random
    if n < 2 or (n != 2 and not n & 1):
        return False
    if n < 6:
        return True
    random_gen = random.SystemRandom()
    for _ in range(k):
        a = random_gen.randrange(2, n - 1)
        exp = n - 1
        while not exp & 1:
            exp >>= 1
        if pow(a, exp, n) == 1:
            continue
        while exp < n - 1:
            if pow(a, exp, n) == n - 1:
                break
            exp <<= 1
        else:
            return False
    return True

Upvotes: 0

oppressionslayer
oppressionslayer

Reputation: 7224

Here is a different Miller Rabin Implementation that works well. The MillerRabin function may be what you're most interested in:

def llinear_diophantinex(a, b, divmodx=1, x=1, y=0, withstats=False, pow_mod_p2=False): 
  origa, origb = a, b 
  r=a  
  q = a//b 
  prevq=1  
  if withstats == True: 
    print(f"a = {a}, b = {b}, q = {q}, r = {r}")   
  while r != 0:  
       prevr = r  
       a,r,b = b, b, r   
       q,r = divmod(a,b) 
       x, y = y, x - q * y 
       if withstats == True: 
         print(f"a = {a}, b = {b}, q = {q}, r = {r}, x = {x}, y = {y}")  
  y = 1 - origb*x // origa - 1 
  x,y=y,x 
  modx = (-abs(x)*divmodx)%origb 
  if withstats == True: 
    print(f"x = {x}, y = {y}, modx = {modx}") 
  if pow_mod_p2==False:   
    return x, y, modx 
  else: 
    if x < 0: return modx 
    else: return x 

def ltrailing(N):
    return len(str(bin(N))) - len(str(bin(N)).rstrip('0'))

def MillerRabin(N, primetest, iterx, powx, withstats=False): 
  primetest = pow(primetest, powx, N) 
  if withstats == True:
     print("first: ",primetest) 
  if primetest == 1 or primetest == N - 1: 
    return True 
  else: 
    for x in range(0, iterx-1): 
       primetest = pow(primetest, 2, N) 
       if withstats == True:
          print("else: ", primetest) 
       if primetest == N - 1: return True 
       if primetest == 1: return False 
  return False 

def sfactorint_isprime(N, withstats=False):
    if N == 2:
      return True
    if N % 2 == 0:
      return False
    if N < 2:
        return False
    iterx = ltrailing(N - 1)
    """ This k test is an algorithmic test builder instead of using
        random numbers. The offset of k, from -2 to +2 produces pow tests
        that fail or pass instead of having to use random numbers and more
        iterations. All you need are those 5 numbers from k to get a 
        primality answer. This is the same as doing:
        pow(N, (1<<N.bit_length()) - 1, 1<<N.bit_length()) but much faster
        using a linear diophantine formula which gives the same result for 
        powers of 2
    """
    k = llinear_diophantinex(N, 1<<N.bit_length(), pow_mod_p2=True) - 1
    t = N >> iterx
    tests = [k-2, k-1, k, k+1, k+2]
    for primetest in tests:
        if primetest >= N:
            primetest %= N
        if primetest >= 2:
            if MillerRabin(N, primetest, iterx, t, withstats) == False:
                return False
    return True

Upvotes: 0

Related Questions