├── preview ├── cantor set.png ├── hilbert curve.png ├── koch snowflake.png ├── jerusalem cross.png ├── pythagorean tree.png ├── sierpiński carpet.png ├── sierpiński triangle.png └── recursion-and-dynamic-programming-profile.png ├── edit distance recursion.py ├── edit distance recursion.jl ├── hanoi tower.py ├── fibonacci with memoization.py ├── hanoi tower.jl ├── factorization.py ├── coin change recursion.py ├── sierpiński triangle.py ├── cantor set.jl ├── coin change recursion.jl ├── cantor set.py ├── factorization.jl ├── sierpiński triangle.jl ├── fibonacci with memoization.jl ├── coin change dynamic programming.py ├── egg drop dynamic programming.py ├── egg drop dynamic programming.jl ├── palindrome checker 4 methods.py ├── stock trading dynamic programming.py ├── coin change dynamic programming.jl ├── stock trading dynamic programming.jl ├── sierpiński carpet.py ├── stock trading recursion.py ├── knapsack multiple choice.py ├── stock trading recursion.jl ├── knapsack.py ├── edit distance dynamic programming.py ├── sierpiński carpet.jl ├── hilbert curve.py ├── pascal triangle with memoization.py ├── knapsack.jl ├── palindrome checker 4 methods.jl ├── edit distance dynamic programming.jl ├── pascal triangle with memoization.jl ├── hilbert curve.jl ├── knapsack multiple choice.jl ├── jerusalem cross.jl ├── jerusalem cross.py ├── koch snowflake.jl ├── koch snowflake.py ├── pythagorean tree.py ├── pythagorean tree.jl ├── README.md └── LICENSE /preview/cantor set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/je-suis-tm/recursion-and-dynamic-programming/HEAD/preview/cantor set.png -------------------------------------------------------------------------------- /preview/hilbert curve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/je-suis-tm/recursion-and-dynamic-programming/HEAD/preview/hilbert curve.png -------------------------------------------------------------------------------- /preview/koch snowflake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/je-suis-tm/recursion-and-dynamic-programming/HEAD/preview/koch snowflake.png -------------------------------------------------------------------------------- /preview/jerusalem cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/je-suis-tm/recursion-and-dynamic-programming/HEAD/preview/jerusalem cross.png -------------------------------------------------------------------------------- /preview/pythagorean tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/je-suis-tm/recursion-and-dynamic-programming/HEAD/preview/pythagorean tree.png -------------------------------------------------------------------------------- /preview/sierpiński carpet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/je-suis-tm/recursion-and-dynamic-programming/HEAD/preview/sierpiński carpet.png -------------------------------------------------------------------------------- /preview/sierpiński triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/je-suis-tm/recursion-and-dynamic-programming/HEAD/preview/sierpiński triangle.png -------------------------------------------------------------------------------- /preview/recursion-and-dynamic-programming-profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/je-suis-tm/recursion-and-dynamic-programming/HEAD/preview/recursion-and-dynamic-programming-profile.png -------------------------------------------------------------------------------- /edit distance recursion.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Mar 19 15:22:38 2018 4 | 5 | @author: Administrator 6 | """ 7 | 8 | #explanation can be found in dynamic programming version 9 | # https://github.com/je-suis-tm/recursion/blob/master/edit%20distance%20dynamic%20programming.py 10 | #the only problem with recursion is that it is so freaking slow 11 | #recursion is so inefficient in python 12 | def f(a,b): 13 | 14 | if a=='' or b=='': 15 | return max(len(a),len(b)) 16 | 17 | temp=0 if a[-1]==b[-1] else 1 18 | steps=min(f(a[:-1],b)+1,f(a,b[:-1])+1,f(a[:-1],b[:-1])+temp) 19 | return steps 20 | 21 | print(f('arsehole','asshoe')) 22 | -------------------------------------------------------------------------------- /edit distance recursion.jl: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | #explanation can be found in dynamic programming version 8 | # https://github.com/je-suis-tm/recursion/blob/master/edit%20distance%20dynamic%20programming.jl 9 | #the only problem with recursion is that it is so freaking slow 10 | #recursion is so inefficient in any programming language 11 | #although it looks much more elegant than dynamic programming 12 | 13 | 14 | # In[2]: 15 | 16 | 17 | function edit_distance(text1,text2) 18 | 19 | if isempty(text1) || isempty(text2) 20 | 21 | return max(length(text1),length(text2)) 22 | 23 | end 24 | 25 | #we are comparing characters here 26 | #to get string, we should do end:end 27 | if text1[end]==text2[end] 28 | 29 | replacement=0 30 | 31 | else 32 | 33 | replacement=1 34 | 35 | end 36 | 37 | steps=min(edit_distance(text1[1:end-1],text2)+1, 38 | edit_distance(text1,text2[1:end-1])+1, 39 | edit_distance(text1[1:end-1],text2[1:end-1])+replacement) 40 | 41 | return steps 42 | 43 | end 44 | 45 | 46 | # In[3]: 47 | 48 | 49 | println(edit_distance("arsehole","asshoe")) 50 | 51 | -------------------------------------------------------------------------------- /hanoi tower.py: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | #hanoi tower is the classic recursion 8 | #the logic behind it is amazing 9 | #rules can be seen from this website: 10 | # https://en.wikipedia.org/wiki/Tower_of_Hanoi 11 | 12 | 13 | # In[2]: 14 | 15 | 16 | def hanoi(n,a,b,c): 17 | 18 | #rule states that each time we can only move one element 19 | #so when the recursion reaches to base case 1 20 | #we print the movement of elements from a to c 21 | if n==1: 22 | print(a,'-',c) 23 | return 24 | 25 | #for the general case 26 | #the first step is to move everything above the base case from column a to column b 27 | #note that we set print a to c when n reaches one 28 | #so in this case we reorder the function, replace c with column b where elements actually move towards 29 | hanoi(n-1,a,c,b) 30 | 31 | #the second step is to move the base case from column a to column c 32 | #we are only moving base case, thats why n=1 33 | hanoi(1,a,b,c) 34 | 35 | #final step would be move everything above base case from column b to column c 36 | hanoi(n-1,b,a,c) 37 | 38 | 39 | # In[3]: 40 | 41 | 42 | hanoi(4,'a','b','c') 43 | 44 | 45 | # the best explanation should be 46 | # https://www.python-course.eu/towers_of_hanoi.php 47 | 48 | -------------------------------------------------------------------------------- /fibonacci with memoization.py: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | #this is a test on how recursion with memory reduces time complexity 5 | 6 | #i need a global dictionary to do the memorization 7 | #or i can change function fib(n) into fib(n,mem) 8 | global mem 9 | mem={1:1,2:1} 10 | import datetime as dt 11 | 12 | #fib(n) is recursion with memory 13 | #everytime we do the calculation, we store it in the dictionary 14 | #i denote the key as the n th fibonacci number 15 | #the value as the number itself 16 | #if we can find the key in dictionary 17 | #we simply return the value 18 | #if not, we append the dictionary then return the value 19 | def fib(n): 20 | if n in mem: 21 | return mem[n] 22 | 23 | 24 | else: 25 | mem[n]=(fib(n-1)+fib(n-2)) 26 | return mem[n] 27 | 28 | #this is the fibonacci recursion function without memory 29 | #it is basically algorithm 101 for any coding language 30 | def f(n): 31 | if n==1: 32 | return 1 33 | elif n==2: 34 | return 1 35 | else: 36 | return f(n-1)+f(n-2) 37 | 38 | #i calculate how long these two functions take 39 | #print out the comparison 40 | def compare(n): 41 | t1=dt.datetime.now() 42 | f(n) 43 | t2=dt.datetime.now() 44 | print('recursion: ',t2-t1) 45 | 46 | t1=dt.datetime.now() 47 | fib(n) 48 | t2=dt.datetime.now() 49 | print('recursion with memory: ',t2-t1) 50 | 51 | 52 | compare(20) 53 | -------------------------------------------------------------------------------- /hanoi tower.jl: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | #hanoi tower is the classic recursion 8 | #the logic behind it is amazing 9 | #rules can be seen from this website: 10 | # https://en.wikipedia.org/wiki/Tower_of_Hanoi 11 | 12 | 13 | # In[2]: 14 | 15 | 16 | function hanoi(n,column1,column2,column3) 17 | 18 | #rule states that each time we can only move one element 19 | #so when the recursion reaches to base case 1 20 | #we print the movement of elements from column 1 to column 3 21 | if n==1 22 | println(column1," -> ",column3) 23 | return 24 | end 25 | 26 | #for the general case 27 | #the first step is to move everything above the base case from column 1 to column 2 28 | #note that we set print 1 to 3 when n reaches one 29 | #so in this case we reorder the function, replace column 3 with column 2 30 | #where elements actually move towards 31 | #the reorder is purely for printing 32 | hanoi(n-1,column1,column3,column2) 33 | 34 | #the second step is to move the base case from column 1 to column 3 35 | #we are only moving base case, thats why n=1 36 | hanoi(1,column1,column2,column3) 37 | 38 | #final step would be move everything above base case from column 2 to column 3 39 | hanoi(n-1,column2,column1,column3) 40 | 41 | end 42 | 43 | 44 | # In[3]: 45 | 46 | 47 | #the best explanation should be 48 | # https://www.python-course.eu/towers_of_hanoi.php 49 | 50 | 51 | # In[4]: 52 | 53 | 54 | hanoi(4,"Column A","Column B","Column C") 55 | 56 | -------------------------------------------------------------------------------- /factorization.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | #global list is the key 4 | #otherwise we will lose the list throughout iterations 5 | global factors 6 | factors=[] 7 | 8 | def f(n): 9 | 10 | global factors 11 | 12 | #negative and float should be excluded 13 | assert n>=0 and type(n)!=float,"negative or float is not allowed" 14 | 15 | #if n is smaller than 4 16 | #prime number it is 17 | if n>4: 18 | 19 | #exclude 1 and itself 20 | #the largest factor of n can not exceed the half of n 21 | #because 2 is the smallest factor 22 | #the range of factors we are gonna try starts from 2 to int(n/2+1) 23 | #int function to solve the odd number problem 24 | for i in range(2,int(n/2)+1): 25 | 26 | #the factorization process 27 | if n%i==0: 28 | factors.append(i) 29 | 30 | #return is crucial 31 | #if the number is not a prime number 32 | #it will stop function from appending non-prime factors 33 | #the next few lines will be ignored 34 | return f(int(n/i)) 35 | 36 | #append the last factor 37 | #it could be n itself if n is a prime number 38 | #in that case there is only one element in the list 39 | #or it could be the last indivisible factor of n which is also a prime number 40 | factors.append(int(n)) 41 | 42 | if len(factors)==1: 43 | print('%d is a prime number'%(n)) 44 | factors=[] 45 | 46 | f(100000097) 47 | print(factors) 48 | -------------------------------------------------------------------------------- /coin change recursion.py: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | #coin change is a straight forward dynamic programming problem 8 | #you are given one note and a set of coins of different values 9 | #assuming you have infinite supply of coins of different values 10 | #we need to compute different ways of making change 11 | #the order of the coins doesnt matter in this case 12 | #more details can be found in the following link 13 | # https://www.geeksforgeeks.org/coin-change-dp-7/ 14 | #the script can also be done in dynamic programming 15 | # https://github.com/je-suis-tm/recursion-and-dynamic-programming/blob/master/coin%20change%20dynamic%20programming.py 16 | 17 | 18 | # In[2]: 19 | 20 | 21 | #to solve this problem via recursion 22 | #we divide one big problem into two sub problems 23 | #the case where coin of η value is excluded in the solutions 24 | #and the case where at least one coin of η value is included 25 | def coin_change(num,choice): 26 | 27 | #base case 28 | if num==0: 29 | 30 | return 1 31 | 32 | #prevent stack overflow 33 | if num<0: 34 | 35 | return 0 36 | 37 | output=0 38 | 39 | #iterate through cases of exclusion 40 | for i in range(len(choice)): 41 | 42 | #case of inclusion 43 | include=coin_change(num-choice[i],choice) 44 | exclude=0 45 | 46 | #case of exclusion 47 | if i>=1: 48 | 49 | exclude=coin_change(num,choice[:i]) 50 | 51 | #two sub problems merge into a big one 52 | output=include+exclude 53 | 54 | return output 55 | 56 | 57 | # In[3]: 58 | 59 | 60 | coin_change(4,[1,2,3]) 61 | 62 | -------------------------------------------------------------------------------- /sierpiński triangle.py: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | #fractal is one of the interesting topics in geometry 7 | #it is usually described by a recursive function 8 | #voila,here we are! 9 | import matplotlib.pyplot as plt 10 | 11 | 12 | # In[2]: 13 | 14 | 15 | def sierpiński_triangle(coordinates,lvl,colour='k'): 16 | 17 | #stop recursion 18 | if lvl==0: 19 | return 20 | 21 | #unpack coordinates 22 | #coordinates have to follow the order of left,mid,right 23 | left=coordinates[0];mid=coordinates[1];right=coordinates[2] 24 | 25 | #compute mid point for each line 26 | left_new=((mid[0]-left[0])/2+left[0],(mid[1]-left[1])/2+left[1]) 27 | mid_new=((right[0]-left[0])/2+left[0],(right[1]-left[1])/2+left[1]) 28 | right_new=((right[0]-mid[0])/2+mid[0],(mid[1]-right[1])/2+right[1]) 29 | 30 | #create new coordinates 31 | coordinates_new=[left_new,mid_new,right_new] 32 | 33 | #viz coordinates 34 | for i in coordinates: 35 | for j in coordinates: 36 | if i!=j: 37 | plt.plot([i[0],j[0]],[i[1],j[1]],c=colour) 38 | 39 | #recursive plot sub triangles 40 | sierpiński_triangle([left,left_new,mid_new],lvl-1) 41 | sierpiński_triangle([left_new,mid,right_new],lvl-1) 42 | sierpiński_triangle([mid_new,right_new,right],lvl-1) 43 | 44 | 45 | # In[3]: 46 | 47 | 48 | sierpiński_triangle([(0,0),(0.5,1),(1,0)],4) 49 | 50 | 51 | # In[4]: 52 | 53 | 54 | #you can check turtle version at the following link 55 | # https://runestone.academy/runestone/books/published/pythonds/Recursion/pythondsSierpinskiTriangle.html 56 | 57 | #you can check print version at the following link 58 | # https://www.geeksforgeeks.org/sierpinski-triangle/ 59 | 60 | -------------------------------------------------------------------------------- /cantor set.jl: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | #fractal is one of the interesting topics in geometry 8 | #it is usually described by a recursive function 9 | #voila,here we are! 10 | using Plots 11 | 12 | 13 | # In[2]: 14 | 15 | 16 | #initialize 17 | x1=0 18 | x2=3 19 | y=0 20 | bar_height=5 21 | between_interval=10 22 | n=6; 23 | 24 | 25 | # In[3]: 26 | 27 | 28 | #create rectangle shape 29 | rectangle(w,h,x,y)=Shape(x.+[0,w,w,0],y.-[0,0,h,h]) 30 | 31 | 32 | # In[4]: 33 | 34 | 35 | #cantor set is one of the simplest fractal shape 36 | #at each iteration,we divide each bar into three equal parts 37 | #we remove the mid part from each bar and keep the rest 38 | #this effectively creates a binary tree 39 | #check the link below for more details on math 40 | # https://www.math.stonybrook.edu/~scott/Book331/Cantor_sets.html 41 | function cantor_set(x1,x2,y,n, 42 | bar_height=5,between_interval=10) 43 | 44 | #base case 45 | if n==0 46 | return 47 | 48 | else 49 | 50 | #viz the first 1/3 and the last 1/3 51 | plot!(rectangle((x2-x1)/3,bar_height,x1,y-between_interval)) 52 | plot!(rectangle((x2-x1)/3,bar_height,x2-(x2-x1)/3,y-between_interval)) 53 | 54 | #recursion 55 | cantor_set(x1,x1+(x2-x1)/3, 56 | y-between_interval, 57 | n-1) 58 | cantor_set(x2-(x2-x1)/3,x2, 59 | y-between_interval, 60 | n-1) 61 | 62 | end 63 | 64 | end 65 | 66 | 67 | # In[5]: 68 | 69 | 70 | #viz 71 | #as n increases 72 | #the bar gets too slim to be visible 73 | gr(size=(250,250)) 74 | fig=plot(legend=false,grid=false,axis=false,ticks=false) 75 | plot!(rectangle((x2-x1),bar_height,x1,y)) 76 | cantor_set(x1,x2,y,n) 77 | fig 78 | 79 | 80 | -------------------------------------------------------------------------------- /coin change recursion.jl: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | 4 | # In[1]: 5 | 6 | #coin change is a straight forward dynamic programming problem 7 | #you are given one note and a set of coins of different values 8 | #assuming you have infinite supply of coins of different values 9 | #we need to compute different ways of making change 10 | #the order of the coins doesnt matter in this case 11 | #more details can be found in the following link 12 | # https://www.geeksforgeeks.org/coin-change-dp-7/ 13 | #the script can also be done in dynamic programming 14 | # https://github.com/je-suis-tm/recursion-and-dynamic-programming/blob/master/coin%20change%20dynamic%20programming.jl 15 | 16 | 17 | # In[2]: 18 | 19 | 20 | #to solve this problem via recursion 21 | #we divide one big problem into two sub problems 22 | #the case where coin of η value is excluded in the solutions 23 | #and the case where at least one coin of η value is included 24 | function coin_change(num,choice) 25 | 26 | #base case 27 | if num==0 28 | 29 | return 1 30 | 31 | end 32 | 33 | #prevent stack overflow 34 | if num<0 35 | 36 | return 0 37 | 38 | end 39 | 40 | output=0 41 | 42 | #iterate through cases of exclusion 43 | for i in 1:length(choice) 44 | 45 | #case of inclusion 46 | include=coin_change(num-choice[i],choice) 47 | exclude=0 48 | 49 | #case of exclusion 50 | if i>=2 51 | 52 | exclude=coin_change(num,choice[1:(i-1)]) 53 | 54 | end 55 | 56 | #two sub problems merge into a big one 57 | output=include+exclude 58 | 59 | end 60 | 61 | return output 62 | 63 | end 64 | 65 | 66 | # In[3]: 67 | 68 | 69 | coin_change(10,[1,2,3]) 70 | -------------------------------------------------------------------------------- /cantor set.py: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | #fractal is one of the interesting topics in geometry 8 | #it is usually described by a recursive function 9 | #voila,here we are! 10 | import matplotlib.pyplot as plt 11 | 12 | 13 | # In[2]: 14 | 15 | 16 | #initialize 17 | x1=0 18 | x2=3 19 | y=0 20 | bar_height=5 21 | between_interval=10 22 | n=6 23 | 24 | 25 | # In[3]: 26 | 27 | 28 | #cantor set is one of the simplest fractal shape 29 | #at each iteration,we divide each bar into three equal parts 30 | #we remove the mid part from each bar and keep the rest 31 | #this effectively creates a binary tree 32 | #check the link below for more details on math 33 | # https://www.math.stonybrook.edu/~scott/Book331/Cantor_sets.html 34 | def cantor_set(x1,x2,y,n, 35 | bar_height=5,between_interval=10): 36 | 37 | #base case 38 | if n==0: 39 | return 40 | 41 | else: 42 | 43 | #viz the first 1/3 and the last 1/3 44 | plt.fill_between([x1,x1+(x2-x1)/3], 45 | [y-between_interval]*2, 46 | [y-bar_height-between_interval]*2,) 47 | plt.fill_between([x2-(x2-x1)/3,x2], 48 | [y-between_interval]*2, 49 | [y-bar_height-between_interval]*2,) 50 | 51 | #recursion 52 | cantor_set(x1,x1+(x2-x1)/3, 53 | y-between_interval, 54 | n-1) 55 | cantor_set(x2-(x2-x1)/3,x2, 56 | y-between_interval, 57 | n-1) 58 | 59 | 60 | # In[4]: 61 | 62 | 63 | #viz 64 | #as n increases 65 | #the bar gets too slim to be visible 66 | ax=plt.figure(figsize=(10,10)) 67 | plt.fill_between([x1,x2], 68 | [y]*2, 69 | [y-bar_height]*2,) 70 | cantor_set(x1,x2,y,n) 71 | plt.axis('off') 72 | plt.show() 73 | 74 | -------------------------------------------------------------------------------- /factorization.jl: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | #global list is the key 7 | #otherwise we will lose the list throughout iterations 8 | global factors=[] 9 | 10 | 11 | # In[2]: 12 | 13 | 14 | function factorization(num) 15 | 16 | #negative and float should be excluded 17 | if (num<0) || !(isinteger(num)) 18 | 19 | error("negative or float is not allowed") 20 | 21 | end 22 | 23 | #if n is smaller than 4 24 | #prime number it is 25 | if num>4 26 | 27 | #exclude 1 and itself 28 | #the largest factor of n can not exceed the half of n 29 | #because 2 is the smallest factor 30 | #the range of factors we are gonna try starts from 2 to fld(num,2)+1 31 | #int function to solve the odd number problem 32 | for i in 2:(fld(num,2)+1) 33 | 34 | #the factorization process 35 | if num%i==0 36 | 37 | push!(factors,i) 38 | 39 | #return is crucial 40 | #if the number is not a prime number 41 | #it will stop function from appending non-prime factors 42 | #the next few lines will be ignored 43 | return factorization(Int32(num/i)) 44 | 45 | end 46 | 47 | end 48 | 49 | end 50 | 51 | #append the last factor 52 | #it could be n itself if n is a prime number 53 | #in that case there is only one element in the list 54 | #or it could be the last indivisible factor of n which is also a prime number 55 | push!(factors,num) 56 | 57 | if length(factors)==1 58 | 59 | println(num," is a prime number") 60 | empty!(factors) 61 | 62 | end 63 | 64 | end 65 | 66 | 67 | # In[3]: 68 | 69 | 70 | factorization(71392643) 71 | 72 | 73 | # In[4]: 74 | 75 | 76 | print(factors) 77 | 78 | -------------------------------------------------------------------------------- /sierpiński triangle.jl: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | #fractal is one of the interesting topics in geometry 8 | #it is usually described by a recursive function 9 | #voila,here we are! 10 | using Plots 11 | 12 | 13 | # In[2]: 14 | 15 | 16 | function sierpiński_triangle(coordinates,lvl,fig,colour="black") 17 | 18 | #stop recursion 19 | if lvl==0 20 | 21 | return 22 | 23 | end 24 | 25 | #unpack coordinates 26 | #coordinates have to follow the order of left,mid,right 27 | left=coordinates[1];mid=coordinates[2];right=coordinates[3] 28 | 29 | #compute mid point for each line 30 | left_new=((mid[1]-left[1])/2+left[1],(mid[2]-left[2])/2+left[2]) 31 | mid_new=((right[1]-left[1])/2+left[1],(right[2]-left[2])/2+left[2]) 32 | right_new=((right[1]-mid[1])/2+mid[1],(mid[2]-right[2])/2+right[2]) 33 | 34 | #create new coordinates 35 | coordinates_new=[left_new,mid_new,right_new] 36 | 37 | #viz coordinates 38 | for i in coordinates 39 | 40 | for j in coordinates 41 | 42 | if i!=j 43 | 44 | #use ! to add to the existing figure 45 | plot!([i[1],j[1]],[i[2],j[2]], 46 | color=colour,axis=false, 47 | legend=false,grid=false) 48 | 49 | end 50 | 51 | end 52 | 53 | end 54 | 55 | #recursive plot sub triangles 56 | sierpiński_triangle([left,left_new,mid_new],lvl-1,fig) 57 | sierpiński_triangle([left_new,mid,right_new],lvl-1,fig) 58 | sierpiński_triangle([mid_new,right_new,right],lvl-1,fig) 59 | 60 | end 61 | 62 | 63 | # In[3]: 64 | 65 | 66 | #annoying feature of julia 67 | #plot wont show up in a loop unless i specify 68 | gr(size=(250,250)) 69 | fig=plot(legend=false,grid=false,axis=false,showaxis=false) 70 | sierpiński_triangle([(0,0),(0.5,1),(1,0)],4,fig) 71 | fig 72 | 73 | -------------------------------------------------------------------------------- /fibonacci with memoization.jl: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | #this is a test on how recursion with memory reduces time complexity 8 | 9 | 10 | # In[2]: 11 | 12 | 13 | #this is the fibonacci recursion function without memory 14 | #it is basically algorithm 101 for any coding language 15 | function fib(n) 16 | 17 | if n==1 18 | return 1 19 | elseif n==2 20 | return 1 21 | elseif n<=0 22 | printstyled("Invalid input",color=:red) 23 | return 24 | else 25 | return fib(n-1)+fib(n-2) 26 | 27 | end 28 | end 29 | 30 | 31 | # In[3]: 32 | 33 | 34 | #i need a global dictionary to do the memorization 35 | #or i can change function mmz(n) into mmz(n,memoization) 36 | global memoization=Dict(1=>1,2=>1) 37 | 38 | 39 | # In[4]: 40 | 41 | 42 | #mmz(n) is recursion with memory 43 | #everytime we do the calculation, we store it in the dictionary 44 | #i denote the key as the n th fibonacci number 45 | #the value as the number itself 46 | #if we can find the key in dictionary 47 | #we simply return the value 48 | #if not, we compute and update the dictionary then return the value 49 | function mmz(n) 50 | 51 | if n<=0 52 | printstyled("Invalid input",color=:red) 53 | return 54 | end 55 | 56 | if !(n in keys(memoization)) 57 | global memoization[n]=mmz(n-1)+mmz(n-2) 58 | end 59 | 60 | return memoization[n] 61 | end 62 | 63 | 64 | # In[5]: 65 | 66 | 67 | #using ijulia inline magic 68 | #equivalent to %timeit in ipython 69 | #0.000093 seconds 70 | @time begin 71 | fib(20) 72 | end 73 | 74 | 75 | # In[6]: 76 | 77 | 78 | #0.019746 seconds (5.60 k allocations: 335.035 KiB) 79 | #it seems to be slower 80 | #but if we compute mmz(30) now 81 | #its much faster than fib(30) 82 | @time begin 83 | mmz(20) 84 | end 85 | 86 | 87 | # In[7]: 88 | 89 | 90 | #0.021588 seconds 91 | @time begin 92 | fib(30) 93 | end 94 | 95 | 96 | # In[8]: 97 | 98 | 99 | #0.000023 seconds (52 allocations: 832 bytes) 100 | @time begin 101 | mmz(30) 102 | end 103 | 104 | -------------------------------------------------------------------------------- /coin change dynamic programming.py: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | #coin change is a straight forward dynamic programming problem 8 | #you are given one note and a set of coins of different values 9 | #assuming you have infinite supply of coins of different values 10 | #we need to compute different ways of making change 11 | #the order of the coins doesnt matter in this case 12 | #more details can be found in the following link 13 | # https://www.geeksforgeeks.org/coin-change-dp-7/ 14 | #the script can also be done in recursion 15 | # https://github.com/je-suis-tm/recursion-and-dynamic-programming/blob/master/coin%20change%20recursion.py 16 | 17 | 18 | # In[2]: 19 | 20 | 21 | #to solve this problem via tabulation 22 | #we divide one big problem into two sub problems 23 | #the case where coin of η value is excluded in the solutions 24 | #and the case where at least one coin of η value is included 25 | def coin_change(num,choice): 26 | 27 | #create matrix (num+1)*leng(choice) 28 | #the raison d'être is the computation starts from 0 to num 29 | #0 is when num is perfectly substituted by coins 30 | tabulation=[[0 for _ in range(len(choice))] for _ in range(num+1)] 31 | 32 | #initialize 33 | #when the remain value happens to be η 34 | #one solution is found 35 | for i in range(len(choice)): 36 | 37 | tabulation[0][i]=1 38 | 39 | #since we initialize the null case 40 | #the outerloop starts from 1 41 | for i in range(1,num+1): 42 | 43 | for j in range(len(choice)): 44 | 45 | if i-choice[j]>=0: 46 | 47 | #the case where at least one coin of η value is included 48 | #we just need the computation where η is deducted 49 | include=tabulation[i-choice[j]][j] 50 | 51 | else: 52 | 53 | include=0 54 | 55 | #the case where coin of η value is excluded in the solutions 56 | if j>=1: 57 | 58 | exclude=tabulation[i][j-1] 59 | 60 | else: 61 | 62 | exclude=0 63 | 64 | #two sub problems merge into a big one 65 | tabulation[i][j]=exclude+include 66 | 67 | #get the final answer 68 | return tabulation[num][len(choice)-1] 69 | 70 | 71 | # In[3]: 72 | 73 | 74 | coin_change(10,[1,2,3]) 75 | 76 | -------------------------------------------------------------------------------- /egg drop dynamic programming.py: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | #egg drop is a simple dynamic programming problem 8 | #the player is given some number of eggs 9 | #to test eggs break by free fall from which floor 10 | #more details can be found in the link below 11 | # https://www.geeksforgeeks.org/egg-dropping-puzzle-dp-11/ 12 | 13 | 14 | # In[2]: 15 | 16 | 17 | #initialize 18 | num_of_floors=40 19 | num_of_eggs=5 20 | 21 | 22 | # In[3]: 23 | 24 | 25 | #to solve this problem via dp 26 | #we test all the floor with the eggs 27 | def egg_drop(num_of_floors,num_of_eggs): 28 | 29 | #create tabulation matrix 30 | tabulation=[[None for _ in range(num_of_floors+1)] for _ in range(num_of_eggs+1)] 31 | 32 | #when u got zero egg u got zero attempt 33 | tabulation[0]=[0]*(num_of_floors+1) 34 | 35 | #when u got one egg the number of attempts will always be the number of floors 36 | tabulation[1]=[ii for ii in range(num_of_floors+1)] 37 | 38 | #ground floor doesnt break eggs as its on the ground 39 | #first floor only takes one attempt no matter how many eggs we have 40 | for i in range(2,num_of_eggs+1): 41 | tabulation[i][0]=0 42 | tabulation[i][1]=1 43 | 44 | #since we initialize the ground floor and the first floor 45 | #the loop starts from 2 46 | for id_egg in range(2,num_of_eggs+1): 47 | for id_floor in range(2,num_of_floors+1): 48 | 49 | #at the current position (id_egg,id_floor) 50 | #there are id_egg number of eggs and id_floor number of floors 51 | #we start to experiment the worst case via brute force 52 | #we drop eggs on each floor 53 | #there are id_floor*2 scenarios 54 | #each floor can be the one that breaks the egg or doesnt break the eggs 55 | #hence we compute how many trials are required for each scenario 56 | #take the maximum first to obtain worst case for each floor 57 | #and take the minimum to obtain the minimum number of trials 58 | tabulation[id_egg][id_floor]=min( 59 | [1+max( 60 | tabulation[id_egg-1][j-1], 61 | tabulation[id_egg][id_floor-j]) for j in range( 62 | 1,id_floor+1)]) 63 | 64 | return tabulation[num_of_eggs][num_of_floors] 65 | 66 | 67 | # In[4]: 68 | 69 | 70 | egg_drop(num_of_floors,num_of_eggs) 71 | 72 | -------------------------------------------------------------------------------- /egg drop dynamic programming.jl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env julia 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | #egg drop is a simple dynamic programming problem 8 | #the player is given some number of eggs 9 | #to test eggs break by free fall from which floor 10 | #more details can be found in the link below 11 | # https://www.geeksforgeeks.org/egg-dropping-puzzle-dp-11/ 12 | 13 | 14 | # In[2]: 15 | 16 | 17 | #initialize 18 | num_of_floors=271 19 | num_of_eggs=3; 20 | 21 | 22 | # In[3]: 23 | 24 | 25 | #to solve this problem via dp 26 | #we test all the floor with the eggs 27 | function egg_drop(num_of_floors,num_of_eggs) 28 | 29 | #create tabulation matrix 30 | tabulation=[[NaN for _ in 1:num_of_floors] for _ in 1:num_of_eggs] 31 | 32 | #when u got one egg the number of attempts will always be the number of floors 33 | tabulation[1]=[ii for ii in 1:num_of_floors] 34 | 35 | #ground floor doesnt break eggs as its on the ground 36 | #first floor only takes one attempt no matter how many eggs we have 37 | for i in 2:num_of_eggs 38 | tabulation[i][1]=1 39 | end 40 | 41 | #since we initialize the ground floor and the first floor 42 | #the loop starts from 2 43 | for id_egg in 2:num_of_eggs 44 | for id_floor in 2:num_of_floors 45 | 46 | #at the current position (id_egg,id_floor) 47 | #there are id_egg number of eggs and id_floor number of floors 48 | #we start to experiment the worst case via brute force 49 | #we drop eggs on each floor 50 | #there are id_floor*2 scenarios 51 | #each floor can be the one that breaks the egg or doesnt break the eggs 52 | #hence we compute how many trials are required for each scenario 53 | #take the maximum first to obtain worst case for each floor 54 | #and take the minimum to obtain the minimum number of trials 55 | tabulation[id_egg][id_floor]=minimum( 56 | append!([1+max( 57 | tabulation[id_egg-1][j-1], 58 | tabulation[id_egg][id_floor-j]) for j in 2:(id_floor-1)], 59 | 60 | #due to index starts at 1,the last worst case has to be separated 61 | [1+tabulation[id_egg-1][id_floor-1]])) 62 | end 63 | end 64 | return tabulation[num_of_eggs][num_of_floors] 65 | end 66 | 67 | 68 | # In[4]: 69 | 70 | 71 | egg_drop(num_of_floors,num_of_eggs) 72 | 73 | 74 | # In[ ]: 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /palindrome checker 4 methods.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Apr 8 18:10:50 2018 4 | 5 | @author: Administrator 6 | """ 7 | #check if a string is a palindrome 8 | #reversing a string is extremely easy 9 | #at first i was thinking about using pop function 10 | #however, for an empty string, pop function is still functional 11 | #it would not stop itself 12 | #it would show errors of popping from empty list 13 | #so i have to use the classic way, slicing 14 | #each time we slice the final letter 15 | #to remove the unnecessary marks, we have to use regular expression 16 | 17 | import re 18 | 19 | def f(n): 20 | #this is the base case, when string is empty 21 | #we just return empty 22 | if n=='': 23 | return n 24 | else: 25 | return n[-1]+f(n[:-1]) 26 | 27 | def check(n): 28 | #this part is the regular expression to get only characters 29 | #and format all of em into lower cases 30 | c1=re.findall('[a-zA-Z0-9]',n.lower()) 31 | c2=re.findall('[a-zA-Z0-9]',(f(n)).lower()) 32 | if c1==c2: 33 | return True 34 | else: 35 | return False 36 | 37 | 38 | 39 | #alternatively, we can use deque 40 | #we pop from both sides to see if they are equal 41 | #if not return false 42 | #note that the length of string could be an odd number 43 | #we wanna make sure the pop should take action while length of deque is larger than 1 44 | 45 | from collections import deque 46 | 47 | def g(n): 48 | 49 | c=re.findall('[a-zA-Z0-9]',n.lower()) 50 | de=deque(c) 51 | while len(de) >=1: 52 | if de.pop()!=de.popleft(): 53 | return False 54 | return True 55 | 56 | 57 | #or we can use non recursive slicing 58 | def h(n): 59 | c=re.findall('[a-zA-Z0-9]',n.lower()) 60 | if c[::-1]==c: 61 | return True 62 | else: 63 | return False 64 | 65 | #or creating a new list 66 | #using loop to append new list from old list s popped item 67 | def i(n): 68 | c=re.findall('[a-zA-Z0-9]',n.lower()) 69 | d=[] 70 | for i in range(len(c)): 71 | d.append(c.pop()) 72 | c=re.findall('[a-zA-Z0-9]',n.lower()) 73 | 74 | if d==c: 75 | return True 76 | else: 77 | return False 78 | 79 | 80 | 81 | print(check('Evil is a deed as I live!')) 82 | print(g('Evil is a deed as I live!')) 83 | print(h('Evil is a deed as I live!')) 84 | print(i('Evil is a deed as I live!')) 85 | #for time and space complexity, python non recursive slicing wins -------------------------------------------------------------------------------- /stock trading dynamic programming.py: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | #a simple day trading game 8 | #day trader is only allowed to make at maximum two trades 9 | #the strategy is long only 10 | #lets find out the maximum profit 11 | 12 | #more details can be found in the following link 13 | # https://www.geeksforgeeks.org/maximum-profit-by-buying-and-selling-a-share-at-most-twice/ 14 | 15 | #an alternative version in recursion exists 16 | #its done by using a different approach 17 | #strongly recommend you to take a look 18 | # https://github.com/je-suis-tm/recursion-and-dynamic-programming/blob/master/stock%20trading%20recursion.py 19 | 20 | 21 | # In[2]: 22 | 23 | 24 | #there are two scenarios to maximize the profit 25 | #one trade or two trades 26 | #first we run a reverse iteration 27 | #to obtain the maximum profit from one trade 28 | #then we run a normal iteration 29 | #to obtain the maximum profit 30 | #from one trade plus the result from reverse iteration 31 | def stock_trading(prices): 32 | 33 | #initialize the profit at zero 34 | profit=[0 for _ in range(len(prices))] 35 | 36 | #initialize maximum price with the close price 37 | max_price=prices[-1] 38 | 39 | #reverse order iteration 40 | for i in range(len(prices)-2,-1,-1): 41 | 42 | #update the maximum price to compute the maximum profit 43 | if prices[i]>max_price: 44 | 45 | max_price=prices[i] 46 | 47 | #two scenarios to get the maximum profit 48 | #either the previous iteration is larger 49 | #or this round of iteration 50 | profit[i]=max(profit[i+1],max_price-prices[i]) 51 | 52 | #initialize minimum price with the open price 53 | min_price=prices[0] 54 | 55 | #second round of iteration 56 | for i in range(1,len(prices)): 57 | 58 | #update the minimum price to compute the maximum profit 59 | if prices[i]=1 51 | 52 | #the case where at least one coin of η value is included 53 | #we just need the computation where η is deducted 54 | include=tabulation[i-choice[j]][j] 55 | 56 | else 57 | 58 | include=0 59 | 60 | end 61 | 62 | #the case where coin of η value is excluded in the solutions 63 | if j>=2 64 | 65 | exclude=tabulation[i][j-1] 66 | 67 | else 68 | 69 | exclude=0 70 | 71 | end 72 | 73 | #two sub problems merge into a big one 74 | tabulation[i][j]=exclude+include 75 | 76 | end 77 | 78 | end 79 | 80 | #get the final answer 81 | return tabulation[num+1][length(choice)] 82 | 83 | end 84 | 85 | 86 | # In[3]: 87 | 88 | 89 | coin_change(10,[1,2,5]) 90 | -------------------------------------------------------------------------------- /stock trading dynamic programming.jl: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | 4 | # In[1]: 5 | 6 | 7 | #a simple day trading game 8 | #day trader is only allowed to make at maximum two trades 9 | #the strategy is long only 10 | #lets find out the maximum profit 11 | 12 | #more details can be found in the following link 13 | # https://www.geeksforgeeks.org/maximum-profit-by-buying-and-selling-a-share-at-most-twice/ 14 | 15 | #an alternative version in recursion exists 16 | #its done by using a different approach 17 | #strongly recommend you to take a look 18 | # https://github.com/je-suis-tm/recursion-and-dynamic-programming/blob/master/stock%20trading%20recursion.jl 19 | 20 | 21 | # In[2]: 22 | 23 | 24 | #there are two scenarios to maximize the profit 25 | #one trade or two trades 26 | #first we run a reverse iteration 27 | #to obtain the maximum profit from one trade 28 | #then we run a normal iteration 29 | #to obtain the maximum profit 30 | #from one trade plus the result from reverse iteration 31 | function stock_trading(prices) 32 | 33 | #initialize the profit at zero 34 | profit=[0 for _ in 1:length(prices)] 35 | 36 | #initialize maximum price with the close price 37 | max_price=prices[end] 38 | 39 | #reverse order iteration 40 | for i in length(prices)-1:-1:1 41 | 42 | #update the maximum price to compute the maximum profit 43 | if prices[i]>max_price 44 | 45 | max_price=prices[i] 46 | 47 | end 48 | 49 | #two scenarios to get the maximum profit 50 | #either the previous iteration is larger 51 | #or this round of iteration 52 | profit[i]=max(profit[i+1],max_price-prices[i]) 53 | 54 | end 55 | 56 | #initialize minimum price with the open price 57 | min_price=prices[1] 58 | 59 | #second round of iteration 60 | for i in 2:length(prices) 61 | 62 | #update the minimum price to compute the maximum profit 63 | if prices[i]minind: 45 | 46 | #when a larger value is found 47 | #update accordingly 48 | if prices[right]>max_price: 49 | 50 | max_price=prices[right] 51 | maxind=right 52 | 53 | 54 | #the same applies to a smaller value 55 | if prices[left]subset_max: 49 | subset_max=subset[k] 50 | target=k 51 | 52 | #dynamic programming 53 | if subset_max+values[i-1]>array[i-1][j]: 54 | array[i][j]=subset_max+values[i-1] 55 | path[i][j]=path[target][j-weights[i-1]]+[i-1] 56 | elif subset_max+values[i-1]==array[i-1][j] and weights[i-1]minind 45 | 46 | #when a larger value is found 47 | #update accordingly 48 | if prices[right]>max_price 49 | 50 | max_price=prices[right] 51 | maxind=copy(right) 52 | 53 | end 54 | 55 | #the same applies to a smaller value 56 | if prices[left]j: 69 | 70 | l[i][j]=l[i-1][j] 71 | 72 | else: 73 | 74 | #we use max funcion to see if adding item i would be the new optimal 75 | l[i][j]=max(l[i-1][j],l[i-1][j-w[i-1]]+v[i-1]) 76 | 77 | return l[len(v)][c] 78 | 79 | print(knapsack([0,40,60,50,120],[0,10,15,20,30],50)) 80 | -------------------------------------------------------------------------------- /edit distance dynamic programming.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Mar 19 15:22:38 2018 4 | 5 | @author: Administrator 6 | """ 7 | 8 | #its also called levenshtein distance 9 | #it can be done via recursion too 10 | #the recursion version is much more elegant yet less efficient 11 | # https://github.com/je-suis-tm/recursion/blob/master/edit%20distance%20recursion.py 12 | 13 | #edit distance is to minimize the steps transforming one string to another 14 | #the way to solve this problem is very similar is to knapsack 15 | #assume we have two strings a and b 16 | #we build a matrix len(a)*len(b) 17 | #however,given lists start at index zero 18 | #our matrix should be (len(a)+1)*(len(b)+1) 19 | 20 | #there are three different ways to transform a string 21 | #insert,delete and replace 22 | #we can use any of them or combined 23 | #lets take a look at the best case first 24 | #assume string a is string b 25 | #we dont need to do anything 26 | #so 0 steps would be the answer 27 | #for the worst case 28 | #when string a has nothing in common with string b 29 | #we would have to replace the whole string a 30 | #the steps become the maximum step which is max(len(a),len(b)) 31 | #for general case,the number of steps would fall between the worst and the best case 32 | #assume we are at mth letter of string a and nth letter string b 33 | #if we wanna get the optimal steps of transforming string a to string b 34 | #we have to make sure at each letter transformation 35 | #a[:m] and b[:n] have reached their optimal status 36 | #otherwise,we could always find another combination of insert,delete and replace 37 | #to get a "real" optimal a[:m] and b[:n] 38 | #it would make our string transformation not so optimal any more 39 | #it is the same logic as the optimization of knapsack problem 40 | #after we set our logic straight 41 | #we would take a look at three different approaches 42 | #lets take a look at insertion 43 | #basically we need to insert nth letter from string b into string a at nth position 44 | #the cumulated steps we have taken should be matrix[m][n-1]+1 45 | #matrix[m][n-1] is the steps for a[:m] to b[:n] 46 | #for delete,it is vice versa 47 | #the cumulated steps we have taken should be matrix[m-1][n]+1 48 | #for replacement,it is a lil bit tricky 49 | #there are two scenarios 50 | #if a[m]==b[n] 51 | #it should be matrix[m-1][n-1] 52 | #we dont need any replacement at all 53 | #else,it should be matrix[m-1][n-1]+1 54 | #we replace mth letter of string a with nth letter of string b 55 | #after we managed to understand three different approaches 56 | #we want to take the minimum number of steps among these three approaches 57 | #throughout the iteration of different positions of both strings 58 | #in the end,we would get the optimal steps to transform one string to another,YAY 59 | def edit_distance(a,b): 60 | 61 | len_a=len(a) 62 | len_b=len(b) 63 | 64 | #this part is to create a matrix of len(a)*len(b) 65 | #as lists start at index 0 66 | #we get a matrix of (len(a)+1)*(len(b)+1) instead 67 | c=[[0]*(len_b+1) for i in range(len_a+1)] 68 | 69 | for j in range(len_a+1): 70 | c[j][0]=j 71 | for k in range(len_b+1): 72 | c[0][k]=k 73 | 74 | #we take iterations on both string a and b 75 | #next,we check if a[m]==b[n] 76 | #if yes,no replacement needed 77 | #if no,replacement needed 78 | #we take a minimum function to see which combination would give the minimum steps 79 | #eventually we got what we are after 80 | for l in range(1,len_a+1): 81 | for m in range(1,len_b+1): 82 | if a[l-1]==b[m-1]: 83 | c[l][m]=min(c[l-1][m]+1,c[l][m-1]+1,c[l-1][m-1]) 84 | else: 85 | c[l][m]=min(c[l-1][m]+1,c[l][m-1]+1,c[l-1][m-1]+1) 86 | 87 | return c[len_a][len_b] 88 | 89 | print(edit_distance('baiseé','bas')) 90 | -------------------------------------------------------------------------------- /sierpiński carpet.jl: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | #fractal is one of the interesting topics in geometry 8 | #it is usually described by a recursive function 9 | #voila,here we are! 10 | using Plots 11 | 12 | 13 | # In[2]: 14 | 15 | 16 | coordinates=[(0,0),(3,0),(3,-3),(0,-3)] 17 | 18 | 19 | # In[3]: 20 | 21 | 22 | function sierpiński_carpet(coordinates,lvl,fig,colour="black") 23 | 24 | #stop recursion 25 | if lvl==0 26 | 27 | return 28 | 29 | end 30 | 31 | #unpack coordinates 32 | #coordinates follow clockwise order 33 | topleft=coordinates[1];topright=coordinates[2] 34 | bottomright=coordinates[3];bottomleft=coordinates[4] 35 | 36 | #create new coordinates 37 | topleft_new=(topleft[1]+(topright[1]-topleft[1])/3, 38 | bottomleft[2]+(topleft[2]-bottomleft[2])/3*2) 39 | 40 | topright_new=(topleft[1]+(topright[1]-topleft[1])/3*2, 41 | bottomleft[2]+(topleft[2]-bottomleft[2])/3*2) 42 | 43 | bottomleft_new=(topleft[1]+(topright[1]-topleft[1])/3, 44 | bottomleft[2]+(topleft[2]-bottomleft[2])/3) 45 | 46 | bottomright_new=(topleft[1]+(topright[1]-topleft[1])/3*2, 47 | bottomleft[2]+(topleft[2]-bottomleft[2])/3) 48 | 49 | coordinates_new=[topleft_new,topright_new, 50 | bottomright_new,bottomleft_new] 51 | 52 | #viz new coordinates 53 | for i in 2:length(coordinates_new) 54 | 55 | #use ! to add to the existing figure 56 | plot!([coordinates_new[i][1],coordinates_new[i-1][1]], 57 | [coordinates_new[i][2],coordinates_new[i-1][2]], 58 | color=colour,legend=false,grid=false,axis=false) 59 | 60 | end 61 | 62 | #julia doesnt allow index at -1 so i have to add one extra line 63 | plot!([coordinates_new[1][1],coordinates_new[end][1]], 64 | [coordinates_new[1][2],coordinates_new[end][2]], 65 | color=colour,legend=false,grid=false,axis=false) 66 | 67 | #recursive plot sub carpets following clockwise order 68 | sierpiński_carpet([topleft,(topleft_new[1],topleft[2]), 69 | topleft_new,(topleft[1],topleft_new[2])],lvl-1,fig) 70 | 71 | sierpiński_carpet([(topleft_new[1],topleft[2]), 72 | (topright_new[1],topright[2]), 73 | topright_new,topleft_new],lvl-1,fig) 74 | 75 | sierpiński_carpet([(topright_new[1],topright[2]),topright, 76 | (topright[1],topright_new[2]),topright_new], 77 | lvl-1,fig) 78 | 79 | sierpiński_carpet([topright_new,(topright[1],topright_new[2]), 80 | (bottomright[1],bottomright_new[2]), 81 | bottomright_new],lvl-1,fig) 82 | 83 | sierpiński_carpet([bottomright_new,(bottomright[1],bottomright_new[2]), 84 | bottomright,(bottomright_new[1],bottomright[2])], 85 | lvl-1,fig) 86 | 87 | sierpiński_carpet([bottomleft_new,bottomright_new, 88 | (bottomright_new[1],bottomright[2]), 89 | (bottomleft_new[1],bottomleft[2])],lvl-1,fig) 90 | 91 | sierpiński_carpet([(topleft[1],bottomleft_new[2]),bottomleft_new, 92 | (bottomleft_new[1],bottomleft[2]),bottomleft], 93 | lvl-1,fig) 94 | 95 | sierpiński_carpet([(topleft[1],topleft_new[2]),topleft_new, 96 | bottomleft_new,(topleft[1],bottomleft_new[2])], 97 | lvl-1,fig) 98 | 99 | end 100 | 101 | 102 | # In[4]: 103 | 104 | 105 | #annoying feature of julia 106 | #plot wont show up in a loop unless i specify 107 | gr(size=(250,250)) 108 | fig=plot(legend=false,grid=false,axis=false,showaxis=false) 109 | sierpiński_carpet(coordinates,4,fig) 110 | fig 111 | 112 | -------------------------------------------------------------------------------- /hilbert curve.py: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | #fractal is one of the interesting topics in geometry 8 | #it is usually described by a recursive function 9 | #voila,here we are! 10 | import matplotlib.pyplot as plt 11 | 12 | 13 | # In[2]: 14 | 15 | 16 | #define starting and ending points 17 | #and the order of hilbert curve 18 | n=4 19 | starting_point=(0,0) 20 | ending_point=(2**n-1,0) 21 | 22 | 23 | # In[3]: 24 | 25 | 26 | #the code for hilbert curve may look intimidating at the first glance 27 | #it is extremely simple and straight forward 28 | def hilbert_curve(point1,point2,n): 29 | 30 | #unpack 31 | x_start,y_start=point1 32 | x_end,y_end=point2 33 | 34 | #the function consists four different parts 35 | #which are 4 different rotations plus flips of a second order hilbert curve 36 | #0 degree second order hilbert curve 37 | if x_startx_end and y_start==y_end: 61 | L=x_start-x_end 62 | keypoints=[(x_start,y_start),(x_start,y_start+(L-1)/2), 63 | (x_start,y_start+(L-1)/2+1),(x_start-(L-1)/2,y_start+(L-1)/2+1), 64 | (x_start-(L-1)/2-1,y_start+(L-1)/2+1),(x_start-L,y_start+(L-1)/2+1), 65 | (x_start-L,y_start+(L-1)/2),(x_start-L,y_start)] 66 | if n==1: 67 | yield keypoints 68 | else: 69 | for i in range(0,8,2): 70 | yield from hilbert_curve(keypoints[i],keypoints[i+1],n-1) 71 | 72 | #clockwise 90 degree horizontal flipped second order hilbert curve 73 | if x_start==x_end and y_start>y_end: 74 | L=y_start-y_end 75 | keypoints=[(x_start,y_start),(x_start+(L-1)/2,y_start), 76 | (x_start+(L-1)/2+1,y_start),(x_start+(L-1)/2+1,y_start-(L-1)/2), 77 | (x_start+(L-1)/2+1,y_start-(L-1)/2-1),(x_start+(L-1)/2+1,y_start-L), 78 | (x_start+(L-1)/2,y_start-L),(x_start,y_start-L)] 79 | if n==1: 80 | yield keypoints 81 | else: 82 | for i in range(0,8,2): 83 | yield from hilbert_curve(keypoints[i],keypoints[i+1],n-1) 84 | 85 | #clockwise 270 degree horizontal flipped second order hilbert curve 86 | if x_start==x_end and y_startj 72 | 73 | matrix[i][j]=matrix[i-1][j] 74 | 75 | else 76 | 77 | #julia index starts from 1 78 | #which is a pain in the ass 79 | #when current capacity==the new item s weight 80 | #it creates an issue 81 | if j==weight[i-1] 82 | 83 | ind=1 84 | 85 | else 86 | 87 | ind=j-weight[i-1] 88 | 89 | end 90 | 91 | #we use max funcion to see if adding item i-1 would be the new optimal 92 | matrix[i][j]=max(matrix[i-1][j], 93 | matrix[i-1][ind]+value[i-1]) 94 | 95 | end 96 | 97 | end 98 | 99 | end 100 | 101 | return matrix[length(value)+1][capacity+1] 102 | 103 | end 104 | 105 | 106 | # In[3]: 107 | 108 | 109 | println(knapsack([0,50,60,60,120],[0,10,15,20,40],50)) 110 | 111 | -------------------------------------------------------------------------------- /palindrome checker 4 methods.jl: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | #check if a string is a palindrome 7 | #reversing a string is extremely easy 8 | #at first i was thinking about using pop function 9 | #however, for an empty string, pop function is still functional 10 | #it would not stop itself 11 | #it would show errors of popping from empty list 12 | #so i have to use the classic way, slicing 13 | #each time we slice the final letter 14 | #to remove the unnecessary marks, we have to use regular expression 15 | function palindrome_slicing(rawtext) 16 | 17 | #regex is very different in julia 18 | #we need to use match attribute to get string 19 | text=[i.match for i in eachmatch(r"[a-zA-Z0-9]",lowercase(rawtext))] 20 | 21 | #use non recursive slicing 22 | if text[length(text):-1:1]==text 23 | 24 | return true 25 | 26 | else 27 | 28 | return false 29 | 30 | end 31 | 32 | end 33 | 34 | 35 | # In[2]: 36 | 37 | 38 | function palindrome_recursion(text) 39 | 40 | #this is the base case, when string is empty 41 | #we just return empty 42 | if isempty(text) 43 | 44 | return text 45 | 46 | else 47 | 48 | #such a pain without a python style list+list 49 | return cat([text[end]], 50 | palindrome_recursion(text[1:end-1]), 51 | dims=1) 52 | 53 | end 54 | 55 | end 56 | 57 | 58 | # In[3]: 59 | 60 | 61 | function recursion_check(rawtext) 62 | 63 | #this part is the regular expression to get only characters 64 | #and format all of em into lower cases 65 | text=[i.match for i in eachmatch(r"[a-zA-Z0-9]",lowercase(rawtext))] 66 | 67 | if palindrome_recursion(text)==text 68 | 69 | return true 70 | 71 | else 72 | 73 | return false 74 | 75 | end 76 | 77 | end 78 | 79 | 80 | # In[4]: 81 | 82 | #or creating a new list 83 | #using loop to append popped items from the old list 84 | function palindrome_list(rawtext) 85 | 86 | text=[i.match for i in eachmatch(r"[a-zA-Z0-9]",lowercase(rawtext))] 87 | 88 | new=String[] 89 | 90 | for i in 1:length(text) 91 | 92 | #be careful with append 93 | #append will create char type instead of string type 94 | push!(new,text[end-i+1]) 95 | 96 | end 97 | 98 | if new==text 99 | 100 | return true 101 | 102 | else 103 | 104 | return false 105 | 106 | end 107 | 108 | end 109 | 110 | 111 | # In[5]: 112 | 113 | #alternatively, we can use deque 114 | #we pop from both sides to see if they are equal 115 | #if not return false 116 | #note that the length of string could be an odd number 117 | #we wanna make sure the pop should take action while length of deque is larger than 1 118 | function palindrome_deque(rawtext) 119 | 120 | text=[i.match for i in eachmatch(r"[a-zA-Z0-9]",lowercase(rawtext))] 121 | 122 | while length(text)>=1 123 | 124 | if pop!(text)!=popfirst!(text) 125 | 126 | return false 127 | 128 | end 129 | 130 | end 131 | 132 | return true 133 | 134 | end 135 | 136 | 137 | # In[6]: 138 | 139 | 140 | #0.257236 seconds (636.24 k allocations: 31.108 MiB, 2.37% gc time) 141 | @time begin 142 | 143 | recursion_check("Evil is a deed as I live!") 144 | 145 | end 146 | 147 | 148 | # In[7]: 149 | 150 | 151 | #0.058568 seconds (87.67 k allocations: 4.231 MiB) 152 | @time begin 153 | 154 | palindrome_slicing("Evil is a deed as I live!") 155 | 156 | end 157 | 158 | 159 | # In[8]: 160 | 161 | 162 | #0.084145 seconds (140.27 k allocations: 6.717 MiB, 8.51% gc time) 163 | @time begin 164 | 165 | palindrome_list("Evil is a deed as I live!") 166 | 167 | end 168 | 169 | 170 | # In[9]: 171 | 172 | 173 | #0.062403 seconds (74.95 k allocations: 3.500 MiB) 174 | @time begin 175 | 176 | palindrome_deque("Evil is a deed as I live!") 177 | 178 | end 179 | 180 | 181 | # In[10]: 182 | 183 | 184 | #in julia, the fastest is still the slicing approach 185 | 186 | -------------------------------------------------------------------------------- /edit distance dynamic programming.jl: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | #its also called levenshtein distance 8 | #it can be done via recursion too 9 | #the recursion version is much more elegant yet less efficient 10 | # https://github.com/je-suis-tm/recursion/blob/master/edit%20distance%20recursion.jl 11 | 12 | #edit distance is to minimize the steps transforming one string to another 13 | #the way to solve this problem is very similar is to knapsack 14 | #assume we have two strings text1 and text2 15 | #we build a matrix with the size of (length(text1)+1)*(length(text2)+1) 16 | 17 | #there are three different ways to transform a string 18 | #insert,delete and replace 19 | #we can use any of them or combined 20 | #lets take a look at the best case first 21 | #assume string text1 is string text2 22 | #we dont need to do anything 23 | #so 0 steps would be the answer 24 | #for the worst case 25 | #when string text1 has nothing in common with string text2 26 | #we would have to replace the whole string text1 27 | #the steps become the maximum step which is max(length(text1),length(text2)) 28 | #for general case,the number of steps would fall between the worst and the best case 29 | #assume we are at i th letter of string text1 and j th letter string text2 30 | #if we wanna get the optimal steps of transforming string text1 to string text2 31 | #we have to make sure at each letter transformation 32 | #text1[1:i] and text2[1:j] have reached their optimal status 33 | #otherwise,we could always find another combination of insert,delete and replace 34 | #to get a "real" optimal text1[1:i] and text2[1:j] 35 | #it would make our string transformation not so optimal any more 36 | #it is the same logic as the optimization of knapsack problem 37 | #after we set our logic straight 38 | #we would take a look at three different approaches 39 | #lets take a look at insertion 40 | #basically we need to insert j th letter from string text2 into string text1 at i th position 41 | #the cumulated steps we have taken should be matrix[i][j-1]+1 42 | #matrix[i][j-1] is the steps for text1[1:i] to text2[1:j] 43 | #for delete,it is vice versa 44 | #the cumulated steps we have taken should be matrix[i-1][j]+1 45 | #for replacement,it is a lil bit tricky 46 | #there are two scenarios 47 | #if text1[i-1]==text2[j-1] 48 | #it should be matrix[i-1][j-1] 49 | #we dont need any replacement at all 50 | #else,it should be matrix[i-1][j-1]+1 51 | #we replace i th letter of string text1 with j th letter of string text2 52 | #after we managed to understand three different approaches 53 | #we want to take the minimum number of steps among these three approaches 54 | #throughout the iteration of different positions of both strings 55 | #in the end,we would get the optimal steps to transform one string to another,YAY 56 | 57 | 58 | # In[2]: 59 | 60 | 61 | function edit_distance(text1,text2) 62 | 63 | len1=length(text1)+1 64 | len2=length(text2)+1 65 | 66 | #this part is to create a matrix of (length(text1)+1)*(length(text2)+1) 67 | matrix=[[0 for _ in 1:len2] for _ in 1:len1] 68 | 69 | for i in 1:len1 70 | 71 | matrix[i][1]=i-1 72 | 73 | end 74 | 75 | for i in 1:len2 76 | 77 | matrix[1][i]=i-1 78 | 79 | end 80 | 81 | #we take iterations on both string text1 and text2 82 | #next,we check if text1[i-1]==text2[j-1] 83 | #if yes,no replacement needed 84 | #if no,replacement needed 85 | #we take a minimum function to see which combination would give the minimum steps 86 | #eventually we got what we are after 87 | for i in 2:len1 88 | 89 | for j in 2:len2 90 | 91 | if text1[i-1]==text2[j-1] 92 | 93 | matrix[i][j]=min(matrix[i-1][j]+1, 94 | matrix[i][j-1]+1, 95 | matrix[i-1][j-1]) 96 | 97 | else 98 | 99 | matrix[i][j]=min(matrix[i-1][j]+1, 100 | matrix[i][j-1]+1, 101 | matrix[i-1][j-1]+1) 102 | 103 | end 104 | 105 | end 106 | 107 | end 108 | 109 | return matrix[len1][len2] 110 | 111 | end 112 | 113 | 114 | # In[3]: 115 | 116 | 117 | println(edit_distance("baiseé","bas")) 118 | 119 | -------------------------------------------------------------------------------- /pascal triangle with memoization.jl: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | function pascal_triangle(n) 8 | 9 | row=Any[] 10 | 11 | #base case 12 | if n==1 13 | 14 | return Any[1] 15 | 16 | elseif n==2 17 | 18 | return Any[1,1] 19 | 20 | else 21 | 22 | #calculate the elements in each row 23 | for i in 2:n-1 24 | 25 | #rolling sum all the values within 2 windows from the previous row 26 | #but we cannot include two boundary numbers 1 in this row 27 | push!(row,pascal_triangle(n-1)[i-1]+pascal_triangle(n-1)[i]) 28 | 29 | end 30 | 31 | #append 1 for both front and rear of the row 32 | pushfirst!(row,1) 33 | push!(row,1) 34 | 35 | end 36 | 37 | return row 38 | 39 | end 40 | 41 | 42 | # In[2]: 43 | 44 | 45 | function styled_row(nested) 46 | 47 | #the first loop is to concatenate all rows 48 | for i in 1:length(nested) 49 | 50 | temp=nested[i] 51 | 52 | #this loop is to reshape the row 53 | #insert '' between each element in the row 54 | for j in 2:(2*i-1) 55 | 56 | #if index k is an even number,insert '' 57 | if j%2==0 58 | 59 | insert!(temp,j,"") 60 | 61 | end 62 | 63 | end 64 | 65 | #need to add '' to both sides of rows 66 | #length(nested)-i=((length(nested)+length(nested)-1)-(i+i-1))/2 67 | #we set the n th row plus n-1 space as the total elements in a row 68 | #we minus the reshaped row (i+i-1) 69 | #we get the space for both sides 70 | #finally we divide it by 2 and add to both sides of the row 71 | #we append the styled row into rows 72 | nested[i]=cat(fill("",length(nested)-i),temp,fill("",length(nested)-i),dims=1) 73 | 74 | end 75 | 76 | return nested 77 | 78 | end 79 | 80 | 81 | # In[3]: 82 | 83 | #print out each row 84 | function printit(n) 85 | 86 | nested=Any[] 87 | 88 | for i in 1:n 89 | 90 | push!(nested,pascal_triangle(i)) 91 | 92 | end 93 | 94 | for i in styled_row(nested) 95 | 96 | println(i) 97 | 98 | end 99 | 100 | end 101 | 102 | 103 | # In[4]: 104 | 105 | 106 | #using memoization 107 | 108 | 109 | # In[5]: 110 | 111 | 112 | function mmz(n) 113 | 114 | row=Any[] 115 | 116 | if !(n in keys(memoization)) 117 | 118 | #rolling sum all the values within 2 windows from the previous row 119 | #but we cannot include two boundary numbers 1 in this row 120 | for i in 2:n-1 121 | 122 | push!(row,mmz(n-1)[i-1]+mmz(n-1)[i]) 123 | 124 | end 125 | 126 | #append 1 for both front and rear of the row 127 | pushfirst!(row,1) 128 | push!(row,1) 129 | 130 | global memoization[n]=row 131 | 132 | end 133 | 134 | return memoization[n] 135 | 136 | end 137 | 138 | 139 | # In[6]: 140 | 141 | 142 | function printit_mmz(n) 143 | 144 | global memoization=Dict(1=>Any[1],2=>Any[1,1]) 145 | 146 | nested=Any[] 147 | 148 | for i in 1:n 149 | 150 | push!(nested,mmz(i)) 151 | 152 | end 153 | 154 | for i in styled_row(nested) 155 | 156 | println(i) 157 | 158 | end 159 | 160 | end 161 | 162 | 163 | # In[7]: 164 | 165 | 166 | #0.021448 seconds (1.95 k allocations: 72.875 KiB) 167 | @time begin 168 | 169 | printit(5) 170 | 171 | end 172 | 173 | 174 | # In[8]: 175 | 176 | 177 | #0.355459 seconds (344.14 k allocations: 17.351 MiB) 178 | @time begin 179 | 180 | printit_mmz(5) 181 | 182 | end 183 | 184 | 185 | # In[9]: 186 | 187 | 188 | #3.822380 seconds (36.60 M allocations: 2.678 GiB, 8.57% gc time) 189 | @time begin 190 | 191 | printit(10) 192 | 193 | end 194 | 195 | 196 | # In[10]: 197 | 198 | 199 | #0.232830 seconds (7.27 k allocations: 254.938 KiB) 200 | @time begin 201 | 202 | printit_mmz(10) 203 | 204 | end 205 | 206 | #at the first glance,memoization isnt faster 207 | #as the number grows larger,memoization reveals its true nature -------------------------------------------------------------------------------- /hilbert curve.jl: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | #fractal is one of the interesting topics in geometry 8 | #it is usually described by a recursive function 9 | #voila,here we are! 10 | using Plots 11 | 12 | 13 | # In[2]: 14 | 15 | 16 | #define starting and ending points 17 | #and the order of hilbert curve 18 | n=4 19 | starting_point=(0,0) 20 | ending_point=(2^n-1,0) 21 | 22 | 23 | # In[3]: 24 | 25 | 26 | #the code for hilbert curve may look intimidating at the first glance 27 | #it is extremely simple and straight forward 28 | #julia version is slightly different 29 | #as we cannot use yield like python 30 | #coroutine in julia is too complicated 31 | #maybe another day... 32 | function hilbert_curve(coordinates,point1,point2,n) 33 | 34 | #unpack 35 | x_start,y_start=point1 36 | x_end,y_end=point2 37 | 38 | #the function consists four different parts 39 | #which are 4 different rotations plus flips of a second order hilbert curve 40 | #0 degree second order hilbert curve 41 | if x_startx_end && y_start==y_end 69 | L=x_start-x_end 70 | keypoints=[(x_start,y_start),(x_start,y_start+(L-1)/2), 71 | (x_start,y_start+(L-1)/2+1),(x_start-(L-1)/2,y_start+(L-1)/2+1), 72 | (x_start-(L-1)/2-1,y_start+(L-1)/2+1),(x_start-L,y_start+(L-1)/2+1), 73 | (x_start-L,y_start+(L-1)/2),(x_start-L,y_start)] 74 | if n==1 75 | append!(coordinates,keypoints) 76 | else 77 | for i in 1:2:8 78 | hilbert_curve(coordinates, 79 | keypoints[i],keypoints[i+1],n-1) 80 | end 81 | end 82 | end 83 | 84 | #clockwise 90 degree horizontal flipped second order hilbert curve 85 | if x_start==x_end && y_start>y_end 86 | L=y_start-y_end 87 | keypoints=[(x_start,y_start),(x_start+(L-1)/2,y_start), 88 | (x_start+(L-1)/2+1,y_start),(x_start+(L-1)/2+1,y_start-(L-1)/2), 89 | (x_start+(L-1)/2+1,y_start-(L-1)/2-1),(x_start+(L-1)/2+1,y_start-L), 90 | (x_start+(L-1)/2,y_start-L),(x_start,y_start-L)] 91 | if n==1 92 | append!(coordinates,keypoints) 93 | else 94 | for i in 1:2:8 95 | hilbert_curve(coordinates, 96 | keypoints[i],keypoints[i+1],n-1) 97 | end 98 | end 99 | end 100 | 101 | #clockwise 270 degree horizontal flipped second order hilbert curve 102 | if x_start==x_end && y_startsubset_max 60 | subset_max=subset[k] 61 | target=k 62 | end 63 | end 64 | 65 | #dynamic programming 66 | if subset_max+values[i]>array[i-1][j] 67 | array[i][j]=subset_max+values[i] 68 | path[i][j]=[path[target][j-weights[i]];[i]] 69 | elseif subset_max+values[i]==array[i-1][j] && weights[i]array[i-1][j] 81 | array[i][j]=values[i] 82 | path[i][j]=[i] 83 | elseif values[i]==array[i-1][j] && weights[i]euclidean_distance((x2,y2),measure) 81 | datapoint=(x1,y1) 82 | else 83 | datapoint=(x2,y2) 84 | end 85 | 86 | end 87 | 88 | return datapoint 89 | 90 | end 91 | 92 | #recursively compute the coordinates of koch curve data points 93 | #to effectively connect the data points,the best choice is to use turtle 94 | #it would be too difficult to connect the dots via analytic geometry 95 | function koch_snowflake(base1,base2,base3,n,arr) 96 | 97 | #base case 98 | if n==0 99 | return 100 | 101 | else 102 | 103 | #find mid point 104 | #midpoint between base1 and base2 has to satisfy two conditions 105 | #it has to be on the same line as base1 and base2 106 | #assume this line follows y=kx+b 107 | #the midpoint is (x,kx+b) 108 | #base1 is (α,kα+b),base2 is (δ,kδ+b) 109 | #the euclidean distance between midpoint and base1 should be 110 | #half of the euclidean distance between base1 and base2 111 | #(x-α)^2+(kx+b-kα-b)^2=((α-δ)^2+(kα+b-kδ-b)^2)/4 112 | #apart from x,everything else in the equation is constant 113 | #this forms a simple quadratic solution to get two roots 114 | #one root would be between base1 and base2 which yields midpoint 115 | #and the other would be farther away from base2 116 | #this function solves the equation via (-B+(B^2-4*A*C)^0.5)/(2*A) 117 | #alternatively,you can use scipy.optimize.root 118 | #the caveat is it does not offer both roots 119 | #a wrong initial guess could take you to the wrong root 120 | midpoint=get_datapoint(base1,base2,euclidean_distance(base1,base2)/2) 121 | 122 | #compute the top point of a triangle 123 | #the computation is similar to midpoint 124 | #the euclidean distance between triangle_top and midpoint should be 125 | #one third of the distance between base3 and midpoint 126 | triangle_top=get_datapoint(midpoint,base3, 127 | euclidean_distance(midpoint,base3)/3, 128 | "outer") 129 | 130 | #two segment points divide a line into three equal parts 131 | #the computation is almost the same as midpoint 132 | #the euclidean distance between segment1 and base1 133 | #should be one third of the euclidean distance between base2 and base1 134 | segment1=get_datapoint(base1,base2,euclidean_distance(base1,base2)/3) 135 | segment2=get_datapoint(base2,base1,euclidean_distance(base1,base2)/3) 136 | 137 | #compute the nearest segment point of the neighboring line 138 | segment_side_1=get_datapoint(base1,base3,euclidean_distance(base1,base3)/3) 139 | segment_side_2=get_datapoint(base2,base3,euclidean_distance(base2,base3)/3) 140 | 141 | append!(arr,[segment1,segment2,triangle_top]) 142 | 143 | #recursion 144 | koch_snowflake(base1,segment1,segment_side_1,n-1,arr) 145 | koch_snowflake(segment1,triangle_top,segment2,n-1,arr) 146 | koch_snowflake(triangle_top,segment2,segment1,n-1,arr) 147 | koch_snowflake(segment2,base2,segment_side_2,n-1,arr) 148 | end 149 | arr 150 | end 151 | 152 | 153 | # In[3]: 154 | 155 | 156 | #set data points 157 | point1=(0.0,0.0) 158 | point2=(3.0,0.0) 159 | point3=(1.5,1.5*√(3)) 160 | 161 | n=4 162 | 163 | 164 | # In[4]: 165 | 166 | 167 | #collect coordinates 168 | coordinates=[point1,point2,point3] 169 | append!(coordinates,koch_snowflake(point1,point2,point3,n,[])) 170 | append!(coordinates,koch_snowflake(point3,point1,point2,n,[])) 171 | append!(coordinates,koch_snowflake(point2,point3,point1,n,[])); 172 | 173 | 174 | # In[5]: 175 | 176 | 177 | #viz 178 | #visually u can tell some of the data points are miscalculated 179 | #purely caused by floating point arithmetic 180 | gr(size=(250,250)) 181 | scatter([i[1] for i in coordinates], 182 | [i[2] for i in coordinates],markersize=2, 183 | legend=false,showaxis=false,ticks=false,grid=false) 184 | 185 | 186 | -------------------------------------------------------------------------------- /koch snowflake.py: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | #another fractal script via recursion 8 | #this script heavily involves analytic geometry 9 | #a good material to help you rewind high school geometry 10 | # https://youthconway.com/handouts/analyticgeo.pdf 11 | import matplotlib.pyplot as plt 12 | 13 | 14 | # In[2]: 15 | 16 | 17 | #simple solution to get coefficients of the equation 18 | def get_line_params(x1,y1,x2,y2): 19 | 20 | slope=(y1-y2)/(x1-x2) 21 | intercept=y1-slope*x1 22 | 23 | return slope,intercept 24 | 25 | 26 | # In[3]: 27 | 28 | 29 | #compute euclidean distance 30 | def euclidean_distance(point1,point2): 31 | return ((point1[0]-point2[0])**2+(point1[1]-point2[1])**2)**0.5 32 | 33 | 34 | # In[4]: 35 | 36 | 37 | #standard solution to quadratic equation 38 | def solve_quadratic_equation(A,B,C): 39 | x1=(-B+(B**2-4*A*C)**0.5)/(2*A) 40 | x2=(-B-(B**2-4*A*C)**0.5)/(2*A) 41 | return [x1,x2] 42 | 43 | 44 | # In[5]: 45 | 46 | 47 | #analytic geometry to compute target datapoints 48 | def get_datapoint(pivot,measure,length,direction='inner'): 49 | 50 | #for undefined slope 51 | if pivot[0]==measure[0]: 52 | y1=pivot[1]+length 53 | y2=pivot[1]-length 54 | x1=pivot[0] 55 | x2=pivot[0] 56 | 57 | #for general cases 58 | else: 59 | 60 | #get line equation 61 | slope,intercept=get_line_params(pivot[0],pivot[1], 62 | measure[0],measure[1],) 63 | 64 | #solve quadratic equation 65 | A=1 66 | B=-2*pivot[0] 67 | C=pivot[0]**2-length**2/(slope**2+1) 68 | x1,x2=solve_quadratic_equation(A,B,C) 69 | 70 | #get y from line equation 71 | y1=slope*x1+intercept 72 | y2=slope*x2+intercept 73 | 74 | if direction=='inner': 75 | 76 | #take the one between pivot and measure points 77 | datapoint=min([(x1,y1),(x2,y2)], 78 | key=lambda x:euclidean_distance(x,measure)) 79 | else: 80 | 81 | #take the one farther away from measure points 82 | datapoint=max([(x1,y1),(x2,y2)], 83 | key=lambda x:euclidean_distance(x,measure)) 84 | 85 | return datapoint 86 | 87 | 88 | # In[6]: 89 | 90 | 91 | #recursively compute the coordinates of koch curve data points 92 | #to effectively connect the data points,the best choice is to use turtle 93 | #it would be too difficult to connect the dots via analytic geometry 94 | def koch_snowflake(base1,base2,base3,n): 95 | 96 | #base case 97 | if n==0: 98 | return 99 | 100 | else: 101 | 102 | #find mid point 103 | #midpoint between base1 and base2 has to satisfy two conditions 104 | #it has to be on the same line as base1 and base2 105 | #assume this line follows y=kx+b 106 | #the midpoint is (x,kx+b) 107 | #base1 is (α,kα+b),base2 is (δ,kδ+b) 108 | #the euclidean distance between midpoint and base1 should be 109 | #half of the euclidean distance between base1 and base2 110 | #(x-α)**2+(kx+b-kα-b)**2=((α-δ)**2+(kα+b-kδ-b)**2)/4 111 | #apart from x,everything else in the equation is constant 112 | #this forms a simple quadratic solution to get two roots 113 | #one root would be between base1 and base2 which yields midpoint 114 | #and the other would be farther away from base2 115 | #this function solves the equation via (-B+(B**2-4*A*C)**0.5)/(2*A) 116 | #alternatively,you can use scipy.optimize.root 117 | #the caveat is it does not offer both roots 118 | #a wrong initial guess could take you to the wrong root 119 | midpoint=get_datapoint(base1,base2,euclidean_distance(base1,base2)/2) 120 | 121 | #compute the top point of a triangle 122 | #the computation is similar to midpoint 123 | #the euclidean distance between triangle_top and midpoint should be 124 | #one third of the distance between base3 and midpoint 125 | triangle_top=get_datapoint(midpoint,base3, 126 | euclidean_distance(midpoint,base3)/3, 127 | direction='outer') 128 | 129 | #two segment points divide a line into three equal parts 130 | #the computation is almost the same as midpoint 131 | #the euclidean distance between segment1 and base1 132 | #should be one third of the euclidean distance between base2 and base1 133 | segment1=get_datapoint(base1,base2,euclidean_distance(base1,base2)/3) 134 | segment2=get_datapoint(base2,base1,euclidean_distance(base1,base2)/3) 135 | 136 | #compute the nearest segment point of the neighboring line 137 | segment_side_1=get_datapoint(base1,base3,euclidean_distance(base1,base3)/3) 138 | segment_side_2=get_datapoint(base2,base3,euclidean_distance(base2,base3)/3) 139 | 140 | #recursion 141 | yield [segment1,segment2,triangle_top] 142 | yield from koch_snowflake(base1,segment1,segment_side_1,n-1) 143 | yield from koch_snowflake(segment1,triangle_top,segment2,n-1) 144 | yield from koch_snowflake(triangle_top,segment2,segment1,n-1) 145 | yield from koch_snowflake(segment2,base2,segment_side_2,n-1) 146 | 147 | 148 | # In[7]: 149 | 150 | 151 | #set data points 152 | point1=(0,0) 153 | point2=(3,0) 154 | point3=(3/2,3/2*(3**0.5)) 155 | 156 | #due to python floating point arithmetic 157 | #a lot of data points could go wrong during the calculation 158 | #unfortunately there is no panacea to this malaise 159 | #unless we plan to use decimal package all the time 160 | #when the depth of snowflake reaches 1 161 | #one of the data points reach -1.1102230246251565e-16 on x axis 162 | #when the depth of snowflake reaches 6 163 | #we end up with complex root and things go wrong 164 | n=4 165 | 166 | 167 | # In[8]: 168 | 169 | 170 | #collect coordinates 171 | arr=list(koch_snowflake(point1,point2,point3,n))+list( 172 | koch_snowflake(point3,point1,point2,n))+list( 173 | koch_snowflake(point2,point3,point1,n))+[(point1,point2,point3)] 174 | coordinates=[j for i in arr for j in i] 175 | 176 | 177 | # In[9]: 178 | 179 | 180 | #viz 181 | #visually u can tell some of the data points are miscalculated 182 | #purely caused by floating point arithmetic 183 | ax=plt.figure(figsize=(5,5)) 184 | plt.scatter([i[0] for i in coordinates], 185 | [i[1] for i in coordinates],s=0.5) 186 | plt.axis('off') 187 | plt.show() 188 | 189 | 190 | # In[10]: 191 | 192 | 193 | #turtle version of koch snowflake 194 | # https://www.geeksforgeeks.org/koch-curve-koch-snowflake/ 195 | -------------------------------------------------------------------------------- /pythagorean tree.py: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | #fractal is one of the interesting topics in geometry 8 | #it is usually described by a recursive function 9 | #voila,here we are! 10 | import matplotlib.pyplot as plt 11 | 12 | 13 | # In[2]: 14 | 15 | 16 | #compute euclidean distance 17 | def euclidean_distance(point1,point2): 18 | return ((point1[0]-point2[0])**2+(point1[1]-point2[1])**2)**0.5 19 | 20 | 21 | # In[3]: 22 | 23 | 24 | #simple solution to get coefficients of the equation 25 | def get_line_params(x1,y1,x2,y2): 26 | 27 | slope=(y1-y2)/(x1-x2) 28 | intercept=y1-slope*x1 29 | 30 | return slope,intercept 31 | 32 | 33 | # In[4]: 34 | 35 | 36 | #standard solution to quadratic equation 37 | def solve_quadratic_equation(A,B,C): 38 | x1=(-B+(B**2-4*A*C)**0.5)/(2*A) 39 | x2=(-B-(B**2-4*A*C)**0.5)/(2*A) 40 | return [x1,x2] 41 | 42 | 43 | # In[5]: 44 | 45 | 46 | #analytic geometry to compute target datapoints 47 | def get_datapoint(pivot,measure,length,direction='inner'): 48 | 49 | #for undefined slope 50 | if pivot[0]==measure[0]: 51 | y1=pivot[1]+length 52 | y2=pivot[1]-length 53 | x1=pivot[0] 54 | x2=pivot[0] 55 | 56 | #for general cases 57 | else: 58 | 59 | #get line equation 60 | slope,intercept=get_line_params(pivot[0],pivot[1], 61 | measure[0],measure[1],) 62 | 63 | #solve quadratic equation 64 | A=1 65 | B=-2*pivot[0] 66 | C=pivot[0]**2-length**2/(slope**2+1) 67 | x1,x2=solve_quadratic_equation(A,B,C) 68 | 69 | #get y from line equation 70 | y1=slope*x1+intercept 71 | y2=slope*x2+intercept 72 | 73 | if direction=='inner': 74 | 75 | #take the one between pivot and measure points 76 | datapoint=min([(x1,y1),(x2,y2)], 77 | key=lambda x:euclidean_distance(x,measure)) 78 | else: 79 | 80 | #take the one farther away from measure points 81 | datapoint=max([(x1,y1),(x2,y2)], 82 | key=lambda x:euclidean_distance(x,measure)) 83 | 84 | return datapoint 85 | 86 | 87 | # In[6]: 88 | 89 | 90 | #recursively plot symmetric pythagorean tree at 45 degree 91 | # https://larryriddle.agnesscott.org/ifs/pythagorean/pythTree.htm 92 | def pythagorean_tree(ax,top_left,top_right,bottom_left, 93 | bottom_right,current_angle,line_len,n): 94 | 95 | #plot square 96 | ax.add_patch(plt.Rectangle(xy=bottom_left,width=line_len,height=line_len, 97 | angle=current_angle,)) 98 | 99 | if n==0: 100 | return 101 | else: 102 | 103 | #find mid point 104 | #midpoint has to satisfy two conditions 105 | #it has to be on the same line as bottom_left and bottom_right 106 | #assume this line follows y=kx+b 107 | #the midpoint is (x,kx+b) 108 | #bottom_left is (α,kα+b),bottom_right is (δ,kδ+b) 109 | #the euclidean distance between midpoint and bottom_left should be 110 | #half of the euclidean distance between bottom_left and bottom_right 111 | #(x-α)**2+(kx+b-kα-b)**2=((α-δ)**2+(kα+b-kδ-b)**2)/4 112 | #apart from x,everything else in the equation is constant 113 | #this forms a simple quadratic solution to get two roots 114 | #one root would be between bottom_left and bottom_right which yields midpoint 115 | #and the other would be farther away from bottom_right 116 | #this function solves the equation via (-B+(B**2-4*A*C)**0.5)/(2*A) 117 | #alternatively,you can use scipy.optimize.root 118 | #the caveat is it does not offer both roots 119 | #a wrong initial guess could take you to the wrong root 120 | bottom_mid=get_datapoint(bottom_left,bottom_right,line_len/2) 121 | top_mid=get_datapoint(top_left,top_right,line_len/2) 122 | 123 | #compute the top point of a triangle 124 | #the computation is similar to midpoint 125 | #the euclidean distance between triangle_top and top_mid should be 126 | #half of the distance between top_mid and bottom_mid 127 | triangle_top=get_datapoint(top_mid,bottom_mid, 128 | line_len/2,direction='outer') 129 | 130 | #get top left for right square 131 | #the computation is similar to midpoint 132 | #the euclidean distance between triangle_top and rightsq_topleft 133 | #should be the same as the distance between triangle_top and top_left 134 | rightsq_topleft=get_datapoint(triangle_top,top_left, 135 | line_len/(2**0.5),direction='outer') 136 | 137 | #get midpoint of the diagonal between rightsq_topleft and top_right 138 | #the computation is similar to midpoint 139 | #the euclidean distance between rightsq_diag_mid and rightsq_topleft 140 | #should be half of the distance between rightsq_topleft and top_right 141 | rightsq_diag_mid=get_datapoint(top_right,rightsq_topleft,line_len/2) 142 | rightsq_topright=get_datapoint(rightsq_diag_mid,triangle_top, 143 | line_len/2,direction='outer') 144 | 145 | #get top left and right for left square similar to right square 146 | leftsq_topleft=get_datapoint(triangle_top,rightsq_topright, 147 | line_len,direction='outer') 148 | leftsq_topright=get_datapoint(triangle_top,top_right, 149 | line_len/(2**0.5),direction='outer') 150 | 151 | #recursive do the same for left square 152 | pythagorean_tree(ax,leftsq_topleft,leftsq_topright, 153 | top_left,triangle_top,current_angle+45, 154 | line_len/(2**0.5),n-1) 155 | 156 | #recursive do the same for right square 157 | pythagorean_tree(ax,rightsq_topleft,rightsq_topright, 158 | triangle_top,top_right,current_angle-45, 159 | line_len/(2**0.5),n-1) 160 | 161 | 162 | # In[7]: 163 | 164 | 165 | #initialize 166 | top_left=(0,0) 167 | top_right=(1,0) 168 | bottom_left=(0,-1) 169 | bottom_right=(1,-1) 170 | n=5 171 | current_angle=0 172 | line_len=euclidean_distance(top_left,top_right) 173 | 174 | 175 | # In[8]: 176 | 177 | 178 | #viz 179 | ax=plt.figure(figsize=(10,8)).add_subplot(111) 180 | pythagorean_tree(ax,top_left,top_right,bottom_left, 181 | bottom_right,current_angle,line_len,n) 182 | 183 | #limit figure dimension for better viz 184 | plt.ylim(min([bottom_left[1],bottom_right[1]]), 185 | max([top_left[1],top_right[1]])+euclidean_distance( 186 | top_left,top_right)*(n-2) 187 | ) 188 | plt.xlim(min([bottom_left[0],top_left[0]])-euclidean_distance( 189 | top_left,top_right)*(n-1)/2, 190 | max([bottom_right[0],top_right[0]])+euclidean_distance( 191 | top_left,top_right)*(n-1)/2, 192 | ) 193 | 194 | plt.axis('off') 195 | plt.show() 196 | 197 | -------------------------------------------------------------------------------- /pythagorean tree.jl: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # In[1]: 4 | 5 | 6 | #fractal is one of the interesting topics in geometry 7 | #it is usually described by a recursive function 8 | #voila,here we are! 9 | using Plots 10 | 11 | 12 | # In[2]: 13 | 14 | 15 | #create rectangle shape 16 | rectangle(top_left,top_right,bottom_left, 17 | bottom_right)=Shape([(top_left[1],top_left[2]), 18 | (top_right[1],top_right[2]), 19 | (bottom_right[1],bottom_right[2]), 20 | (bottom_left[1],bottom_left[2])]) 21 | 22 | 23 | # In[3]: 24 | 25 | 26 | #compute euclidean distance 27 | function euclidean_distance(point1,point2) 28 | return √((point1[1]-point2[1])^2+(point1[2]-point2[2])^2) 29 | end 30 | 31 | 32 | # In[4]: 33 | 34 | 35 | #simple solution to get coefficients of the equation 36 | function get_line_params(x1,y1,x2,y2) 37 | 38 | slope=(y1-y2)/(x1-x2) 39 | intercept=y1-slope*x1 40 | 41 | return slope,intercept 42 | 43 | end 44 | 45 | 46 | # In[5]: 47 | 48 | 49 | #standard solution to quadratic equation 50 | function solve_quadratic_equation(A,B,C) 51 | x1=(-B+√(B^2-4*A*C))/(2*A) 52 | x2=(-B-√(B^2-4*A*C))/(2*A) 53 | return [x1,x2] 54 | end 55 | 56 | 57 | # In[6]: 58 | 59 | 60 | #analytic geometry to compute target datapoints 61 | function get_datapoint(pivot,measure,length,direction="inner") 62 | 63 | #for undefined slope 64 | if pivot[1]==measure[1] 65 | y1=pivot[2]+length 66 | y2=pivot[2]-length 67 | x1=pivot[1] 68 | x2=pivot[1] 69 | 70 | #for general cases 71 | else 72 | 73 | #get line equation 74 | slope,intercept=get_line_params(pivot[1],pivot[2], 75 | measure[1],measure[2],) 76 | 77 | #solve quadratic equation 78 | A=1 79 | B=-2*pivot[1] 80 | C=pivot[1]^2-length^2/(slope^2+1) 81 | x1,x2=solve_quadratic_equation(A,B,C) 82 | 83 | #get y from line equation 84 | y1=slope*x1+intercept 85 | y2=slope*x2+intercept 86 | 87 | end 88 | 89 | if direction=="inner" 90 | 91 | #take the one between pivot and measure points 92 | if euclidean_distance((x1,y1),measure)euclidean_distance((x2,y2),measure) 102 | datapoint=(x1,y1) 103 | else 104 | datapoint=(x2,y2) 105 | end 106 | 107 | end 108 | 109 | return datapoint 110 | 111 | end 112 | 113 | 114 | # In[7]: 115 | 116 | 117 | #recursively plot symmetric pythagorean tree at 45 degree 118 | # https//larryriddle.agnesscott.org/ifs/pythagorean/pythTree.htm 119 | function pythagorean_tree(top_left,top_right,bottom_left, 120 | bottom_right,current_angle,line_len,n) 121 | 122 | #plot square 123 | plot!(rectangle(top_left,top_right,bottom_left,bottom_right)) 124 | 125 | if n==0 126 | return 127 | else 128 | 129 | #find mid point 130 | #midpoint has to satisfy two conditions 131 | #it has to be on the same line as bottom_left and bottom_right 132 | #assume this line follows y=kx+b 133 | #the midpoint is (x,kx+b) 134 | #bottom_left is (α,kα+b),bottom_right is (δ,kδ+b) 135 | #the euclidean distance between midpoint and bottom_left should be 136 | #half of the euclidean distance between bottom_left and bottom_right 137 | #(x-α)**2+(kx+b-kα-b)**2=((α-δ)**2+(kα+b-kδ-b)**2)/4 138 | #apart from x,everything else in the equation is constant 139 | #this forms a simple quadratic solution to get two roots 140 | #one root would be between bottom_left and bottom_right which yields midpoint 141 | #and the other would be farther away from bottom_right 142 | #this function solves the equation via (-B+(B**2-4*A*C)**0.5)/(2*A) 143 | #alternatively,you can use scipy.optimize.root 144 | #the caveat is it does not offer both roots 145 | #a wrong initial guess could take you to the wrong root 146 | bottom_mid=get_datapoint(bottom_left,bottom_right,line_len/2) 147 | top_mid=get_datapoint(top_left,top_right,line_len/2) 148 | 149 | #compute the top point of a triangle 150 | #the computation is similar to midpoint 151 | #the euclidean distance between triangle_top and top_mid should be 152 | #half of the distance between top_mid and bottom_mid 153 | triangle_top=get_datapoint(top_mid,bottom_mid, 154 | line_len/2,"outer") 155 | 156 | #get top left for right square 157 | #the computation is similar to midpoint 158 | #the euclidean distance between triangle_top and rightsq_topleft 159 | #should be the same as the distance between triangle_top and top_left 160 | rightsq_topleft=get_datapoint(triangle_top,top_left, 161 | line_len/(√(2)),"outer") 162 | 163 | #get midpoint of the diagonal between rightsq_topleft and top_right 164 | #the computation is similar to midpoint 165 | #the euclidean distance between rightsq_diag_mid and rightsq_topleft 166 | #should be half of the distance between rightsq_topleft and top_right 167 | rightsq_diag_mid=get_datapoint(top_right,rightsq_topleft,line_len/2) 168 | rightsq_topright=get_datapoint(rightsq_diag_mid,triangle_top, 169 | line_len/2,"outer") 170 | 171 | #get top left and right for left square similar to right square 172 | leftsq_topleft=get_datapoint(triangle_top,rightsq_topright, 173 | line_len,"outer") 174 | leftsq_topright=get_datapoint(triangle_top,top_right, 175 | line_len/(√(2)),"outer") 176 | 177 | #recursive do the same for left square 178 | pythagorean_tree(leftsq_topleft,leftsq_topright, 179 | top_left,triangle_top,current_angle+45, 180 | line_len/(√(2)),n-1) 181 | 182 | #recursive do the same for right square 183 | pythagorean_tree(rightsq_topleft,rightsq_topright, 184 | triangle_top,top_right,current_angle-45, 185 | line_len/(√(2)),n-1) 186 | 187 | end 188 | 189 | end 190 | 191 | 192 | # In[8]: 193 | 194 | 195 | #initialize 196 | top_left=(0,0) 197 | top_right=(1,0) 198 | bottom_left=(0,-1) 199 | bottom_right=(1,-1) 200 | n=4 201 | current_angle=0 202 | line_len=euclidean_distance(top_left,top_right); 203 | 204 | 205 | # In[9]: 206 | 207 | 208 | #viz 209 | gr(size=(250,200)) 210 | fig=plot(legend=false,grid=false,axis=false,ticks=false, 211 | ) 212 | 213 | pythagorean_tree(top_left,top_right,bottom_left, 214 | bottom_right,current_angle,line_len,n) 215 | fig 216 | 217 | 218 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Recursion and Dynamic Programming 2 | 3 | ## Intro 4 | 5 | On par with many of my repositories, this one didn’t start off with great ambition either. It was merely an archive of LeetCode problems and solutions. For a problem like edit distance, there are two approaches – memoization (top down, solve big problem then the smaller ones) and tabulation (bottom up, solve small problem then the bigger ones). Conventionally, memoization approach is called recursion and tabulation approach is called dynamic programming. They used to be the core features of this repository. 6 | 7 | Oddly enough, this repository didn’t help me beat any interview related to recursion or dynamic programming :joy: but it became entrepôt of my interest in fractal geometry :yum: I suppose I am pretty good at making lemonade when life gives me :lemon: (salute to Riley Reid :stuck_out_tongue_closed_eyes:) Jokes aside, a fractal is a mathematical set that exhibits a repeating pattern that displays at every scale. It can be modeled by using recursive algorithms or L-systems techniques. Fractal patterns will be the core feature of this repository in the foreseeable future. 8 | 9 | Whether you are here for algorithm and data structure or for the niche branch of geometry, I sincerely hope this repository helps you along the way, and perhaps boldly take you somewhere you have never gone before :vulcan_salute: 10 | 11 | ## Recursion 12 | 13 | * Coin Change (Julia, Python) 14 | 15 | * Edit Distance (Julia, Python) 16 | 17 | * Fibonacci (Julia, Python) 18 | 19 | * Hanoi Tower (Julia, Python) 20 | 21 | * Palindrome (Julia, Python) 22 | 23 | * Pascal Triangle (Julia, Python) 24 | 25 | * Prime Factorization (Julia, Python) 26 | 27 | * Stock Trading (Julia, Python) 28 | 29 | ## Fractal Geometry 30 | 31 | * Cantor Set (Julia, Python) 32 | 33 | ![alt text](https://github.com/je-suis-tm/recursion-and-dynamic-programming/blob/master/preview/cantor%20set.png) 34 | 35 | * Hilbert Curve (Julia, Python) 36 | 37 | ![alt text](https://github.com/je-suis-tm/recursion-and-dynamic-programming/blob/master/preview/hilbert%20curve.png) 38 | 39 | * Jerusalem Cross (Julia, Python) 40 | 41 | ![alt text](https://github.com/je-suis-tm/recursion-and-dynamic-programming/blob/master/preview/jerusalem%20cross.png) 42 | 43 | * Koch Snowflake (Julia, Python) 44 | 45 | ![alt text](https://github.com/je-suis-tm/recursion-and-dynamic-programming/blob/master/preview/koch%20snowflake.png) 46 | 47 | * Pythagorean Tree (Julia, Python) 48 | 49 | ![alt text](https://github.com/je-suis-tm/recursion-and-dynamic-programming/blob/master/preview/pythagorean%20tree.png) 50 | 51 | * Sierpiński Carpet (Julia, Python) 52 | 53 | ![alt text](https://github.com/je-suis-tm/recursion-and-dynamic-programming/blob/master/preview/sierpi%C5%84ski%20carpet.png) 54 | 55 | * Sierpiński Triangle (Julia, Python) 56 | 57 | ![alt text](https://github.com/je-suis-tm/recursion-and-dynamic-programming/blob/master/preview/sierpi%C5%84ski%20triangle.png) 58 | 59 | ## Dynamic Programming 60 | 61 | * Coin Change (Julia, Python) 62 | 63 | * Edit Distance (Julia, Python) 64 | 65 | * Egg Drop (Julia, Python) 66 | 67 | * Knapsack (Julia, Python) 68 | 69 | * Knapsack Multiple Choice (Julia, Python) 70 | 71 | * Stock Trading (Julia, Python) 72 | 73 | 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------