best travel python
Question:
https://www.codewars.com/kata/best-travel/train/python
my code times out, what could i have done better?
def choose_best_sum(t, k, ls):
import itertools
values=list(itertools.combinations(ls,k))
distances=[]
for row in values:
dis=0
for i in range(k):
dis+=row[i]
distances.append(dis)
while len(distances)!=0 and max(distances)>t:
#print("max:",max(distances),"min:",min(distances))
distances.pop(distances.index(max(distances)))
if len(distances)==0:
result="None"
else:
result=max(distances)
return result
xs = [100, 76, 56, 44, 89, 73, 68, 56, 64, 123, 2333, 144, 50, 132, 123, 34, 89]
choose_best_sum(230, 4, xs)#, 230)
choose_best_sum(430, 5, xs)#, 430)
choose_best_sum(430, 8, xs)#, None)
Answers:
Writing your own sum
is probably slowing you down a lot. using the built in function is almost always going to be faster:
def choose_best_sum(t, k, ls):
import itertools
values=list(itertools.combinations(ls,k))
distances= [sum(row) for row in values]
#...
When you go to find the largest value smaller than t
, you modify the list a bunch before coming up with your result. Python lists are not terribly efficient at getting longer or shorter, so it would be much better to just sort the list and either just iterate until you find the edge of what is greater than t
or you could use a binary search.
#...
for distance in reversed(sorted(distances)):
if distance <= t:
return distance
Similar to the answer by @Aaron but without sorting the list. Just keep track of the running best.
import itertools
def choose_best_sum(t, k, ls):
best_sum = 0
best_set = None
combinations = itertools.combinations(ls, k)
for combination in combinations:
combi_sum = sum(combination)
if combi_sum <= t and combi_sum > best_sum:
best_set = combination
best_sum = combi_sum
return (best_set, best_sum)
xs = [100, 76, 56, 44, 89, 73, 68, 56, 64, 123, 2333, 144, 50, 132, 123, 34, 89]
xs = [item for item in xs if item < 430]
choose_best_sum(230, 4, xs)#, 230)
choose_best_sum(430, 5, xs)#, 430)
choose_best_sum(430, 8, xs)#, None)
The first thing you can do is just ditch 2333 from xs
because that can never be in a combination. I’m not sure why, in addition to creating your own sum
, you have while len(distances)!=0 and max(distances)>t:
and so much list manipulation (appending and popping). The %timeit
result:
%timeit choose_best_sum(430, 8, xs)
5.43 ms ± 72.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
def choose_best_sum(t, k, ls):
from itertools import combinations
ls.sort()
p=combinations(ls,k)
l=list(p)
l=[sum(i) for i in l]
l.sort()
if t in l:
return t
if len(l)==0:
return None
l=l[::-1]
for i in l:
if i<t:
return i
https://www.codewars.com/kata/best-travel/train/python
my code times out, what could i have done better?
def choose_best_sum(t, k, ls):
import itertools
values=list(itertools.combinations(ls,k))
distances=[]
for row in values:
dis=0
for i in range(k):
dis+=row[i]
distances.append(dis)
while len(distances)!=0 and max(distances)>t:
#print("max:",max(distances),"min:",min(distances))
distances.pop(distances.index(max(distances)))
if len(distances)==0:
result="None"
else:
result=max(distances)
return result
xs = [100, 76, 56, 44, 89, 73, 68, 56, 64, 123, 2333, 144, 50, 132, 123, 34, 89]
choose_best_sum(230, 4, xs)#, 230)
choose_best_sum(430, 5, xs)#, 430)
choose_best_sum(430, 8, xs)#, None)
Writing your own sum
is probably slowing you down a lot. using the built in function is almost always going to be faster:
def choose_best_sum(t, k, ls):
import itertools
values=list(itertools.combinations(ls,k))
distances= [sum(row) for row in values]
#...
When you go to find the largest value smaller than t
, you modify the list a bunch before coming up with your result. Python lists are not terribly efficient at getting longer or shorter, so it would be much better to just sort the list and either just iterate until you find the edge of what is greater than t
or you could use a binary search.
#...
for distance in reversed(sorted(distances)):
if distance <= t:
return distance
Similar to the answer by @Aaron but without sorting the list. Just keep track of the running best.
import itertools
def choose_best_sum(t, k, ls):
best_sum = 0
best_set = None
combinations = itertools.combinations(ls, k)
for combination in combinations:
combi_sum = sum(combination)
if combi_sum <= t and combi_sum > best_sum:
best_set = combination
best_sum = combi_sum
return (best_set, best_sum)
xs = [100, 76, 56, 44, 89, 73, 68, 56, 64, 123, 2333, 144, 50, 132, 123, 34, 89]
xs = [item for item in xs if item < 430]
choose_best_sum(230, 4, xs)#, 230)
choose_best_sum(430, 5, xs)#, 430)
choose_best_sum(430, 8, xs)#, None)
The first thing you can do is just ditch 2333 from xs
because that can never be in a combination. I’m not sure why, in addition to creating your own sum
, you have while len(distances)!=0 and max(distances)>t:
and so much list manipulation (appending and popping). The %timeit
result:
%timeit choose_best_sum(430, 8, xs)
5.43 ms ± 72.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
def choose_best_sum(t, k, ls):
from itertools import combinations
ls.sort()
p=combinations(ls,k)
l=list(p)
l=[sum(i) for i in l]
l.sort()
if t in l:
return t
if len(l)==0:
return None
l=l[::-1]
for i in l:
if i<t:
return i