Python

➡Created by H Bharath Bhat

Download the pdf version here


Contents

Algorithm

  1. Algorithm: how to systematically perform a task
  1. Programming language describes the step
  1. Algorithms that manipulate information:
    • Compute numerical functions - f(x,y)
    • Reorganize data - arrange in ascending order
    • Optimization - find the shortest route

Greatest Common Divisor

gcd(m, n)

  • gcd must be the common divisor which divides both the numbers
  • for example: gcd(8, 12) = 4
  • 1 divides every number

Steps

  1. List out factors of m
  1. List out factors of n
  1. Report the largest number that appears in the list

def gcd(m, n):
    m_multiples = []
    n_multiples = []
    for i in range(1, m+1):
        if m%i == 0:
            m_multiples.append(i)
    for i in range(1, n+1):
        if n%i == 0:
            n_multiples.append(i)
    
    for i in range (0, len(m_multiples)):
        for j in range(0, len(n_multiples)):
            if m_multiples[i] == n_multiples[j]:
                gcd = m_multiples[i]
    return gcd

print(gcd(8,12))
print(gcd(18,25))


'''
Outputs
4
1
'''

An algorithm for gcd(m, n)

  1. Use fm, fn for list of m, n respectively
  1. For each i from 1 to m, add(append) i to fm if i divides m
  1. For each j from 1 to n, add(append) j to fm if j divides n
  1. Use cf for list of common factors
  1. For each f in fm, add f to cf if f also appears in fn
  1. Return rightmost value in cf (largest value)

Better code

  1. We scan from 1 to m to compute fm and again from 1 to n to compute fn
  1. Instead we can scan only once
    1. For each i in 1 to max(m,n) add i to fm if i divides m and add i to fn if i divides n

Best code

  1. Compare them to compute common factors cf, at one shot
    1. For each i in 1 to max(m,n), if i divides m and i also divides n, then add i to cf
  1. Actually, any common factor must be less than min(m,n)
    1. For each i in 1 to min(m,n), if i divides m and i also divides n, then add i to cf

def gcd(m,n):
    multiples = []
    for i in range (1, min(m,n)+1):
        if m%i == 0 and n%i == 0:
            multiples.append(i)
    return max(multiples)

print(gcd(8,12))
print(gcd(18,25))

'''
Outputs
4
1
'''

Even more better

  1. We do not require any list at all, a value can be stored in a variable

def gcd(m,n):
    for i in range (1, min(m,n)+1):
        if m%i == 0 and n%i == 0:
            gcd = i
    return gcd

print(gcd(8,12))
print(gcd(18,25))

'''
Outputs
4
1
'''

Better code than before

Can be made even more efficient by scanning backwards instead of from 1

Let i run from min(m,n) to 1

def gcd(m,n):
    i = min(m,n)
    while i > 0:
        if m%i == 0 and n%i == 0:
            return i
        else:
            i = i-1
print(gcd(8,12))
print(gcd(18,25))

'''
Outputs
4
1
'''

Euclid’s Algorithm

  1. Suppose d divides both m and n, and m>n
  1. Then m = a*d, n = b*d
  1. So m-n = ad - bd = (a-b)*d
  1. d divides m-n as well
  1. So gcd(m,n) = gcd(n,m-n)

Consider gcd(m,n) with m>n
If n divides m, return n
Otherwise, compute gcd(n, m-n) and return that value

def gcd(m,n):
		# Assume m>=n [comment]
    if m<n:
        (m,n)=(n,m)
    if m%n == 0:
        return n
    else:
        diff = m-n
        return(gcd(max(n,diff),min(n,diff))) # Recursion
print(gcd(8,12))
print(gcd(18,25))

'''
Outputs
4
1
'''

Euclid’s Algorithm using while condition

def gcd(m,n):
    if m<n:
        (m,n)=(n,m)
    while m%n != 0:
        diff = m-n
        (m,n) = (max(n,diff),min(n,diff))
    return n
print(gcd(8,12))
print(gcd(18,25))

'''
Outputs
4
1
'''

Euclid’s Extended Version

Consider gcd(m,n) with m>n
If n divides m, return n
Otherwise, let r = m%n
Return gcd(n,r)
def gcd(m,n):
    if m<n:
        (m,n)=(n,m)
    if m%n == 0:
        return n
    else:
        return gcd(n,m%n)  # m%n <n, always
print(gcd(8,12))
print(gcd(18,25))

'''
Outputs
4
1
'''

While Condition

while condition:
	step 1
	step 2
	step 3

  1. Don't know in advance how many times we will repeat the steps
  1. Should be careful to ensure the loop terminates, eventually the condition should become false

Python and its Environment

Compiler

Translates high level programming language to machine level instructions, generates “executable” code.

Interpreter

Itself is a program that runs and directly “understands” high level programming language.

Python is basically an interpreted language

Load the Python interpreter

Send Python commands to the interpreter to be executed

Easy to interactively explore language features

Can load complex programs from files

>>>from filename import *

Running a Python program

Resources

Typical Python Program

  1. Interpreter executes statements from top to bottom
  1. Function definitions are “digested” for future use
