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.

Categories