h5

Notes on correct solutions to the assignment...
See also videos in the CS 151 playlist

** factorial.py **
n! = n * (n-1) * (n-2) * ... * 2 * 1
4! = 4 * 3 * 2 * 1
* Loop or recursion
*
total = 1
for i in range(1, 4+1):
  #print(i)
  total = total * i
print(total)
* Who cares?  4! is the number of ways to arrange 4 items.  How many different ways to have abcd in some order: abcd, abdc, acbd, ...
* binomial(5, 2) is pronounced "5 choose 2" - how many different ways to pick 2 items out of 5?  How many different ways to pick two letters from abcde?  ab, ac, ...
* binomial(5, 2) = 5! / (2! * (5-2)!)
* How many different ways to pick 5 starters from a team of 10?  binomial(10, 5)


** cards.py **
card is a number from 0 to 51
0: Ace of hearts
1: Ace of diamonds
2: Ace of spades
3: Ace of clubs
4: 2 of hearts
5: 2 of diamonds
6: 2 of spades
7: 2 of clubs
8: 3 of hearts
9: 3 of diamonds
10: 3 of spades
11: 3 of clubs
12: 4 of hearts
13:
14:
15:
16: 5
...
51: King of clubs
suit is card % 4 - hearts, diamonds, spades, clubs
                     0        1        2      3
value is card // 4 - Ace, 2, 3, 4, 5, 6, 7, 8, 9, 10,  Jack, Queen, King
                      0   1  2  3  4  5  6  7  8   9    10    11     12
Examples
  if card is 13 - 13 = 3 * 4 + 1
                - card%4 is 1 so diamond
                - card//4 is 3 so 4
		- card is 4 of diamonds
  if card is 26 - 26 = 6 * 4 + 2 - remainder of 2, divided and got 6
                - card%4 is 2 so spade
                - card//4 is 6 so 7
		- card is 7 of spades
  if card is 8  - card%4 is 0 so hearts
                - card//4 is 2 so 3
		- card is a 3 of hearts
hand = [13, 8, 26, 10] - 4 of diamonds, 3 of hearts, 7 of spades, 3 of spades

flush... check if all cards are the same suit

print(flush([0, 4, 12])) # Ace of hearts, 2 of hearts, 4 of hearts
idea/algorithm for checking for flush - check 1st and 2nd, 2nd and 3rd, 3rd and 4th, ...
* suit of 0, suit of 4
   heart   ,   heart   - same!
* suit of 4, suit of 12
   heart   ,   heart   - same!
   
flush([0, 4, 9])
* suit of 0, suit of 4
   heart   ,   heart - same!
* suit of 4, suit of 9
   heart   , diamond - not same X
* answer should be False

for i in range(1, len(hand)):
  if card[i]%4 != card[i-1]:
    return False
# if made here, never returned False, so all cards are the same suit
return True

pairs... check if at least two with the same number

2 of diamonds, K of hearts, 6 of hearts, 2 of spades
    5,             48,          20,         6
sorted - 
2 of diamonds, 2 of spades, 6 of hearts, K of hearts
    5               6          20          48
is 2 of diamonds same value as 2 of spades?  yes - return True


Ace of clubs, 2 of hearts,  7 of diamonds
    3,           4,           25 (6 remainder 1)
is ace of clubs same value as 2 of hearts? no
is 2 of hearts same value as 7 of diamonds? no
return False

Note - need to sort the cards first.




** numberFun.py **
- See video explanation

Who cares?
* The sieve is actually very efficient, and you can list out the primes up to large numbers this way.
* Much faster than doing a prime test for every i from 1 to n.  First algorithm of listing primes by checking if each is prime - too slow.
* Moral of the story - designing algorithms - come with something that is correct, if it's to slow make it better/faster.
  
n = 50 # example, for what isPrime would look like at the end
       # note - leaving commas out to make it look nicer
       # abbreviate True as T, list indexes
isPrime = [
 F  F  T  T  F  T  F  T  F  F
 0  1  2  3  4  5  6  7  8  9 

 F  T  F  T  F  F  F  T  F  T
10 11 12 13 14 15 16 17 18 19 

 F  F  F  T  F  F  F  F  F  T
20 21 22 23 24 25 26 27 28 29 

 F  T  F  F  F  F  F  T  F  F
30 31 32 33 34 35 36 37 38 39 

 F  T  F  T  F  F  F  T  F  F
40 41 42 43 44 45 46 47 48 49 

 F
50
]  
Note: it is enough to cross out for p's with p**2 <= n, so
p <= sqrt(50) = 7.071067811865475 

Running time:
# of p's that we checked: <= 7, <= sqrt(n)
# of times crossed out: 54 (??), maybe around n??
  <= n/2 + n/3 + n/5 + n/7 + … + n/(sqrt(n))
  <= n/2 + n/3 + n/4 + n/5 + n/6 + n/7 + … + n/(sqrt(n))
   = n * (1/2 + 1/3 + 1/4 + 1/5 + … + 1/sqrt(n))
    
   that is <= n * sqrt(n)
   actually roughly n * ln(n), because the harmonic series
   note that ln(n) grows very slowly, so really the # of time's crossing is out is <= constant * n, maybe 20 * n

   note that the formula n/2 + n/3 + n/5 + … actually is even better, it's roughly n * ln(ln(n))




** birthdays.py **

How many numbers 1-10 until we get a repeat?

9, 6, 5, 1, 7, 10, 3, 8, 3.  That took 9 times.
2, 5, 4, 8, 4.  That took 5 times.
5, 2, 8, 8.  That took 4 times.
7, 8, 3, 4, 8.  That took 5 times.

How about numbers 1-50?
33, 24, 2, 50, 17, 21, 11, 7, 50.  That took 9 times.
16, 44, 19, 14, 19.  That took 5 times.

How about birthdays - numbers from 1-365?

b = [7, 8, 3, 4, 8]
# are there duplicates
b.sort() # then b = [3, 4, 7, 8, 8]
Check b[1] versus b[0], b[2] versus b[1], b[3] versus b[2]
	• If any are the same, we have a duplicate
	• If none are the same, we do not.
	
We ran it maybe 50 times, and had answers between 6 and 54, 
average somewhere in the 20s

Nice explanation for what we should expect.
Suppose we have 50 people.  Call this K.  Call N = 365
What is the chance that none of them have the same birthday?
Each pair of the 50 people could have the same birthday.
How many different pairs of the 50 people are there?
How many ways to choose 2 people out of 50?
50 choose 2, aka binom(50, 2) = 50! / (48! * 2!) = 50*49 / 2 = 1,225, roughly K**2 / 2
For a particular pair, what is the chance they have the same birthday?
1/365
What is the expected number of pairs of people with the same birthday?
It turns out: 1225 * (1/365) = 3.3562 
In general, it is roughly K**2 / 2 / 365
K = 20, 20*19 / 2 / 365 = 0.5205 
K = 15, 15 * 14 / 2 / 365 = 0.2877 

Expected # of pairs that are the same is roughly 
K**2 / 2 / N, so once K gets about sqrt(2N), then there's a pretty good chance

Why do we care?  It's a math problem, and we can run simulations.  
The simulation should give a similar answer as the math.