def function01(x,y):
		return x*y
def function02(m,n):
		return m-n
def function03(a,b):
		return a+b

statement_01
statement_02
	:
	:
	:
statement_n
  1. Actual program starts from statement_01
  1. Python allows free mixing of function definitions and statements

Basics of Python

Assignment Statement

i = 5
j = 2*i
j = j + 5 # statement that updates a value

Names, values and types

i = 5 # i is int
i = 7*1 # i is still int
j = i/3 # j is float, creates float
...
i = 2*j # i is now float
i = 5
j = 5.55
print(type(i))
print(type(j))

'''
Outputs
<class 'int'>
<class 'float'>
'''

Numeric Values

Numbers come in two flavors:

  1. int - integers

    Ex: 178, -87, 0, 76

  1. float - fractional numbers

    Ex: 0.1, 3.14, -0.01

Operations on Numbers

Boolean Values: bool

x = 5
y = 5
z = 8
print(x == y)
print(x == z)
print(x != z)

'''
Outputs
True
False
True
'''

  • Combine using Logical Operators
    • n >0 and m%n == 0:

x = 5
y = 5
z = 8
print(x == y and x == z)
print(x == y or x == z)
print(x == y and x != z)
print(x == y or x != z)

'''
Outputs
False
True
True
True
'''
def divides(m, n):
    if m%n == 0:
        return True
    else:
        return False

def even(n):
    return divides(n,2)

def odd(n):
    return not divides(n,2)

print(divides(12,4))
print(divides(12,5))
print(even(4))
print(even(5))
print(odd(4))
print(odd(5))

'''
Outputs
True
False
True
False
False
True
'''

Strings

print("Bharath's")
print('Bharath\'s')
print("'Bharath'")
name = '''Bharath is a "gangster's brother"'''
print(name)

'''
Outputs
Bharath's
Bharath's
'Bharath'
Bharath is a "gangster's brother"
'''
name = "bharath's"
for i in range (0,len(name)):
    print(i, name[i])
print('\n')
for i in range (-len(name), 0):
    print(i, name[i])
    
'''
Outputs
0 b
1 h
2 a
3 r
4 a
5 t
6 h
7 '
8 s


-9 b
-8 h
-7 a
-6 r
-5 a
-4 t
-3 h
-2 '
-1 s
'''

Operations on Strings

print('Bharath'+'is my name')
print('Bharath '+'is my name')
print('Bharath','is my name')
print(len("bharath"))

'''
Outputs
Bharathis my name
Bharath is my name
Bharath is my name
7
'''

Extracting a Substring

s = "hello"
print(s[1:4])
print(s[1:5])
print(s[2:])
print(s[:4])
print(s[-5:])
print(s[-4:-1])
print(s[:-1])

'''
Outputs
ell
ello
llo
hell
hello
ell
hell
'''

Modifying Strings

s = s[:3]+"p!"
print(s)

'''
Output
help!
'''

Lists

s = "hello"
print(s[0]) # returns a string itself
print(s[0:1]) # returns a string itself

'''
h
h
'''
mixed = [1, 'bharath', 2, "sharath"]
for i in range(0, len(mixed)):
    print(mixed[i])
print(mixed[1:])
print(mixed[:])

'''
Outputs
1
bharath
2
sharath
['bharath', 2, 'sharath']
[1, 'bharath', 2, 'sharath']
'''

Nested List

mixed.append([2,3])
print(mixed)
mixed.insert(2, ['hrushik', 'ramya']) # list.insert(index, element)
print(mixed)

'''
Outputs
[1, 'bharath', 2, 'sharath', [2, 3]]
[1, 'bharath', ['hrushik', 'ramya'], 2, 'sharath', [2, 3]]
'''

for i in range(0, len(mixed)):
    print(mixed[i])

'''
Outputs
1
bharath
['hrushik', 'ramya']
2
sharath
[2, 3]
'''

print(mixed[2][0])
print(mixed[2][0][1])
print(mixed[2][0][3:])

'''
Outputs
hrushik
r
shik
'''

mixed[2][0] = 'hrasika'
print(mixed)
mixed[2] = ['bengaluru', 'chennai', 'mumbai']
print(mixed)

'''
Outputs
[1, 'bharath', ['hrasika', 'ramya'], 2, 'sharath', [2, 3]]
[1, 'bharath', ['bengaluru', 'chennai', 'mumbai'], 2, 'sharath', [2, 3]]
'''

sample = [0, 1, 2, 3]
sample[1] = 2
print(sample)

'''
Outputs
[0, 2, 2, 3]
'''

List Operations

sample = [3, 1, 4, 1, 5, 9]
sample.append(2)
sample.extend([6, 5])
sample.remove(3) # removes the first occurence in the list
sample.pop()
sample.sort()
sample.reverse()
sample[2] = 'n'
sample.extend([3,4,5]) # list must always be passed in case of extend method
print(sample)

'''
Outputs
sample:			             [3, 1, 4, 1, 5, 9]
sample.append(2):	       [3, 1, 4, 1, 5, 9, 2]
sample.extend([6, 5]):	 [3, 1, 4, 1, 5, 9, 2, 6, 5]
sample.remove(3):	       [1, 4, 1, 5, 9, 2, 6, 5]
sample.pop():		         [1, 4, 1, 5, 9, 2, 6]
sample.sort():		       [1, 1, 2, 4, 5, 6, 9]
sample.reverse():	       [9, 6, 5, 4, 2, 1, 1]
sample[2] = 'n'		       [9, 6, 'n', 4, 2, 1, 1]
sample.extend([3,4,5])   [9, 6, 'n', 4, 2, 1, 1, 3, 4, 5]
'''
if 'n' in sample:
    sample.remove('n')
print(sample)

'''
Output
[9, 6, 4, 2, 1, 1, 3, 4, 5]
'''
while 4 in sample:
    sample.remove(4)
print(sample)

'''
Output
[9, 6, 2, 1, 1, 3, 5]
'''
sample.sort()
print(sample)
print(sample.index(5))

'''
Outputs
[1, 1, 2, 3, 5, 6, 9]
4
'''

x = 5
y = x
x = 7
print("x: ",x)
print("y: ",y)

'''
Outputs
x:  7
y:  5
'''
list1 = [1,2,3,4]
list2 = list1
list1[2] = 'n'
print(list1)
print(list2)

'''
Outputs
[1, 2, 'n', 4]
[1, 2, 'n', 4]
'''

Copying list

l[:k] is l[0:k], l[k:] is l[k:len(l)]
l[:] == l[0:len(l)]
lista = [5, 6, 7, 8, 9]
listb = lista[:]
lista[2] = 'm'
print(lista)
print(listb)

'''
Outputs
[5, 6, 'm', 8, 9]
[5, 6, 7, 8, 9]
'''
list1 = [1,3,5,7]
list2 = list1
list1 = list1[0:2] + [7] + list1[3:]
print(list1, list2)

'''
Output
[1, 3, 7, 7] [1, 3, 5, 7]
'''

Digression on equality

lista = [5, 6, 7, 8, 9]
listb = lista
listc = listb
listc[2] = 'n'
print(lista,listb, listc)

'''
Outputs
[5, 6, 'n', 8, 9] [5, 6, 'n', 8, 9] [5, 6, 'n', 8, 9]
'''
lista = [5, 6, 7, 8, 9]
listb = [5, 6, 7, 8, 9]
listc = listb
print(lista == listb)
print(lista == listc)
print(listc == listb)
print(lista is listb)
print(lista is listc)
print(listc is listb)
listc[2] = 'n'
print(lista,listb, listc)

'''
Outputs
True
True
True
False
False
True
[5, 6, 7, 8, 9] [5, 6, 'n', 8, 9] [5, 6, 'n', 8, 9]
'''

Concatenation of Lists

listd = lista + listb
listd + [8]
print(listd)

'''
Outputs
[5, 6, 7, 8, 9, 5, 6, 'n', 8, 9]
'''
list1 = [1, 2, 3, 4]
list2 = list1
list1 = list1 + [9]
print(list1, list2)

'''
Outputs
[1, 2, 3, 4, 9] [1, 2, 3, 4]
'''

Tuples

(age, name, primes) = (23, "Bharath", [2,3,5])
print(age)
print(name)
print(primes)

'''
Outputs
23
Bharath
[2, 3, 5]
'''
point = (3.5, 4.8)
date = (18,4,2024)
print(point)
print(date)

'''
Outputs
(3.5, 4.8)
(18,4,2024)
'''
xCoOrdinate = point[0]
monthYear = date[1:]
print(xCoOrdinate, monthYear)
'''
Outputs
3.5 (4, 2024)
'''

Dictionaries

score = {}
score["test1"] = {}
score["test2"] = {}
score["test1"]["dhawan"] = 84
score["test2"]["dhawan"] = 27
score["test1"]["kohli"] = 100
print(score)

'''
Outputs
{'test1': {'dhawan': 84, 'kohli': 100}, 'test2': {'dhawan': 27}}
'''

Operating on dictionaries

for k in score.keys():
    print(k)
    
'''
Outputs
test1
test2
'''
score = {}
score["test2"] = {}
score["test1"] = {}
score["test2"]["dhawan"] = 84
score["test1"]["dhawan"] = 27
score["test2"]["kohli"] = 100
print(score)
for k in score.keys():
    print(k)
print("\n")
for k in sorted(score.keys()):
    print(k)
print(list(score.keys()))
    
'''
Outputs
{'test2': {'dhawan': 84, 'kohli': 100}, 'test1': {'dhawan': 27}}
test2
test1


test1
test2
['test2', 'test1']
'''
for s in score.values():
    print(s)
'''
Outputs
{'dhawan': 84, 'kohli': 100}
{'dhawan': 27}
'''

/code

Dictionary v/s lists

Execution of typical python program

def function01(x,y):
		return x*y
def function02(m,n):
		return m-n
def function03(a,b):
		return a+b

statement_01
statement_02
	:
	:
	:
statement_n

Control Flow

Conditional Execution

If Statement

  1. If statement
if m%n != 0:
	(m, n) = (n, m%n)
if condition:
	statement_01 # executes conditionally
	statement_02 # executes conditionally
