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), where
# d= gcd(a,m) = ab + mc
In [3]:
1 == 3*9 + 26*(-1)
Out[3]:
True
In [4]:
xgcd(15,26)
Out[4]:
(1, 7, -4)
In [5]:
# In above, 7 is inverse of 15 mod 26
7*15 % 26
Out[5]:
1
In [6]:
xgcd(124124892173, 123891237273231) # fast
Out[6]:
(1, -21325555939501, 21365775254)
In [7]:
# CHALLENGE: finish this function
def exp_mod(a,e,b):
    """Compute a^e mod b"""
    # don't just do: return (a^e) % b 
    prod = 1 
    for i in range(e):
        prod = (prod * a) % b # reduce mod m at each step,
        # prevents numbers from getting too big
    return prod
In [8]:
# test on 2^10000 % 7, exp_mod(2,10000,7)
In [9]:
exp_mod(2,12,7)
Out[9]:
1
In [11]:
exp_mod(2, 10000, 7)
Out[11]:
2
In [15]:
%%time
exp_mod(2,19237300,7)
CPU times: user 2.77 s, sys: 1.27 s, total: 4.03 s
Wall time: 4.02 s
Out[15]:
2
In [ ]:
# Want: modular exponentiation algorithm that takes roughly num_digits(e) steps 
# to compute a^e mod b 
In [16]:
# CHALLENGE: finish this, using "repeated squaring"
def exp_mod_powtwo(a, n, b):
    """Return a^(2^n) mod b"""
    prod = a % b  # start with a%b, rather than 1
    for i in range(n):
        prod = (prod * prod) % b  # square, and reduce mod b
    return prod
In [17]:
exp_mod_powtwo(3,3,7)
Out[17]:
2
In [24]:
exp_mod_powtwo(2, 10, 17)
Out[24]:
1
In [21]:
exp_mod(2, 2^10, 17)
Out[21]:
1
In [25]:
exp_mod_powtwo(2, 1000, 17) # same as above, but faster
Out[25]:
1
In [ ]: