Python DP min cost path of a triangle of ints - python

I am working on some code to compute the shortest path in a given 2D array that represents a triangle. This is using DP to get to the solution, it needs to output a path rather than the final cost. I understand that it is populating the array bottom-up style, but I will implement back-tracking once this outputs the correct cost.
This is the code i currently have. For some reason it is just concatenating the numbers rather than splitting it like adding a string together.
with open("small_triangle.txt") as textFile:
perm = [line.split() for line in textFile]
output = [None] * len(perm)
lines = perm
for j in range(len(lines)):
print(lines[j])
output[0] = lines[0][0]
for x in range(len(lines)-2, 0, -1):
for y in range(0 , x):
if(lines[x+1][y] < lines[x+1][y+1]):
output[x] = lines[x+1][y]
lines[x][y] += lines[x+1][y]
else:
output[x] = lines[x+1][y+1]
lines[x][y] += lines[x+1][y+1]
print(output)
#for i in range(len(output)):
# print("\n"+output[i])
given this 2D array
['79']
['82', '04']
['86', '93', '64']
['34', '30', '17', '44']
['41', '79', '83', '33', '86']
['10', '34', '55', '92', '26', '23']
['54', '84', '30', '79', '40', '30', '65']
['94', '64', '79', '36', '79', '78', '72', '36']
['12', '88', '25', '57', '72', '37', '37', '45', '26']
['92', '24', '07', '07', '04', '48', '25', '60', '54', '72']
This is the array it is outputting
['79', '8630793430365704', '17332630723725', '332630723725', '2630723725', '30723725', '723725', '3725', '54', None]

Your triangle contains strings instead of integers. To fix that we will need to cast all those strings to int before doing any operations on them, otherwise the + operation will concat and the < comparison operator will do a dictionary comparison on strings.
with open("small_triangle.txt") as textFile:
perm = [[int(x) for x in line.split()] for line in textFile]
Although, your algorithm is wrong. I will not fix it all for you, but I can point out the main error. Have a look at your loop
for x in range(len(lines)-2, 0, -1):
for y in range(0 , x):
if(lines[x+1][y] < lines[x+1][y+1]):
# On every iteration that overwrites output[x]
output[x] = lines[x+1][y]
lines[x][y] += lines[x+1][y]
else:
# On every iteration that overwrites output[x]
output[x] = lines[x+1][y+1]
lines[x][y] += lines[x+1][y+1]
Notice that for a given x, you change the value of output[x] on every iteration of for y in range(0 , x). So basically what is going on is that you are not keeping track of what your best answer is, you are simply overwriting it.
What you want to do for a dynamic programming solution is to fill in, from bottom to top, what the minimal distance to the bottom and the associated path are for every element. Then you should end up with a solution at the top of your triangle.

Related

how can I combine list elements inside list based on element value?

If I want to combine lists inside list based on element value how can I achieve that?
suppose if list
lis = [['steve','reporter','12','34','22','98'],['megan','arch','44','98','32','22'],['jack','doctor','80','32','65','20'],['steve','dancer','66','31','54','12']]
here list containing 'steve' appears twice so I want to combine them as below
new_lis = [['steve','reporter','12','34','22','98','dancer','66','31','54','12'],['megan','arch','44','98','32','22'],['jack','doctor','80','32','65','20']]
I tried below code to achieve this
new_dic = {}
for i in range(len(lis)):
name = lis[i][0]
if name in new_dic:
new_dic[name].append([lis[i][1],lis[i][2],lis[i][3],lis[i][4],lis[i][5]])
else:
new_dic[name] = [lis[i][1],lis[i][2],lis[i][3],lis[i][4],lis[i][5]]
print(new_dic)
I ended up creating a dictionary with multiple values of lists as below
{'steve': ['reporter', '12', '34', '22', '98', ['dancer', '66', '31', '54', '12']], 'megan': ['arch', '44', '98', '32', '22'], 'jack': ['doctor', '80', '32', '65', '20']}
but I wanted it as single list so I can convert into below format
new_lis = [['steve','reporter','12','34','22','98','dancer','66','31','54','12'],['megan','arch','44','98','32','22'],['jack','doctor','80','32','65','20']]
is there a way to tackle this in different way?
There is a differnet way to do it using groupby function from itertools. Also there are ways to convert your dict to a list also. It totally depends on what you want.
from itertools import groupby
lis = [['steve','reporter','12','34','22','98'],['megan','arch','44','98','32','22'],['jack','doctor','80','32','65','20'],['steve','dancer','66','31','54','12']]
lis.sort(key = lambda x: x[0])
output = []
for name , groups in groupby(lis, key = lambda x: x[0]):
temp_list = [name]
for group in groups:
temp_list.extend(group[1:])
output.append(temp_list)
print(output)
OUTPUT
[['jack', 'doctor', '80', '32', '65', '20'], ['megan', 'arch', '44', '98', '32', '22'], ['steve', 'reporter', '12', '34', '22', '98', 'dancer', '66', '31', '54', '12']]
Not sure whether this snippet answers your question or not. This is not a fastest approach in terms to time complexity. I will update this answer if I can solve in a better way.
lis = [['steve','reporter','12','34','22','98'],['megan','arch','44','98','32','22'],['jack','doctor','80','32','65','20'],['steve','dancer','66','31','54','12']]
new_lis = []
element_value = 'steve'
for inner_lis in lis:
if element_value in inner_lis:
if not new_lis:
new_lis+=inner_lis
else:
inner_lis.remove(element_value)
new_lis+=inner_lis
lis.remove(inner_lis)
print([new_lis] + lis)
Output
[['steve', 'reporter', '12', '34', '22', '98', 'dancer', '66', '31', '54', '12'], ['megan', 'arch', '44', '98', '32', '22'], ['jack', 'doctor', '80', '32', '65', '20']]

Transform a 2D nested list, which includes other data types, to integers in Python

I am new to the coding/ computer science world and trying to learn Python. If anyone could help with the below. I am trying to iterate through the two dimensional list and convert all the strings, which are numbers, and convert them into integers. I am getting an error when using the int() method due to the fact that some of the strings are names. Any help would be appreciated.
My 2D nested array is;
examRes = [['joe bloggs', '45', '46', '52', '83'],
['bobby uncle', '52', '45', '85', '63'],
['luke van', '69', '48', '45', '22']]
You can do something along the following lines:
examRes = [['joe bloggs', '45', '46', '52', '83'],['bobby uncle', '52', '45', '85', '63'],['luke van', '69', '48', '45', '22']]
for lst in examRes:
for i, val in enumerate(lst):
if val.isdigit():
lst[i] = int(val)
More generally, if you had floats or negative ints, you could just do:
try:
lst[i] = int(val)
except ValueError:
pass
in the inner loop. In this particular case, however, where you know the positions of your names and grades, you can simply use slice assignment:
for lst in examRes:
lst[1:] = map(int, lst[1:])

Modify numbers inside list with lambda and map functions in Python 3

for homework I need to create a function that receives 2 list of numbers and every time a number from the first list matches with one of the second one I have to add 10 to the number in the first list. Here's what I've tried.
def modifyEspecials(mylist,especials):
return list(map(lambda n: str(int(n)+10) if map(lambda x: True if n==x else False,especials)== True else n,mylist))
if __name__ == "__main__":
especials=[*range(39,48),*range(58,63),*range(91,97)]
mylist= ['72', '45', '41', '56', '46', '56', '49', '45', '48', '41', '39', '46', '71', '52', '46', '56', '52', '46', '42']
modifiedList= modifyEspecials(mylist,especials)
print(modifiedList)
This returns the same list.
Note: I can't create variables inside modifyEspecials() in this homework and I'm only allowed to import ascii_lowercase, ascii_uppercase and functools or for/while loops
So if you have to use this overcomplicated methods to do so, there is 2 mistakes you do:
In the second map you compare string of int with ints
The second map returns an iterator of size especials
In order to overcome the first issue just transform to int, for the second issue just sum all elements after transforming to list, if there is at least 1 True than you should add 10
See the following fix:
def modifyEspecials(mylist,especials):
return list(map(lambda n: str(int(n)+10) if sum(list(map(lambda x: True if int(n)==x else False, especials))) > 0 else n, mylist))
if __name__ == "__main__":
especials=[*range(39,48),*range(58,63),*range(91,97)]
mylist= ['72', '45', '41', '56', '46', '56', '49', '45', '48', '41', '39', '46', '71', '52', '46', '56', '52', '46', '42']
modifiedList= modifyEspecials(mylist,especials)
print(modifiedList)
Note Although map and lambda are powerful, its not always advisable to use them where there is no need to.

Search exact string in list