statement_03   # executes unconditionally

  1. Alternative Execution
if m%n != 0:
	(m, n) = (n, m%n)
else:
	gcd = n

Shortcuts for conditions

Multiway Branching, elif

if x == 1:
    y = f1(x)
else:
    if x == 2:
        y = f2(x)
    else:
        if x == 3:
            y = f3(x)
        else:
            y = f4(x)
if x == 1:
    y = f1(x)
elif x == 2:
    y = f2(x)
elif x == 3:
    y = f3(x)
else:
    y = f4(x)

Loops: Repeated actions

for loop

list = [1, 2, 3, 4]
for i in list:
	y = y*i
	z = z+1 
def flist(n):
    flist = []
    for i in range(1, n+1):
        if n%i == 0:
            flist = flist + [i]
    return flist

print(flist(9))

'''
Outputs
[1, 3, 9]
'''

while conditional loop

while condition:
	statement_01
	...
	statement_02

Break and Continue statements

  1. break statement
def findpos(l, v):
    pos, i= -1, 0
    for x in l:
        if x == v:
            pos = i
            break
        i = i+1
    return pos
print(findpos(sample, 5))
print(findpos(sample, 9))

'''
Outputs
4
6
'''

def findpos(l, v):
    pos = -1
    for i in range(len(l)):
        if l[i] == v:
            pos = i
            break
    # If pos not in the loop, pos is -1
    return pos
# break, if v is found
# terminate loop normally - v is not found
print(findpos(sample, 5))
print(findpos(sample, 9))
print(findpos(sample, 4))

'''
Outputs
4
6
-1
'''
def findpos(l, v):
    for i in range(len(l)):
        if l[i] == v:
            pos = i
            break
    else:
        pos = -1 # No break, v not in l
    return pos

print(findpos(sample, 5))
print(findpos(sample, 9))
print(findpos(sample, 4))

'''
Outputs
4
6
-1
'''

  1. continue statement
while True:
    name = input('Who are you: ')
    if name != 'bharath':
        continue
    password = input('Hello Bharath, Whats the password: ')
    if password == 'bharath28':
        break
print('Access granted')

'''
Output
Who are you: bh
Who are you: bharath
Hello Bharath, Whats the password: bha
Who are you: bharath
Hello Bharath, Whats the password: bharath28
Access granted
'''

Function Definition

def f(a, b, c):
	statement_01
	statement_02
	...
	return (v)

Passing values to functions

def power(x, n):
    ans = 1
    for i in range(0, n):
        ans = ans*x
    return ans
print(power(3, 5))

'''
Outputs
243
'''

def update(l, i, v):
    if i >= 0 and i < len(l):
        l[i] = v
        return True
    else:
        return False

ns = [3,11,12]
z = 8
print(update(ns,2,z),ns) # ns is [3, 11, 8]
print(update(ns,4,z),ns) # z remains 8

'''
Outputs
True [3, 11, 8]
False [3, 11, 8]
'''

Scope of names in a function

def name(x):
    n = 17
    return x

n = 7
v = name(28)
print(n, v) # Name x inside the function is seperate from x outside

'''
Outputs
7 28
'''

Defining Functions

Recursive Functions

def factorial(n):
    if n<=0:
        return 1
    else:
        val = n*factorial(n-1)
        return val

print(factorial(5))

'''
Outputs
120
'''

Passing Arguments by Name

def power(x,n):
    ans = 1
    for i in range(0,n):
        ans = ans*x
    return ans
print(power(n=5, x=4)) # 4^5 (4*4*4*4*4)

'''
Outputs
1024
'''

Default Arguments

print(int("A5",16))
print(int("AB5",16))
# Output 165 (10*16^1+5)
# Output 2741 (10*16^2 + 11*16^1 + 5) → 2560+176+5

print(int('10100',2))
# Output 20 (1*2^4 + 1*2^2) → 16+4
def f(a, b, c=14, d=22):
	....

Function definitions

a=9
b=8
if a>b:
    def sub(a,b):
        res = a-b
        return res
else:
    def sub(a,b):
        res = b-a
        return res
print(sub(a,b))

a=8
b=9
if a>b:
    def sub(a,b):
        res = a-b
        return res
else:
    def sub(a,b):
        res = b-a
        return res
print(sub(a,b))


'''
Outputs
1
1
'''
def f(a,b,c):
	...
g=f 
def apply(f, x, n):
    res = x
    for i in range(n):
        res = f(res)
    return res

def square(x):
    return x*x

apply(square, 5, 2)

# Output: (5^2)^2

Examples

def primeFinder(n):
    if n == 1:
        return 'non-prime'
    else:
        factorlist = []
        for i in range(1,n+1):
            if n%i == 0:
                factorlist.append(i)
        if len(factorlist) > 2:
            return 'non-prime'
        else:
            return 'prime'
    
print(primeFinder(9))
print(primeFinder(31))
print(primeFinder(69)) 
print(primeFinder(11))
print(primeFinder(121))
print(primeFinder(1))

'''
Output
non-prime
prime
non-prime
prime
non-prime
non-prime
'''

