In [1]:
alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
In [4]:
# Multiplication cipher
def mult_cipher(plaintext, key):
    """Convert each char c to number n using alpha. 
    Then n-> n*key mod 26. Convert back to char"""
    ciphertext = []
    for i in range(len(plaintext)):  
        c = plaintext[i] 
        numc = alpha.find(c)
        multnumc = (numc * key) % 26 # only significant change vs shift_cipher
        codedc = alpha[multnumc]
        ciphertext.append(codedc)
    return ciphertext
In [8]:
"".join(mult_cipher("ABCDEFGHIJKLMNOPQRSTUV", 3))
Out[8]:
'ADGJMPSVYBEHKNQTWZCFIL'
In [9]:
"".join(mult_cipher("ABCDEFGHIJKLMNOPQRSTUV", 2))
Out[9]:
'ACEGIKMOQSUWYACEGIKMOQ'
In [10]:
mult_cipher('A',2)
Out[10]:
['A']
In [15]:
mult_cipher('N',2)
Out[15]:
['A']
In [ ]:
# From the encrypted message 'A', can't tell whether original message was
# 'A' or 'N'
# So 2 is not a good encryption key to use
In [17]:
# To decipher mult_cipher(plaintext, 3), use mult_cipher(ciphertext, 9)
# Works because 3*9 % 26 = 1
"".join(mult_cipher('ADGJMPSVYBEHKNQTWZCFIL', 9))
Out[17]:
'ABCDEFGHIJKLMNOPQRSTUV'
In [18]:
mult_cipher("HELLO", 3)
Out[18]:
['V', 'M', 'H', 'H', 'Q']
In [ ]:
# Want to understand whick keys can be inverted modulo 26
# Need to understand when two numbers a,b have a common factor
In [ ]:
# For this, we will use the technique of recursion to compute gcd (next time)
In [ ]:
# Intro to recursino 
In [21]:
# factorial function n!, WITHOUT recursion, using loop
def fact_loop(n):
    run_prod = 1
    for i in range(1,n+1):
        run_prod *= i
    return run_prod
In [23]:
fact_loop(5)
Out[23]:
120
In [30]:
# factorial WITH recursion
def fact(n):
    print(n)
    if n==1:
        return 1  # base case
    else:
        return n * fact(n-1) # function calls itself here: a recursive call
In [31]:
fact(3)
3
2
1
Out[31]:
6
In [32]:
# CHALLENGE: compute nth elt of fibonacci sequence using recursion
# a0=1, a1=1, a2=2, a3=3, a4=5, a5=8, a6=13
def fib(n):
    if n==0 or n==1:
        return 1
    return fib(n-1) + fib(n-2)
In [34]:
[fib(n) for n in range(10)] # test above
Out[34]:
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
In [38]:
fib(20)
Out[38]:
10946
In [ ]:
# this fib function is slower than the fib function from a previous class
# Reason: there is a slot of redunancy 
# (for instance, computes fib(n-2) multiple times)