In [1]:
bin(21) # convert to binary (base 2)
# note 0b at the beginning, which is just to tell user that this is a binary representation
Out[1]:
'0b10101'
In [5]:
bin(21)[-1]   # get last bit
Out[5]:
'b'
In [6]:
bin(2349)
Out[6]:
'0b100100101101'
In [1]:
bin(19)[2:]  # get rid of the '0b' at the beginning
Out[1]:
'10011'
In [7]:
# CHALLENGE: compute 5^13 % 6 by hand, using:
bin(13)
Out[7]:
'0b1101'
In [10]:
bin(13)[3] 
Out[10]:
'1'
In [11]:
def exp_mod(a,e,b):
    """Returns a^e mod b, using repeated squaring and binary expansion.
    Computes same thing as exp_mod from last time, but much faster"""
    bin_rep = bin(e)[2:] # gets rid of '0b' from the binary expansion
    prod = 1  # keep track of the current product
    cur_a_powtwo = a % b  # keeps track of a^(2^i) for higher and higher i
    for i in range(len(bin_rep)):
        if bin_rep[-1-i]=='1': # gets ith from last element in bin rep
            prod = prod * cur_a_powtwo % b  # if corresponding bit is 1, 
            # multiply prod by the current a^(2^i)
        cur_a_powtwo = (cur_a_powtwo)^2 % b  # repeated squaring step
    return prod
    
In [12]:
exp_mod(5,13,6)
Out[12]:
5
In [13]:
exp_mod(3,21,7)
Out[13]:
6
In [14]:
exp_mod(2,3,7)
Out[14]:
1
In [17]:
exp_mod(2,3842701232130031923493,7) # very fast
Out[17]:
2
In [18]:
# number of steps to compute above:
len(bin(3842701232130031923493))
Out[18]:
74
In [ ]:
# number of steps to compute a^e mod b is about:
# log_2(e) (i.e. number of bits in e)
# this is much smaller than e itself