def primeUptoNo(n):
    primelist = []
    for i in range(1,n+1):
        if primeFinder(i) == 'prime':
            primelist.append(i)
    return primelist

print(primeUptoNo(13))
print(primeUptoNo(8))
print(primeUptoNo(30))

'''
Outputs
[2, 3, 5, 7, 11, 13]
[2, 3, 5, 7]
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
'''

def nprimes(n):
    primelist = []
    count = 0
    i = 1
    while count < n:
        if primeFinder(i) == 'prime':
            count += 1
            primelist.append(i)
        i=i+1
    return primelist
                
print(nprimes(9),len(nprimes(9)))
print(nprimes(20),len(nprimes(20)))

'''
Outputs
[2, 3, 5, 7, 11, 13, 17, 19, 23] 9
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71] 20
'''

Arrays and lists

Arrays

Lists

Operations

Operating on Lists

map function

def f(x):
    return x*x

l = [2,3,6,7]

num = map(f, l)
print(list(num))

# Output [4, 9, 36, 49]
def isprime(n):
    if n < 2:  
        return False
    for i in range(2, int(n ** 0.5) + 1): 
        if n % i == 0:
            return False
    return True

numberlist = list(range(0,100))
primelist = []
for i in numberlist:
    if isprime(i):
        primelist.append(i)
print(primelist)

'''
Output
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
'''

filter function

List Comprehension

def square(x):
    return x*x
def iseven(x):
    if x%2 == 0:
        return True
    else:
        return False
x=2
value=[square(x) for i in range(100) if iseven(x)]
print(value)

# Output: [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]

Binary Search

def search(seq, v):
    for i in seq:
        if i == v:
            return True

    return False
        
listx=[2,1,6,5,7,8,9,4,5,4]
print(listx)
print(search(listx, 9))
print(search(listx, 6))
print(search(listx, 10))

'''
Output
[2, 1, 6, 5, 7, 8, 9, 4, 5, 4]
True
True
False
'''

def binarySearch(seq, value, l, r):
    seq.sort()
    if r-l == 0:
        return False
    mid = (l+r)//2
    if value == seq[mid]:
        return True
    if value < seq[mid]:
        return binarySearch(seq, value, l, mid)
    else:
        return binarySearch(seq, value, mid+1, r)

listx=[2,1,6,5,7,8,9,4,5,4]
print(listx)
print(binarySearch(listx, 9, 0, len(listx)))
print(binarySearch(listx, 6, 0, len(listx)))
print(binarySearch(listx, 10, 0, len(listx)))

'''
Outputs
[2, 1, 6, 5, 7, 8, 9, 4, 5, 4]
True
True
False
'''

Efficiency

time python3 filename.py

Sorting

Selection Sort

743289552164
21
2132
213255
21325564
2132556474
213255647489
743289552164
213289557464
213289557464
213255897464
213255748964
213255746489
def selectionSort(l):
    for start in range(len(l)):
        minpos = start
        for i in range(start, len(l)):
            if l[i] < l[minpos]:
                minpos = i
        (l[start], l[minpos]) = (l[minpos], l[start])
    return l

listx=[2,1,6,5,7,8,9,4,5,4]
print(listx)
print(selectionSort(listx))

'''
Outputs
[2, 1, 6, 5, 7, 8, 9, 4, 5, 4]
[1, 2, 4, 4, 5, 5, 6, 7, 8, 9]
'''

Analysis of Selection sort

Insertion Sort

743289552164
74
3274
327489
32557489
2132557489
213255647489
def insertionSort(l):
    for sliceEnd in range(len(l)):
        pos = sliceEnd
        while pos > 0 and l[pos] < l[pos-1]:
            (l[pos], l[pos-1]) = (l[pos-1], l[pos])
            pos = pos -1
    return l
listx=[2,1,6,5,7,8,9,4,5,4]
print(listx)
print(insertionSort(listx))

'''
Outputs
[2, 1, 6, 5, 7, 8, 9, 4, 5, 4]
[1, 2, 4, 4, 5, 5, 6, 7, 8, 9]
'''

Analysis of Insertion Sort

=O(n2)⁍ = O(n^2)

Recursive Computation

def factorial(n):
    if n == 0:
        return 1
    else:
        return(n * factorial(n-1))
    
print(factorial(5))

'''
Outputs
120
'''

Inductive Definitions for lists

Merge Sort

List

743289552164

Halved List - 01

743289

Halved List - 02

552164

Sorted - Halved List - 01

327489

Sorted - Halved List - 02

215564

Merge both the sorted list

21
2132
213255
21325564
2132556474
213255647489

Sorting

327489
215564
327489
5564
7489
5564
7489
64
7489
89

Procedure

def merge(A, B):
    (C, m,n) = ([],len(A), len(B))
    (i,j) = (0,0)
    while i+j < m+n:
        if i == m:
            C.append(B[j])
            j = j+1
        elif j == n:
            C.append(A[i])
            i = i+1
        elif A[i] <= B[j]:
            C.append(A[i])
            i = i+1
        elif A[i] > B[j]:
            C.append(B[j])
            j = j+1
    return C
