In [1]:
# Extended Euclidean algorithm (builtin function), very fast 
# Allows us to compute inverse of a mod m 
xgcd(3,26)
Out[1]:
(1, 9, -1)
In [2]:
# xgcd(a,m) = (d,b,c) s.t.
# ab + mc = d = gcd(a,m)
In [4]:
3 * 9 + 26 * (-1) == 1
Out[4]:
True
In [5]:
xgcd(15, 26)
Out[5]:
(1, 7, -4)
In [6]:
7*15 % 26
Out[6]:
1
In [ ]:
# So inverse of 15 mod 26 is 7
In [7]:
# CHALLENGE: finish this, using modular reduction at each step
def exp_mod(a,e,m):
    """Return a^e % m """
    # Don't just do: return (a^e) % m 
    prod = 1 
    for i in range(e):
        prod = (prod * a) % m  # reduce mod m at each step
        # prevents numbers from getting too big
    return prod
In [8]:
exp_mod(2,10,7)
Out[8]:
2
In [9]:
exp_mod(3,3,41)
Out[9]:
27
In [14]:
exp_mod(3,10000001,67)
Out[14]:
66
In [ ]:
# Want: modular exponentiation algorithm that takes roughly num_digits(e) steps 
# to compute a^e mod b
In [15]:
# CHALLENGE: finish this, using "repeated squaring"
def exp_mod_powtwo(a,n,m):
    """Return a^(2^n) % m """
    # Don't do this: return exp_mod(a,2^n,m)
    prod = a % m  # start with a%m, rather than 1
    for i in range(n):
        prod = (prod * prod) % m  # square, and reduce mod m
    return prod
    
In [16]:
exp_mod_powtwo(3,4,7)
Out[16]:
4
In [17]:
exp_mod_powtwo(2,5,11)
Out[17]:
4
In [18]:
exp_mod(2,2^5,11)
Out[18]:
4
In [21]:
exp_mod_powtwo(2,1000,11)
Out[21]:
9
In [20]:
exp_mod(2,2^1000,11) # should compute same thing as above, but runs into error
# because numbers are too big
---------------------------------------------------------------------------
OverflowError                             Traceback (most recent call last)
<ipython-input-20-aa9c70a7cde9> in <module>()
----> 1 exp_mod(Integer(2),Integer(2)**Integer(1000),Integer(11))

<ipython-input-7-bb73e83ef30a> in exp_mod(a, e, m)
      4     # Don't just do: return (a^e) % m
      5     prod = Integer(1)
----> 6     for i in range(e):
      7         prod = (prod * a) % m
      8     return prod

OverflowError: range() result has too many items
In [ ]: