Skip to content

Commit 9587ac7

Browse files
committed
[main] Added functions
1 parent 0e2c5f1 commit 9587ac7

File tree

6 files changed

+339
-11
lines changed

6 files changed

+339
-11
lines changed

pythonProblems/dp/boxes.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
2+
from typing import List
3+
4+
5+
class Box:
6+
def __init__(self, width, height, depth):
7+
self.width = width
8+
self.height = height
9+
self.depth = depth
10+
11+
12+
def boxes(stack: List[Box]) -> int:
13+
n = len(stack)
14+
boxes: List[Box] = []
15+
for eachbox in stack:
16+
boxes.append(eachbox) ## the box
17+
boxes.append(Box(min(eachbox.height, eachbox.depth), eachbox.width, max(eachbox.height, eachbox.depth))) ## the box rotated, with width as height, width as min(height, depth) and depth as min(height, depth)
18+
boxes.append(Box(min(eachbox.height, eachbox.width), eachbox.depth, max(eachbox.height, eachbox.width))) ## the box rotated, with depth as height, width as min(height, width), and depth as min(height, width)
19+
n *= 3
20+
21+
boxes.sort(key=lambda b: b.depth * b.width, reverse=True)
22+
23+
msh = [0] * n
24+
25+
for i in range(len(msh)):
26+
msh[i] = boxes[i].height
27+
28+
for i in range(1, n):
29+
for j in range(0, i):
30+
if boxes[i].width < boxes[j].width and boxes[i].depth < boxes[j].depth: ## if the current target box's width is less then the subproblem box width, and depth likewise
31+
if msh[i] < msh[j] + boxes[i].height: ## we can make a taller combination if the current dp target solution is less then the smaller dp solution + the target box height
32+
msh[i] = msh[j] + boxes[i].height ## we assign the target problem the result of subproblem j's solution + the target box's height
33+
34+
return max(msh) ## return the max height, which is the last element in the dp table
35+
36+
37+
if __name__ == '__main__':
38+
arr = [Box(6, 4, 7), Box(2, 1, 3), Box(5, 4, 6), Box(12, 10, 32)]
39+
print(boxes(arr))
Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,60 @@
1+
import random
12
from typing import List
23

34

45
class Solution:
56
def firstMissingPositive(self, nums: List[int]) -> int:
67
min_number = -1
7-
max_number = -1
8-
if 1 not in nums:
8+
max_number = 0
9+
gap_base = -1
10+
for eachnum in nums:
11+
if eachnum > 0:
12+
if min_number == -1:
13+
## initialize all trackers
14+
max_number = eachnum
15+
min_number = eachnum
16+
else:
17+
gap = abs(eachnum - max_number)
18+
gap_base_diff = abs(eachnum - gap_base)
19+
if gap > 1 and ((eachnum > max_number and gap_base == -1) or (eachnum < max_number and gap_base == -1) or (eachnum < max_number and gap_base_diff == 1) or (eachnum < max_number and gap_base_diff > 0 and eachnum < gap_base)): ## before and after
20+
if gap_base_diff == 1:
21+
gap_base = eachnum
22+
elif gap_base == -1:
23+
gap_base = min(max_number, eachnum)
24+
else:
25+
gap_base = min(max_number, eachnum)
26+
min_number = min(min_number, eachnum)
27+
max_number = max(max_number, eachnum)
28+
if min_number != 0 and min_number > 1:
929
return 1
10-
for eachnumber in nums:
11-
if eachnumber < 0:
12-
continue
13-
else:
14-
min_number = eachnumber if min_number == -1 else min(min_number, eachnumber)
15-
max_number = eachnumber if max_number == -1 else max(max_number, eachnumber)
16-
return max_number + 1 if min_number <= 1 else min_number - 1
30+
if gap_base == -1:
31+
## no gaps detected, check max, if max is 0, return 1
32+
if max_number == 0:
33+
return 1
34+
# return max + 1
35+
return max_number + 1
36+
else:
37+
return gap_base + 1
38+
1739