a = list(range(0, 50, 2)) # Even numbers from 0 to 50
b = list(range(1, 40, 2)) # Odd numbers from 1 to 40
print(a,b)
print(merge(a,b))
print(len(merge(a,b)))

'''
Outputs
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48] [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 42, 44, 46, 48]
45
'''
def mergeSort(a, left, right):
    if right - left <= 1:
        return a[left : right]
    if right - left > 1:
        mid = (left+right)//2
        l = mergeSort(a, left, mid)
        r = mergeSort(a, mid, right)
    return merge(l,r)
a = list(range(1, 100, 2)) + list(range(0, 100, 2))
print(a)
print(mergeSort(a, 0, len(a)))

'''
Outputs
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
''''

Analysis of Merge

Analysis of Merge Sort

Merge Sort: Shortcomings

Quick Sort

Alternative approach to Merge Sort

Divide and conquer without merging

T(n)=2T(n/2)+n=O(nlogn)T(n) = 2T(n/2) + n = O(n·logn)

Quick Sort: Algorithm

4332227863579113

Red: Pivot Element

Yellow: Elements lesser than pivot element, goes to the left of pivot element

Green: Elements greater than pivot element, goes to the right of pivot element

1332224363579178
1322324357637891

Quick Sort: Partitioning

4332227863579113

| ≤43 >43

4332221363579178

| ≤43 >43

133222 4363579178

| ≤pivot | Pivot | >Pivot |

def quickSort(A, l, r):
    if r-l <= 1:
        return()
    yellow = l+1
    
    for green in range(l+1, r):
        if A[green] <=  A[l]:
            (A[yellow], A[green]) = (A[green], A[yellow])
            yellow = yellow+1
        
    (A[l], A[yellow-1]) = (A[yellow-1], A[l])
    
    quickSort(A, l, yellow-l)
    quickSort(A, yellow, r)
    return A

a = list(range(100, 1, -2)) + list(range(1,100,2))
print(a)
print(quickSort(a, 0, len(a)))

'''
Outputs
[100, 98, 96, 94, 92, 90, 88, 86, 84, 82, 80, 78, 76, 74, 72, 70, 68, 66, 64, 62, 60, 58, 56, 54, 52, 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
'''

Analysis of Quick Sort

Worst Case

T(n)=T(n1)+n=T(n2)+T(n1)+1=1+2+...+n=O(n2)T(n)=T(n-1)+n=T(n-2) + T(n-1) + 1 =1+2+...+n=O(n^2)

But …

Quicksort: Randomization

Stable Sorting

Quicksort in Practice

Exception Handling

Common Errors

Exception Handling

Types of Errors

Syntax Errors

Run Time Errors

Terminologies

Handling exceptions

Zero Division Error

def spam(divideby):
    return 42/divideby
try:
    print(spam(9))
    print(spam(6))
    print(spam(0))
except ZeroDivisionError:
    print("error: invalid argument")
    
'''
Outputs
4.666666666666667
7.0
error: invalid argument
'''

Index Error

def listPrint(l,n):
    sq = []
    for i in range(n):
        sq.append(l[i]*l[i])
    return sq

l = [1,2,3,4]    
try:
    print(listPrint(l,4))
    print(listPrint(l,5))
except IndexError:
    print("Index Error is found: ",l)

'''
Outputs
[1, 4, 9, 16]
Index Error is found:  [1, 2, 3, 4]
'''

Name Error

x = 108
try:
    print(x)
    print(y)
except NameError:
    print("Entered variable is missing (hence pritning x): ",x)
'''
Outputs
108
Entered variable is missing (hence pritning x):  108
'''
def spam(divideby):
    return 42/divideby

x = 7

try:
    print(spam(x))
    print(spam(y))
    print(spam(0))
except (NameError, ZeroDivisionError):
    print("Error found")
    
'''
Outputs
6.0
Error found
'''

def spam(divideby):
    return 42/divideby

x = 7

try:
    print(spam(x))
    #print(spam(y))
    print(spam(0))
except (NameError, ZeroDivisionError):
    print("Error found")
'''
Outputs
6.0
Error found
'''

except: Catch all exceptions

else:

def spam(divideby):
    return 42/divideby

x = 7

try:
    print(spam(x))
except:
    print("Error found")
else:
    print("done and dusted")
'''
Outputs
6.0
done and dusted
'''

Using Exception Handling in a Positive Manner

Traditional Manner

scores = {'Dhawan':[3,22], 'kohli':[200,3]}
if 'b' in scores.keys():
    scores['b'].append('s')
else:
    scores['b'] = ['s']
print(scores)

'''
Outputs
{'Dhawan': [3, 22], 'kohli': [200, 3], 'b': ['s']}
'''

Using exceptions

scores = {'Dhawan':[3,22], 'kohli':[200,3]}
try:
    scores['b'].append('s')
except KeyError:
    scores['b'] = ['s']
print(scores)

'''
Outputs
{'Dhawan': [3, 22], 'kohli': [200, 3], 'b': ['s']}
'''

Interacting with the user

Inputs

userdata = input()
print(userdata)
userdata = input("enter a number: ")
print(type(userdata))

