Improving runtime of truck pick up and drop off - python
If anyone can help with improving the runtime that would be great!
I have a truck that has a max capacity of C and a beginning stock on it of S1 The truck goes through a fixed route Depot --> 1 --> 2 --> ... --> N-1 --> N --> Depot
Each station i=1…n has a current stock items of Xi and the objective stock items of Xi* At each station the truck can decide to drop-off or take the amount of items possible according to the situation. Let Yi be the number of items left after the truck visited station i The total cost is TC (as written in the code).
I implemented a dynamic programming code whereas xd is the number of units taken or dropped at each station and s is the number of items on the truck:
run on -min(c-s,xi)<= xd <= s: f(i,s) = f(i+1, s-xd) - so if xd is in minus it means the truck took items from a station.
this is the code - the problem is that it's running for days and not returning an answer.
anyone know a way to implement it better?
n = 50
c=10
s1 = 6
xi = [59,33,14,17,26,31,91,68,3,53,53,73,86,24,98,37,55,14,97,61,57,23,65,24,50,31,39,31,24,60,92,80,48,28,47,81,19,82,3,74,50,89,86,37,98,11,12,94,6,61]
x_star = [35,85,51,88,44,20,79,68,97,7,68,19,50,19,42,45,8,9,61,60,80,4,96,57,100,22,2,51,56,100,6,84,96,69,18,31,86,6,39,6,78,73,14,45,100,43,89,4,76,70]
c_plus = [4.6,1.3,2.7,0.5,2.7,5,2.7,2.6,4.1,4,3.2,3.1,4.8,3.1,0.8,1,0.5,5,5,4.6,2.5,4.1,2.1,2.9,1.4,3.9,0.5,1.7,4.9,0.6,2.8,4.9,3.3,4.7,3.6,2.4,3.4,1.5,1.2,0.5,4.3,4.3,3.9,4.8,1.2,4.8,2,2.2,5,4.5]
c_minus = [8.7,7.5,11.7,6.9,11.7,14.4,7.5,11.1,1.2,1.5,12,8.1,2.7,8.7,9.3,1.5,0.3,1.5,1.2,12.3,5.7,0.6,8.7,8.1,0.6,3.9,0.3,5.4,14.7,0,10.8,6.6,8.4,9.9,14.7,2.7,1.2,10.5,9.3,14.7,11.4,5.4,6,13.2,3.6,7.2,3,4.8,9,8.1]
dict={}
values={}
def tc(i,xd):
yi = xi[i-1] + xd
if yi>=x_star[i-1]:
tc = c_plus[i-1]*(yi-x_star[i-1])
else:
tc = c_minus[i-1]*(x_star[i-1]-yi)
return tc
def func(i,s):
if i==n+1:
return 0
else:
a=[]
b=[]
start = min(c-s,xi[i-1])*-1
for xd in range(start,s+1):
cost = tc(i,xd)
f= func(i+1,s-xd)
a.append(cost+f)
b.append(xd)
min_cost = min(a)
index = a.index(min_cost)
xd_optimal = b[index]
if i in values:
if values[i]>min_cost:
dict[i] = xd_optimal
values[i] = min_cost
else:
values[i] = min_cost
dict[i] = xd_optimal
return min_cost
best_cost = func(1,s1)
print best_cost
print dict
First, the solution:
The function is called very often with exactly the same parameters. Thus, I added a cache that avoids repeating the calculations for recurring parameter sets. This returns the answer almost instantly on my computer.
cache = {}
def func(i,s):
if i==n+1:
return 0
else:
try:
return cache[(i,s)]
except KeyError:
pass
a=[]
...
cache[(i,s)] = min_cost
return min_cost
And here is how I found out what to do...
I modified your code to produce some debug output:
...
count = 0
def func(i,s):
global count
count += 1
print count, ':', i, s
...
Setting n to 2 results in the following output:
1 : 1 6
2 : 2 10
3 : 3 10
4 : 3 9
5 : 3 8
6 : 3 7
7 : 3 6
8 : 3 5
9 : 3 4
10 : 3 3
11 : 3 2
12 : 3 1
13 : 3 0
14 : 2 9
15 : 3 10
16 : 3 9
17 : 3 8
18 : 3 7
19 : 3 6
20 : 3 5
21 : 3 4
22 : 3 3
23 : 3 2
24 : 3 1
25 : 3 0
26 : 2 8
27 : 3 10
28 : 3 9
29 : 3 8
30 : 3 7
31 : 3 6
32 : 3 5
...
You will notice that the function is called very often with the same set of parameters.
After (i=2, s=10) it runs through all combinations of (i=3, s=x). It does that again after (i=2, s=9). The whole thing finishes after 133 recursions. Setting n=3 takes 1464 recursions, and setting n=4 takes 16105 recursions. You can see where that leads to...
Remark: I have absolutely no idea how your optimization works. Instead I simply treated the symptoms :)
Related
Python string length generate output
I am trying to create ruler by print, it should look like this for input value 5: Im trying to change in my code numbers to symbols, my code is: length = str(input("Enter the ruler length = ")) def ruler(string): top = [] top_out = [] bottom = [] for i in range(length): top.append((i+1)//10) bottom.append((i+1)%10) for i in range(length): if ((i+1)//10) == 0: top_out.append(" ") elif (((i+1)//10) in list(sorted(set(top)))) and (((i+1)//10) not in top_out): top_out.append(((i+1)//10)) else: top_out.append(" ") print (''.join(list(map(str, top_out)))) print (''.join(list(map(str,bottom)))) print (string) How to correct it to get appropriate output format of a ruler?
Ruler printing can be down by pretty small function like this, def print_ruler(n): print('|....'*(n)+'|') print(''.join(f'{i:<5}' for i in range(n+1))) Execution: In [1]: print_ruler(5) |....|....|....|....|....| 0 1 2 3 4 5 In [2]: print_ruler(10) |....|....|....|....|....|....|....|....|....|....| 0 1 2 3 4 5 6 7 8 9 10 In [3]: print_ruler(15) |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....| 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 For double-digit numbers, It doesn't come to the center. For ex: For 12, | align with number 1 or 2 it can't not make into the center of 12
How to delete a matrix cell's neighbors which are the same value with it
I have a matrix as shown below (taken from a txt file with an argument), and every cell has neighbors. Once you pick a cell, that cell and all neighboring cells that containing the same number will disappear. 1 0 4 7 6 8 0 5 4 4 5 5 2 1 4 4 4 6 4 1 3 7 4 4 I've tried to do this with using recursion. I separated function four parts which are up(), down() , left() and right(). But I got an error message: RecursionError: maximum recursion depth exceeded in comparison cmd=input("Row,column:") cmdlist=command.split(",") row,column=int(cmdlist[0]),int(cmdlist[1]) num=lines[row-1][column-1] def up(x,y): if lines[x-2][y-1]==num and x>1: left(x,y) right(x,y) lines[x-2][y-1]=None def left(x,y): if lines[x-1][y-2]==num and y>1: up(x,y) down(x,y) lines[x-1][y-2]=None def right(x,y): if lines[x-1][y]==num and y<len(lines[row-1]): up(x,y) down(x,y) lines[x-1][y]=None def down(x,y): if lines[x][y-1]==num and x<len(lines): left(x,y) right(x,y) lines[x][y-1]=None up(row,column) down(row,column) for i in lines: print(str(i).strip("[]").replace(",","").replace("None"," ")) When I give the input (3,3) which represents the number of "4", the output must be like this: 1 0 7 6 8 0 5 5 5 2 1 6 4 1 3 7 I don't need fixed code, just the main idea will be enough. Thanks a lot.
Recursion error happens when your recursion does not terminate. You can solve this without recursing using set's of indexes: search all indexes that contain the looked for number into all_num_idx add the index you are currently at (your input) to a set tbd (to be deleted) loop over the tbd and add all indexed from all_num_idx that differ only in -1/+1 in row or col to any index thats already in the set do until tbd does no longer grow delete all indexes from tbd: t = """4 0 4 7 6 8 0 5 4 4 5 5 2 1 4 4 4 6 4 1 3 7 4 4""" data = [k.strip().split() for k in t.splitlines()] row,column=map(int,input("Row,column:").strip().split(";")) num = data[row][column] len_r =len(data) len_c = len(data[0]) all_num_idx = set((r,c) for r in range(len_r) for c in range(len_c) if data[r][c]==num) tbd = set( [ (row,column)] ) # inital field tbd_size = 0 # different size to enter while done = set() # we processed those already while len(tbd) != tbd_size: # loop while growing tbd_size=len(tbd) for t in tbd: if t in done: continue # only 4-piece neighbourhood +1 or -1 in one direction poss_neighbours = set( [(t[0]+1,t[1]), (t[0],t[1]+1), (t[0]-1,t[1]), (t[0],t[1]-1)] ) # 8-way neighbourhood with diagonals # poss_neighbours = set((t[0]+a,t[1]+b) for a in range(-1,2) for b in range(-1,2)) tbd = tbd.union( poss_neighbours & all_num_idx) # reduce all_num_idx by all those that we already addded all_num_idx -= tbd done.add(t) # delete the indexes we collected for r,c in tbd: data[r][c]=None # output for line in data: print(*(c or " " for c in line) , sep=" ") Output: Row,column: 3,4 4 0 7 6 8 0 5 5 5 2 1 6 4 1 3 7 This is a variant of a "flood-fill-algorythm" flooding only cells of a certain value. See https://en.wikipedia.org/wiki/Flood_fill
Maybe you should replace def right(x,y): if lines[x-1][y]==num and y<len(lines[row-1]): up(x,y) down(x,y) lines[x-1][y]=None by def right(x,y): if lines[x-1][y]==num and y<len(lines[row-1]): lines[x-1][y]=None up(x - 1,y) down(x - 1,y) right(x - 1, y) and do the same for all the other functions. Putting lines[x-1][y]=None ensure that your algorithm stops and changing the indices ensure that the next step of your algorithm will start from the neighbouring cell.
Sampling a matrix with conditions (no zeros or repeated columns)
In case you are interested in the background of the question, I'm thinking how to solve this post- incidentally, if you solve it there, I'll just erase this question. Ideally, I'd like to get an analytical or algebraic solution (constrained non-capturing rook problem), but short of that I'd like a simulation. Incidentally, I posted a related question without as much detail, in case it is easier to tackle. But you don't have to leave this page. Basically there are pairings of two lists of soccer teams, and some pairings are good, while others are forbidden by the rules. This is the matrix: So to generate multiple samplings to match the teams on the row names (to the left) with the column names of opposing teams (at the top), I have to come up with a conditional sampling procedure, but I have no clue how to. This is what I have attempted so far: BCN = c(0,2,3,4,0,0,7,8) ATL = c(0,0,3,4,5,0,7,8) DOR = c(0,0,3,4,5,6,7,0) MON = c(1,2,3,0,5,6,7,0) ARS = c(1,2,3,0,0,6,7,8) LEI = c(1,2,3,4,0,6,0,8) JUV = c(1,2,3,4,5,0,7,8) NAP = c(1,2,0,4,5,6,7,8) chessboard = t(as.matrix(data.frame(BCN, ATL, DOR, MON, ARS, LEI, JUV, NAP))) colnames(chessboard) = c("MAD", "BYN", "BEN", "PSG", "MCY", "SEV", "OPO", "LEV") chessboard MAD BYN BEN PSG MCY SEV OPO LEV BCN 0 2 3 4 0 0 7 8 ATL 0 0 3 4 5 0 7 8 DOR 0 0 3 4 5 6 7 0 MON 1 2 3 0 5 6 7 0 ARS 1 2 3 0 0 6 7 8 LEI 1 2 3 4 0 6 0 8 JUV 1 2 3 4 5 0 7 8 NAP 1 2 0 4 5 6 7 8 match = function(){ vec = rep(0,8) for(i in 1:8){ tryCatch({vec[i] = as.numeric(sample(as.character(chessboard[i,][!(chessboard[i,] %in% vec) & chessboard[i,] > 0]),1)) last=chessboard[8,][!(chessboard[8,] %in% vec) & chessboard[i,] > 0] },error=function(e){}) } vec } match() set.seed(0) nsim = 100000 matches = t(replicate(nsim, match())) matches = subset(matches, matches[,8]!=0) colnames(matches) = c("BCN", "ATL", "DOR", "MON", "ARS", "LEI", "JUV", "NAP") head(matches) table = apply(matches, 2, function(x) table(x)/nrow(matches)) table $BCN x 2 3 4 7 8 0.1969821 0.2125814 0.1967272 0.1967166 0.1969927 $ATL x 3 4 5 7 8 0.2016226 0.1874462 0.2357732 0.1875737 0.1875843 $DOR x 3 4 5 6 7 0.1773264 0.1686188 0.2097673 0.2787270 0.1655605 $MON x 1 2 3 5 6 7 0.2567882 0.2031199 0.1172017 0.1341921 0.1789617 0.1097365 $ARS x 1 2 3 6 7 8 0.2368882 0.1907169 0.1104480 0.1651358 0.1026112 0.1941999 $LEI x 1 2 3 4 6 8 0.2129743 0.1717302 0.1019210 0.1856410 0.1511081 0.1766255 $JUV x 1 2 3 4 5 7 8 0.15873252 0.12940289 0.07889902 0.14203948 0.22837179 0.12845781 0.13409648 $NAP x 1 2 4 5 6 7 8 0.1346168 0.1080481 0.1195272 0.1918956 0.2260675 0.1093436 0.1105011
Maybe try this: matches = setNames(as.list(rep(NA,8)), rownames(mat)) set.seed(1) # For each row, sample a column, then drop that column. # 'sample.int' will automatically renormalize the probabilities. for (i in sample.int(8)) { team_i = rownames(mat)[i] j = sample.int(ncol(mat), 1, prob=mat[i,]) matches[[team_i]] = colnames(mat)[j] mat = mat[,-j,drop=FALSE] } > matches # $Barcelona # [1] "Oporto" # # $Atletico # [1] "Benfica" # # $Dortmund # [1] "Paris" # # $Juventus # [1] "City" # # $Arsenal # [1] "Sevilla" # # $Napoli # [1] "Leverkusen" # # $Monaco # [1] "Bayern" # # $Leicester # [1] "Madrid" Might be a good idea to add restrictions so you don't end up with a row of zeros.
Pascal's triangle code
I am having trouble getting this python code to work right. it is a code to display pascal's triangle using binomials. I do not know what is wrong. The code looks like this from math import factorial def binomial (n,k): if k==0: return 1 else: return int((factorial(n)//factorial(k))*factorial(n-k)) def pascals_triangle(rows): rows=20 for n in range (0,rows): for k in range (0,n+1): print(binomial(n,k)) print '\n' This is what it keeps printing 1 1 1 1 2 1 1 12 3 1 1 144 24 4 1 1 2880 360 40 5 1 1 86400 8640 720 60 6 1 1 3628800 302400 20160 1260 and on and on. any help would be welcomed.!!
from math import factorial def binomial (n,k): if k==0: return 1 else: return int((factorial(n)//factorial(k))*factorial(n-k)) def pascals_triangle(rows): for n in range (rows): l = [binomial(n, k) for k in range (0,n+1)] print l pascals_triangle(5) output: [1] [1, 1] [1, 2, 1] [1, 12, 3, 1] [1, 144, 24, 4, 1]
there are many wrong things. The first one is the way you compute the values : if building a pascal triangle, you want to use the previous line to compute the current one, and not use the binomial computation (which is expensive due to the number of multiplications). then by default, print appends a "\n" Correct implementation: def print_line(x): print (" ".join(map(str,x))) def pascals_triangle(rows): cur_line=[1,1] for x in range (2,rows): new_line=[1] for n in range (0,len(cur_line)-1): new_line.append(cur_line[n]+cur_line[n+1]) new_line.append(1) print_line (new_line) cur_line=new_line this provides the following output $ python pascal.py 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 1 6 15 20 15 6 1 1 7 21 35 35 21 7 1 1 8 28 56 70 56 28 8 1 1 9 36 84 126 126 84 36 9 1
Your binomial function had a small bracketing mistake in it, which was giving you incorrect output: from math import factorial def binomial(n, k): if k==0: return 1 else: return int((factorial(n)/(factorial(k)*factorial(n-k)))) def pascals_triangle(rows, max_width): for n in range (0,rows): indent = (rows - n - 1) * max_width print(' ' * indent, end='') for k in range(0, n+1): print("{:^{w}}".format(binomial(n, k), w = max_width*2), end='') print() pascals_triangle(7, 2) With the addition of a padding parameter, the output can be made to look like this: 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 1 6 15 20 15 6 1
All 6-Number Permutations from a List
I'm writing a program, and the goal is to take a list of numbers and return all the six-letter combinations for it using a recursive function (without importing a function to do it for me). Say, for example, my numbers are "1 2 3 4 5 6 7 8 9", output would be: 1 2 3 4 5 6 1 2 3 4 5 7 1 2 3 4 5 8 1 2 3 4 5 9 1 2 3 4 6 7 1 2 3 4 6 8 1 2 3 4 6 9 1 2 3 4 7 8 ... etcetera, all the way down to 4 5 6 7 8 9 I'm not looking for code, persay, just a push in the right direction conceptually. What I've attempted thus far has failed and I've driven myself into a logical rut. I've included the code I used before below, but it isn't really a recursive function and only seems to work for 6-8-digit values. It's very messy, and I'd be fine with scrapping it entirely: # Function prints all the possible 6-number combinations for a group of numbers def lotto(constantnumbers, variablenumbers): # Base case: No more constant variables, or only 6 numbers to begin with if len(constantnumbers) == 0 or len(variablenumbers) == 0: if len(constantnumbers) == 0: print(" ".join(variablenumbers[1:7])) else: print(" ".join(constantnumbers[0:6])) i = 6 - len(constantnumbers) outvars = variablenumbers[1:i + 1] if len(variablenumbers) > len(outvars) + 1: print(" ".join(constantnumbers + outvars)) for index in range(len(outvars), 0, -1): outvars[index - 1] = variablenumbers[index + 1] print(" ".join(constantnumbers + outvars)) else: i = 6 - len(constantnumbers) outvars = variablenumbers[1:i + 1] print(" ".join(constantnumbers + outvars)) if len(variablenumbers) > len(outvars) + 1: for index in range(len(outvars), 0, -1): outvars[index - 1] = variablenumbers[index + 1] print(" ".join(constantnumbers + outvars)) #Reiterates the function until there are no more constant numbers lotto(constantnumbers[0:-1], constantnumbers[-1:] + variablenumbers)
import itertools for combo in itertools.combinations(range(1,10), 6): print(" ".join(str(c) for c in combo)) which gives 1 2 3 4 5 6 1 2 3 4 5 7 1 2 3 4 5 8 ... 3 4 6 7 8 9 3 5 6 7 8 9 4 5 6 7 8 9 Edit: ok, here is a recursive definition: def combinations(basis, howmany): for index in range(0, len(basis) - howmany + 1): if howmany == 1: yield [basis[index]] else: this, remainder = basis[index], basis[index+1:] for rest in combinations(remainder, howmany - 1): yield [this] + rest Edit2: Base case: A 1-item combination is any basis item. Induction: An N-item combination is any basis item plus an (N-1)-item combination from the remaining basis.