1840

1941
if __name__ == '__main__':
2042
sol = Solution()
21-
nums = [3, 4, -1, 1]
22-
print(sol.firstMissingPositive(nums))
43+
arrs = [
44+
[1,2,0],
45+
[3,4,-1,1],
46+
[7,8,9,11,12],
47+
[1, 2, 3, 50, 48, 5214, 32, 6324, 234, -32, -1231, -432134, -5123, -412243, -44, -324, 0],
48+
[1, 2, 3, 6, 7, 4],
49+
[40, 33, 50, -11, 0, 12, 50, 1, 33, -1, -3, 27, 29, -19],
50+
[1,2,6,3,5,4]
51+
]
52+
# for i in range(5):
53+
# new_arr = []
54+
# for j in range(random.randint(10, 30)):
55+
# new_arr.append(random.randint(-20, 50))
56+
# arrs.append(new_arr)
57+
# new_arr = []
58+
for eacharr in arrs:
59+
print('testing {}'.format(eacharr))
60+
print(sol.firstMissingPositive(eacharr))

pythonProblems/dp/lcs.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from pprint import pprint
2+
from typing import List
3+
4+
def find_subsequence_len(str1: str, str2: str, max_length = 0) -> int:
5+
for ind, elem in enumerate(str1):
6+
if elem in str2:
7+
max_length = max(max_length, 1 + find_subsequence_len(str1[ind + 1:], str2[str2.index(elem) + 1:]))
8+
return max_length
9+
10+
11+
def lcs(substr1: str, substr2: str) -> int:
12+
while len(substr1) > 0 and len(substr2) > 0:
13+
if substr1[-1] != substr2[-1]:
14+
return max(lcs(substr1[:-1], substr2), lcs(substr1, substr2[:-1]))
15+
return 1 + lcs(substr1[:-1], substr2[:-1])
16+
return 0
17+
18+
19+
if __name__ == '__main__':
20+
x = 'abcbdab'
21+
y = 'bdcaba'
22+
print(lcs(x, y))
23+
24+
25+