'''
Outputs
78
78
enter a number: 78
<class 'str'>
'''
userdata = int(input("enter a number: "))
print(type(userdata))

'''
Outputs
enter a number: 78
<class 'int'>
'''
while True:
    try:
        userdata = int(input("enter a number: "))
        print(type(userdata))
    except ValueError:
        print("Not a number try again")
    else:
        print("Break")
        break
'''
Outputs
enter a number: bharath28
Not a number try again
enter a number: 28k
Not a number try again
enter a number: 28
<class 'int'>
Break
'''

Outputs

Printing on screen

a = 'Hi'
b = "bruh"
print(a,b,"lol-string")

# Output: Hi bruh lol-string /

Fine tuning print()

print("Bharath is a legend.")
print("yes, I know")
print("Bharath is a legend.",end=" ")
print("yes, I know")
print("Bharath is a legend.",end=".")
print("yes, I know")
print("Bharath is a legend.",end=" surely ")
print("yes, I know")

'''
Outputs
Bharath is a legend.
yes, I know
Bharath is a legend. yes, I know
Bharath is a legend..yes, I know
Bharath is a legend. surely yes, I know
'''
x = "is"
y = "well-known"
print("bharath",x,"a",y,"legend.")
print("bharath",x,"a ",y," legend.", sep="")

'''
Outputs
bharath is a well-known legend.
bharathisa well-known legend.
'''

Formatting Print

name = 'Bharath'
print(name.rjust(20))
print(name.ljust(20))
print(name.rjust(20,'*'))
print(name.ljust(20,'*'))
print(name.center(20,'*'))

'''
Outputs
             Bharath
Bharath             
*************Bharath
Bharath*************
******Bharath*******
'''

Dealing with Files

Disk Buffers

Reading/writing disk data

Opening a file

fh = open('profile.txt', 'r')
Key LetterFunctionDescription
rreadopens a file for reading only
wwritecreates an empty to write to
aappendappend to an existing file
contents = fh.read()
print(contents)

'''
File contains/output
Hey there,
This is Bharath Bhat
'''
content = fh.readlines()
print(content)

# Output: ['Hey there,\n', 'This is Bharath Bhat']

Reading of a file

block = fh.read(12)
print(block)

'''
Output: 
Hey there,
T

out of 
Hey there,
This is Bharath Bhat
'''

End of file:

Write to a file

fh.write(s)
s = "bharath is a legend"
fh = open('profile.txt','w')
fh.write(s)
# Returns 19

fh = open('profile.txt', 'r')
contents = fh.read()
print(contents)
# Output: bharath is a legend
s = "bharath is a legendary person \nHe lives in Bengaluru"
fh = open('profile.txt','w')
fh.writelines(s)
fh = open('profile.txt', 'r')
contents = fh.read()
print(contents)

'''
Output
bharath is a legendary person 
He lives in Bengaluru
'''

Closing a file

fh.close()

Write a Half Adder Verilog code using python file handling

code ='''
module half_adder(input a, b, output sum, carry);
    
    assign sum = a^b;
    assign carry = a&b;

endmodule
'''
adder = open('half_adder.v','w')
adder.write(code) # 113 characters are added to the file
adder.close()

adder = open('half_adder.v', 'r')
adder_content = adder.read()
fh.close()

print(adder_content)

'''
Output

module half_adder(input a, b, output sum, carry);
    
    assign sum = a^b;
    assign carry = a&b;

endmodule
'''

Processing file line by line

adder = open('half_adder.v', 'r')
adder_copy = open('full_adder.v','w')
for line in adder.readlines():
    adder_copy.write(line)
    
# Above 2 lines can be replaced with the below 2 lines
# contents = adder.readlines()
# adder_copy_01.writelines(contents)

adder.close()
adder_copy.close()

adder_copy = open('full_adder.v','r')
content = adder_copy.read()
print(content)

'''
Outputs

module half_adder(input a, b, output sum, carry);
    
    assign sum = a^b;
    assign carry = a&b;

endmodule
'''

Strip new line character

adder = open('half_adder.v', 'r')
contents = adder.readlines()
for line in contents:
    s = line[:-1]
print(s)
# Output: endmodule
for line in contents:
    s = line.rstrip()
print(s)
# Output: endmodule

String Processing

Strip Whitespaces

s.rstrip(): removes trailing (right) whitespaces

s.lshift(): removes leading (left) whitespaces

s.strip(): removes whitespaces both the leading and trailing whitespaces of the string

name = "    H Bharath Bhat      "
print(name.rstrip())
print(name.lstrip())
print(name.strip())

'''
Outputs
    H Bharath Bhat
H Bharath Bhat      
H Bharath Bhat
'''

Searching for text

s.find(value)

name.find('Bharath') # Output: 7
name.find('Bhat')    # Output: 15
name.find('Bhatt')   # Output: -1

s.find(pattern, start, end)

name.find('Bha',1,20)  # Output: 7
name.find('Bha',13,20) # Output: 15

s = 'brown fox grey dog brown fox'
print(s.find('brown'))
print(s.find('brown',5,len(s)))
print(s.find('cat'))
print(s.index('brown'))
print(s.index('brown',5,len(s)))