I am doing an exercise where I need to search the exact function name from the fun list and get the corresponding information from another list detail.
Here is the dynamic list detail:
csvCpReportContents =[
['[PLT] rand (DEBUG INFO NOT FOUND)', '11', '15'],
['rand', '10', '11', '12'],
['__random_r', '23', '45'],
['__random', '10', '11', '12'],
[],
['multiply_matrices()','23','45'] ]
Here is fun list contains function name to be searched:
fun = ['multiply_matrices()','__random_r','__random']
Expected Output for function fun[2]
['__random', '10', '11', '12']
Expected Output for function fun[1]
['__random_r', '23', '45'],
Here what I have tried for fun[2]:
for i in range(0, len(csvCpReportContents)):
row = csvCpReportContents[i]
if len(row)!=0:
search1 = re.search("\\b" + str(fun[2]).strip() + "\\b", str(row))
if search1:
print(csvCpReportContents[i])
Please suggest to me how to search for the exact word and fetch only that information.
for each fun function you can just iterate through the csv list checking if the first element starts with it
csvCpReportContents = [
['[PLT] rand (DEBUG INFO NOT FOUND)', '11', '15'],
['rand', '10', '11', '12'],
[],
['multiply_matrices()', '23', '45']]
fun=['multiply_matrices()','[PLT] rand','rand']
for f in fun:
for c in csvCpReportContents:
if len(c) and c[0].startswith(f):
print(f'fun function {f} is in csv row {c}')
OUTPUT
fun function multiply_matrices() is in csv row ['multiply_matrices()', '23', '45']
fun function [PLT] rand is in csv row ['[PLT] rand (DEBUG INFO NOT FOUND)', '11', '15']
fun function rand is in csv row ['rand', '10', '11', '12']
Updated code since you changed the test cases and requirement in the question. My first answer was based on your test cases that you wanted to match lines that started with item from fun. Now you seem to have changed that requirement to match an exact match and if not exact match match a starts with match. Below code updated to handle that scenario. However i would say next time be clear in your question and dont change the criteria after several people have answered
csvCpReportContents =[
['[PLT] rand (DEBUG INFO NOT FOUND)', '11', '15'],
['rand', '10', '11', '12'],
['__random_r', '23', '45'],
['__random', '10', '11', '12'],
[],
['multiply_matrices()','23','45'] ]
fun = ['multiply_matrices()','__random_r','__random','asd']
for f in fun:
result = []
for c in csvCpReportContents:
if len(c):
if f == c[0]:
result = c
elif not result and c[0].startswith(f):
result = c
if result:
print(f'fun function {f} is in csv row {result}')
else:
print(f'fun function {f} is not vound in csv')
OUTPUT
fun function multiply_matrices() is in csv row ['multiply_matrices()', '23', '45']
fun function __random_r is in csv row ['__random_r', '23', '45']
fun function __random is in csv row ['__random', '10', '11', '12']
fun function asd is not vound in csv
above input is nested list, so you have to consider 2D Indexing such as
l = [[1,2,3,4],[2,5,7,9]]
for finding 3 number element
you have to use the index of l[0][2]
With custom search_by_func_name function:
csvCpReportContents = [
['[PLT] rand (DEBUG INFO NOT FOUND)', '11', '15'],
['rand', '10', '11', '12'],
[],
['multiply_matrices()', '23', '45']]
fun = ['multiply_matrices()', '[PLT] rand', 'rand']
def search_by_func_name(name, content_list):
for lst in content_list:
if any(i.startswith(name) for i in lst):
return lst
print(search_by_func_name(fun[1], csvCpReportContents)) # ['[PLT] rand (DEBUG INFO NOT FOUND)', '11', '15']
print(search_by_func_name(fun[2], csvCpReportContents)) # ['rand', '10', '11', '12']
You can also use call_fun function as I did in the below code.
def call_fun(fun_name):
for ind,i in enumerate(csvCpReportContents):
if i:
if i[0].startswith(fun_name):
return csvCpReportContents[ind]
# call_fun(fun[2])
# ['rand', '10', '11', '12']

using a for loop to compare lists

The problem at hand is I have a list of lists that I need to iterate through and compare one by one.
def stockcheck():
stock = open("Stock.csv", "r")
reader = csv.reader(stock)
stockList = []
for row in reader:
stockList.append(row)
The output from print(stockList) is:
[['Product', 'Current Stock', 'Reorder Level', 'Target Stock'], ['plain blankets', '5', '10', '50'], ['mugs', '15', '20', '120'], ['100m rope', '60', '15', '70'], ['burner', '90', '20', '100'], ['matches', '52', '10', '60'], ['bucket', '85', '15', '100'], ['spade', '60', '10', '65'], ['wood', '100', '10', '200'], ['sleeping bag', '50', '10', '60'], ['chair', '30', '10', '60']]
I've searched the basics for this but i've had no luck... I'm sure the solution is simple but it's escaping me! Essentially I need to check whether the current stock is less than the re-order level, and if it is save it to a CSV (that part I can do no problem).
for item in stockList:
if stockList[1][1] < stockList[1][2]:
print("do the add to CSV jiggle")
This is as much as I can do but it doesn't iterate through... Any ideas? Thanks in advance!
Iterate through the stockList using list comprehension, maybe and then print out the results
[sl for sl in stockList[1:] if sl[1] < sl[2]]
You will get the following results:
[['mugs', '15', '20', '120']]
In case you were wondering stockList[1:] is to ensure that you ignore the header.
However, you must note that the values are strings that are being compared. Hence, the values are compared char by char. If you want integer comparisons then you must convert the strings to integers, assuming you are absolutely sure that sl[1] and sl[2] will always be integers - just being presented as strings. Just try doing:
[sl for sl in stockList[1:] if int(sl[1]) < int(sl[2])]
The result changes:
[['plain blankets', '5', '10', '50'], ['mugs', '15', '20', '120']]
Use the [1:] to not get the header, and then make the comparation.
for item in stockList[1:]:
if item[1] < item[2]:
print item
print("do the add to CSV jiggle")

Categories