pythonProblems/dp/needleman.py

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
from random import randint
2+
3+
# Constant values to be used for the scoring grid in the Needleman-Wunsch algorithm
4+
IDENTITY = 4
5+
TRANSITION = -2
6+
TRANSVERSION = -3
7+
GAP = -8
8+
SCORES = { # scoring matrix for the Needleman-Wunsch algorithm
9+
"A" : {
10+
"A" : IDENTITY,
11+
"T" : TRANSVERSION,
12+
"C" : TRANSVERSION,
13+
"G" : TRANSITION
14+
},
15+
"T" : {
16+
"A" : TRANSVERSION,
17+
"T" : IDENTITY,
18+
"C" : TRANSITION,
19+
"G" : TRANSVERSION
20+
},
21+
"C" : {
22+
"A" : TRANSVERSION,
23+
"T" : TRANSITION,
24+
"C" : IDENTITY,
25+
"G" : TRANSVERSION
26+
},
27+
"G" : {
28+
"A" : TRANSITION,
29+
"T" : TRANSVERSION,
30+
"C" : TRANSVERSION,
31+
"G" : IDENTITY
32+
}
33+
}
34+
35+
# left, up, diagonal, used to fill direction matrix path for backpropagation
36+
DIRECTIONS = ("DIAG", "LEFT", "UP")
37+
38+
def get_seq() -> tuple[str, str]:
39+
"""Prompt user for sequences to be used in the Needleman-Wunsch algorithm,
40+
then read and format sequences.
41+
42+
Returns a tuple of cleaned DNA sequence strings.
43+
"""
44+
seq = ["", ""]
45+
path = input("Please enter a filepath containing two sequences:\n").strip()
46+
47+
try:
48+
with open(path, "r", encoding='utf-8') as f:
49+
lines = f.readlines()
50+
idx_1, idx_2 = None, None
51+
for i in range(len(lines)):
52+
if lines[i].startswith(">"):
53+
if idx_1 == None:
54+
idx_1 = i
55+
elif idx_2 == None:
56+
idx_2 = i
57+
break
58+
59+
# Format txt file input of FASTA seq into a continuous string of nucleotides
60+
try:
61+
seq[0] = "".join(lines[idx_1+1:idx_2]).upper().replace("\n", "")
62+
seq[1] = "".join(lines[idx_2+1:]).upper().replace("\n", "")
63+
except TypeError as e:
64+
print(e, "\nFile must be in the form of\n>SEQUENCE 1 NAME\nAGTC ... GTA\n>SEQUENCE 2 NAME\nTGAT ... CCA")
65+
except FileNotFoundError as e:
66+
print("ERROR READING FILEPATH: ", e)
67+
68+
69+
return seq
70+
71+
def make_matrix(seq_1:str, seq_2:str, make_dir:bool) -> list[list]:
72+
"""Create grid of values to be used for the Needleman-Wunsch algorithm
73+
This matrix is in the form of:
74+
[
75+
[_, _, A, T, A, ..., G]
76+
[_, 0, _, _, _, ..., _]
77+
...
78+
[A, _, _, _, _, ..., _]
79+
]
80+
81+
if make_dir is True, the [1][1] position contains the string 'DONE' to halt
82+
back propagation, and the first row/col axes have 'LEFT' and 'UP' directions,
83+
respectively
84+
85+
otherwise, fill the valued matrix alongside the first row/col by multiples
86+
of the GAP penalty
87+
"""
88+
89+
# initialize empty array
90+
mat = [ [None]*(len(seq_2) + 2) for i in range(len(seq_1) + 2) ]
91+
92+
if make_dir: # fill direction matrix
93+
# make seq 1 exist along the y axis (rows)
94+
for i in range(len(seq_1)):
95+
mat[i+2][0] = seq_1[i]
96+
mat[i+2][1] = DIRECTIONS[0]
97+
98+
# make seq 2 exist along the x axis (cols)
99+
for i in range(len(seq_2)):
100+
mat[0][i+2] = seq_2[i]
101+
mat[1][i+2] = DIRECTIONS[1]
102+
else: # fill value matrix
103+
# make seq 1 exist along the y axis (rows)
104+
for i in range(len(seq_1)):
105+
mat[i+2][0] = seq_1[i]
106+
mat[i+2][1] = GAP*(i+1)
107+
108+
# make seq 2 exist along the x axis (cols)
109+
for i in range(len(seq_2)):
110+
mat[0][i+2] = seq_2[i]
111+
mat[1][i+2] = GAP*(i+1)
112+
113+
mat[1][1] = "DONE" if make_dir else 0
114+
115+
return mat
116+
117+
def fill_matrix(mat:list[list], mat_dir:list[list], x:int, y:int, max_x:int, max_y:int) -> None:
118+
"""Uses the scores matrix to fill values and directions for the Needleman-Wunsch algorithm grid"""
119+
# end if x or y positions are out of bounds or mat not initialized
120+
if mat == None or x > max_x or y > max_y:
121+
return
122+
# end if None is at the current position, the left, or above
123+
if mat[y][x] != None or mat[y-1][x] == None or mat[y][x-1] == None:
124+
return
125+
126+
# find the score that corresponds to the index/column of current position
127+
score = SCORES[mat[y][0]] [mat[0][x]]
128+
129+
# get list of the 3 possible new values
130+
values = [
131+
mat[y-1][x-1] + score, # value coming from diagonal,
132+
mat[y][x-1] + GAP, # value coming from left
133+
mat[y-1][x] + GAP, # value coming from up
134+
]
135+
136+
max_val = max(values) # max val
137+
max_idx = values.index(max_val) # index corresponds to the back-propagation movement, prioritizes first index (diagonal)
138+
if max_idx > 0 and values[1] == values [2]:
139+
max_idx = randint(1,2) # if theres a tie between left and up, randomly pick one
140+
141+
mat[y][x] = max_val # make position's value the best scoring outcome
142+
mat_dir[y][x] = DIRECTIONS[max_idx] # make positions value the backprop direction
143+
144+
fill_matrix(mat, mat_dir, x+1, y, max_x, max_y) # recusively fill values by moving right
145+
fill_matrix(mat, mat_dir, x, y+1, max_x, max_y) # recursively fill values by moving down
146+
147+
def get_alignment(mat:list[list], mat_dir:list[list]) -> tuple[str, str, int]:
148+
"""Uses a filled direction matrix and uses the Needleman-Wunsch algorithm to get the
149+
best scoring alignment via backpropogation. The maximum value is also obtained from
150+
the valued matrix
151+
152+
Function returns a tuple containing an optimal alignment for seq1, seq2, and the best
153+
score associated with the alignment"""
154+
x = len(mat[0]) -1
155+
y = len(mat) -1
156+
best_score = mat[y][x]
157+
158+
align_1 = "" # vertical seq
159+
align_2 = "" # horizontal seq
160+
while mat_dir[y][x] != "DONE":
161+
direction = mat_dir[y][x]
162+
163+
if direction == "DIAG":
164+
# backpropagation must move diagonally
165+
align_1 = mat_dir[y][0] + align_1
166+
align_2 = mat_dir[0][x] + align_2
167+
x -= 1
168+
y -= 1
169+
elif direction == "LEFT":
170+
# backpropagation must move left
171+
align_1 = "-" + align_1
172+
align_2 = mat_dir[0][x] + align_2
173+
x -=1
174+
else:
175+
# backpropagation must move up
176+
align_1 = mat_dir[y][0] + align_1
177+
align_2 = "-" + align_2
178+
y -=1
179+
180+
return (align_1, align_2, best_score)
181+
182+
def print_align_results(align_1:str, align_2:str, score:int) -> None:
183+
"""Takes the aligned sequences strings and best score, then prints out the results"""
184+
print(f"SEQUENCE 1:\t{align_1}")
185+
print(f"SEQUENCE 2:\t{align_2}")
186+
print("Alignment Score:", score)
187+
188+
def print_mat(mat:list[list]) -> None:
189+
"""cleanly prints a provided matrix"""
190+
for row in mat:
191+
for i in row:
192+
print("_" if i == None else i, end="\t")
193+
print("\n")
194+
195+
if __name__ == "__main__":
196+
while True:
197+
seq1, seq2 = get_seq()
198+
if seq1 == "" or seq2 == "":
199+
print("Inputs contain invalid sequences, please check your FASTA files.")
200+
else:
201+
mat = make_matrix(seq1, seq2, make_dir=False)
202+
mat_dir = make_matrix(seq1, seq2, make_dir=True)
203+
204+
fill_matrix(mat, mat_dir, 2, 2, len(mat[0])-1, len(mat)-1)
205+
206+
align_1, align_2, score = get_alignment(mat, mat_dir)
207+
208+
print("############ DYNAMIC PROGRAMMING TABLE ############\n")
209+
print_mat(mat)
210+
print_mat(mat_dir)
211+
212+
print("############ ALIGNMENT RESULTS ############\n")
213+
print_align_results(align_1, align_2, score)
214+
215+
if input("Would you like to align sequences again? (y/n)").strip().lower() == "y":
216+
continue
217+
else:
218+
break

pythonProblems/dp/test.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
>Sequence 1
2+
taccagta
3+
>Sequence 2
4+
aggcat

pythonProblems/dp/test2.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
>Sequence 1
2+
atcg
3+
>Sequence 2
4+
aattccgg

0 commit comments

Comments
 (0)