'''
Outputs
0
19
-1
0
19
'''

Search and Replace

s.replace(fromstr, tostr)

s.replace(fromstr, tostr, n)

s.replace('brown','black')   # Output: black fox grey dog black fox
s.replace('brown','black',1) # Output: black fox grey dog brown fox

Splitting a string

columns = s.split(' ') # Output: ['brown', 'fox', 'grey', 'dog', 'brown', 'fox']
columns = s.split(' ',2) # Output: ['brown', 'fox', 'grey dog brown fox']
csvline = '6,7,8'
print(csvline.split(','))   # Output: ['6', '7', '8']
print(csvline.split(',',1)) # Output: ['6', '7,8']
csvline = '6#?7#?8'
print(csvline.split('#?'))  # Output: ['6', '7', '8']

Joining Strings

# Example 01
csvline = ','.join(columns) # Output: brown,fox,grey,dog,brown,fox

# Example 02
date = '24'
month = 'may'
year = '2002'

birthdate = '-'.join([date, month, year])
print(birthdate)
# Output: 24-may-2002

Converting Case

name = 'bharath Bhat'
print(name.capitalize()) # Output: Bharath bhat
print(name.lower())      # Output: bharath bhat
print(name.upper())      # Output: BHARATH BHAT
print(name.title())      # Output: Bharath Bhat
print(name.swapcase())   # Output: BHARATH bHAT

Resizing Strings

print(name.center(50)) #                    bharath Bhat                   |
print(name.center(50,'-')) # -------------------bharath Bhat-------------------
print(name.rjust(40,'→')) # →→→→→→→→→→→→→→→→→→→→→→→→→→→→bharath Bhat
print(name.ljust(40,'←')) # bharath Bhat←←←←←←←←←←←←←←←←←←←←←←←←←←←←

Other Functions

name = 'Bharath Bhat'
password = 'bharath28'

print(name.islower()) # False
print(name.isupper()) # False

print(name.isalpha()) # False because of space
print(password.isalnum()) # True
namee = 'BharathBhat'
print(namee.isalpha()) # True
number = '7892104186' # it must be a string only
print(number.isdecimal()) # True
print(name.istitle()) # True

String format() method

Example

print('First: {0}, second: {1}'.format(47,11))
print('second: {1}, First: {0}'.format(47,11))

'''
Outputs
First: 47, second: 11
second: 11, First: 47
'''
print('one: {f}, two: {s}'.format(f=47,s=11))
print('one: {f}, two: {s}'.format(s=47,f=11))

'''
Outputs
one: 47, two: 11
one: 11, two: 47
'''

Formatting

print('value:{0:3d}'.format(4)) # value:  4
print('value:{0:6.2f}'.format(47.523)) # value: 47.52

Doing Nothing in Python

while True:
    try:
        userdata = int(input("enter a number: "))
        print(type(userdata))
    except ValueError:
        print("Not a number try again")
    else:
        print("Break")
        break
'''
Outputs
enter a number: bharath28
Not a number try again
enter a number: 28k
Not a number try again
enter a number: 28
<class 'int'>
Break
'''
while True:
    try:
        userdata = int(input("enter a number: "))
        print(type(userdata))
    except ValueError:
        pass
    else:
        print("Break")
        break
'''
Outputs
enter a number: bh4
enter a number: gh
enter a number: 45
<class 'int'>
Break
'''

The value None

Removing a list entry

l = [1,4,3,4,5,6]
del(l[4])
# l = [1, 4, 3, 4, 6]

Global Variables

Scope of a Name

def f():
    y = x
    print(y)

x = 7
f()

# Output: 7
def f():
    y = x
    print(y)
    x = 22

x = 7
f()

---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
Cell In[2], line 7
      4     x = 22
      6 x = 7
----> 7 f()

Cell In[2], line 2, in f()
      1 def f():
----> 2     y = x
      3     print(y)
      4     x = 22

UnboundLocalError: local variable 'x' referenced before assignment

Global Variables

def f():
    y = x[0]
    print(y)
    x[0] = 22

x = [7]
f()

# Output: 7

Nest Function Definitions

def f():
    def g(a):
        return a+1
    
    def h(b):
        return 2*b
    
    global x
    y = g(x) + h(x)
    print(y)
    x = 22

x = 7
f()

# Output: 22

Data Structures

Sets in python

colours = {'red','black','green','red'}
print(colours)
# Output: {'red', 'black', 'green'}
colours = set()
'black' in colours # True
numbers = set([1,2,4,5,1,6,7]) # {1, 2, 4, 5, 6, 7}
print(numbers)
letters = set('banana') # {'b', 'a', 'n'}
print(letters)

Set operations

odd = set([1,3,5,7,9,11])
prime = set([2,3,5,7,11])

# Union
print(odd | prime) # {1, 2, 3, 5, 7, 9, 11}

# Intersection
print(odd & prime) # {11, 3, 5, 7}

# Set difference
print(odd - prime) # {1, 9}
print(prime - odd) # {2}

# Exclusive or
print(odd ^ prime) # {1, 2, 9}

Stacks

Queues