We always care about how fast an algorithm runs and try to come up with clever ways of having faster algorithms.

We use a notation called big **O** notation to analyze how fast an algorithm runs, and how it scales when its input grows. We often focus on the worst case sceanrio. I.e. what happens if you have an input that makes the algorithm run as slow as possible? (e.g. we saw this in binary search when we analyzed the maximum number of steps it takes us to guess a number)

In [ ]:

```
#assigning a variable
L=range(1000)
L[0]=5 #doesn't matter how big L is
L[10]=10
```

In [28]:

```
#e.g. for loops
x=[1,5,3,2,30,30,22]
min_val=x[0]
for i in x:
if i <min_val: #1, 1
min_val=i
print min_val
```

In [6]:

```
#2 for loops
#e.g. compare two lists L1 and L2 in two for loops
L1=[3,5,6,7,9,10,8] #7
L2=[9,7,6,30,20,10,15] #7
intersection=[]
for i in L1:
for j in L2:
if i==j:
intersection += [i]
print(intersection)
```

In [8]:

```
#3 for loops
L1=[3,5,6,7,9,10,8]
L2=[9,7,6,20,11,7,15]
L3=[6,8,10,20,9,20,40]
intersection =[]
for i in L2:
for j in L2:
for k in L3:
if i==j==k:
intersection +=[i]
print(intersection)
```

In [21]:

```
#e.g. binary search
#Searching through 1 billion numbers (e.g. number of Facebook users)
from math import log
log(1e9,2)
```

Out[21]:

e.g. merge sort

In [18]:

```
1e9*log(1e9,2)
```

Out[18]:

selection sort (n${^2}$)

In [25]:

```
1e9**2
```

Out[25]: