How to optimize an O(N*M) to be O(n**2)? - python

I am trying to solve USACO's Milking Cows problem. The problem statement is here: https://train.usaco.org/usacoprob2?S=milk2&a=n3lMlotUxJ1
Given a series of intervals in the form of a 2d array, I have to find the longest interval and the longest interval in which no milking was occurring.
Ex. Given the array [[500,1200],[200,900],[100,1200]], the longest interval would be 1100 as there is continuous milking and the longest interval without milking would be 0 as there are no rest periods.
I have tried looking at whether utilizing a dictionary would decrease run times but I haven't had much success.
f = open('milk2.in', 'r')
w = open('milk2.out', 'w')
#getting the input
farmers = int(f.readline().strip())
schedule = []
for i in range(farmers):
schedule.append(f.readline().strip().split())
#schedule = data
minvalue = 0
maxvalue = 0
#getting the minimums and maximums of the data
for time in range(farmers):
schedule[time][0] = int(schedule[time][0])
schedule[time][1] = int(schedule[time][1])
if (minvalue == 0):
minvalue = schedule[time][0]
if (maxvalue == 0):
maxvalue = schedule[time][1]
minvalue = min(schedule[time][0], minvalue)
maxvalue = max(schedule[time][1], maxvalue)
filled_thistime = 0
filled_max = 0
empty_max = 0
empty_thistime = 0
#goes through all the possible items in between the minimum and the maximum
for point in range(minvalue, maxvalue):
isfilled = False
#goes through all the data for each point value in order to find the best values
for check in range(farmers):
if point >= schedule[check][0] and point < schedule[check][1]:
filled_thistime += 1
empty_thistime = 0
isfilled = True
break
if isfilled == False:
filled_thistime = 0
empty_thistime += 1
if (filled_max < filled_thistime) :
filled_max = filled_thistime
if (empty_max < empty_thistime) :
empty_max = empty_thistime
print(filled_max)
print(empty_max)
if (filled_max < filled_thistime):
filled_max = filled_thistime
w.write(str(filled_max) + " " + str(empty_max) + "\n")
f.close()
w.close()
The program works fine, but I need to decrease the time it takes to run.

A less pretty but more efficient approach would be to solve this like a free list, though it is a bit more tricky since the ranges can overlap. This method only requires looping through the input list a single time.
def insert(start, end):
for existing in times:
existing_start, existing_end = existing
# New time is a subset of existing time
if start >= existing_start and end <= existing_end:
return
# New time ends during existing time
elif end >= existing_start and end <= existing_end:
times.remove(existing)
return insert(start, existing_end)
# New time starts during existing time
elif start >= existing_start and start <= existing_end:
# existing[1] = max(existing_end, end)
times.remove(existing)
return insert(existing_start, end)
# New time is superset of existing time
elif start <= existing_start and end >= existing_end:
times.remove(existing)
return insert(start, end)
times.append([start, end])
data = [
[500,1200],
[200,900],
[100,1200]
]
times = [data[0]]
for start, end in data[1:]:
insert(start, end)
longest_milk = 0
longest_gap = 0
for i, time in enumerate(times):
duration = time[1] - time[0]
if duration > longest_milk:
longest_milk = duration
if i != len(times) - 1 and times[i+1][0] - times[i][1] > longest_gap:
longes_gap = times[i+1][0] - times[i][1]
print(longest_milk, longest_gap)

As stated in the comments, if the input is sorted, the complexity could be O(n), if that's not the case we need to sort it first and the complexity is O(nlog n):
lst = [ [300,1000],
[700,1200],
[1500,2100] ]
from itertools import groupby
longest_milking = 0
longest_idle = 0
l = sorted(lst, key=lambda k: k[0])
for v, g in groupby(zip(l[::1], l[1::1]), lambda k: k[1][0] <= k[0][1]):
l = [*g][0]
if v:
mn, mx = min(i[0] for i in l), max(i[1] for i in l)
if mx-mn > longest_milking:
longest_milking = mx-mn
else:
mx = max((i2[0] - i1[1] for i1, i2 in zip(l[::1], l[1::1])))
if mx > longest_idle:
longest_idle = mx
# corner case, N=1 (only one interval)
if len(lst) == 1:
longest_milking = lst[0][1] - lst[0][0]
print(longest_milking)
print(longest_idle)
Prints:
900
300
For input:
lst = [ [500,1200],
[200,900],
[100,1200] ]
Prints:
1100
0

Related

Python complete search in one pass function

I am writing a program that takes in a list of start and end times for farmers milking cows and determines both the longest time where >=1 cow is being milked and the longest time where no cows are being milk.
In it, I've tried using this function. It's an exercise on complete search, but this isn't fast enough when there's a lot of data (I think because there are n^2 iterations).
timesIS is simply a list of the times in increasing start order, and timesDE is a list of the same times by decreasing end. timeIndex is the position from which to start. For the longest interval of milking, my program later does this for every index and returns the longest interval.
While still keeping to a complete search, how can I make this more efficient (switch to something closer to n passes, perhaps)?
def nextCease(TimesIS, timesDE, timeIndex):
latestTime = TimesIS[timeIndex][1]
for j in range (0, len(timesDE)):
for i in range (0, len(timesDE)):
if timesDE[i][0]<=latestTime and timesDE[i][1]>=latestTime:
latestTime = timesDE[i][1]
if latestTime == timesDE[0][1]:
return latestTime
break
return latestTime
Here's a small piece of data input (first line is just the number of farmers):
6
100 200
200 400
400 800
800 1600
50 100
1700 3200
I think this is a minimal, complete, and verifiable example:
from operator import itemgetter
times = [[100,200], [200,400], [400,800], [800,1600], [50,100], [1700,3200]
def nextCease(TimesIS, timesDE, timeIndex):
latestTime = TimesIS[timeIndex][1]
for j in range (0, len(timesDE)):
for i in range (0, len(timesDE)):
if timesDE[i][0]<=latestTime and timesDE[i][1]>=latestTime:
latestTime = timesDE[i][1]
if latestTime == timesDE[0][1]:
return latestTime
break
return latestTime
timesIS = sorted(times[:], key=itemgetter(0)) #increasing starttimes
timesDE = sorted(times[:], key=itemgetter(1), reverse=True) #decreasing endtimes
longestIntervalMilk = 0
for i in range (0, len(times)):
interval = nextCease(timesIS, timesDE, i) - timesIS[i][0]
if interval > longestIntervalMilk:
longestIntervalMilk = interval
longestIntervalNoMilk = 0
latestFinish = 0
for i in range (0, len(times)):
latestFinish = nextCease(timesIS, timesDE, i)
timesIS2 = timesIS[:]
while(timesIS2[0][0] < latestFinish):
nextStartExists = True
del timesIS2[0]
if timesIS2 == []:
nextStartExists = False
break
if nextStartExists == True:
nextStart = timesIS2[0][0]
longestIntervalNoMilk = nextStart - latestFinish
print(str(longestIntervalMilk) + " " + str(longestIntervalNoMilk) + "\n"))
EDIT: In the meantime, I wrote up this. It gives the wrong output for a very long list (it's 1001 lines so I won't reprint it here, but you can find it at http://train.usaco.org/usacodatashow?a=iA4oZAAX7KZ) and I'm confused as to why:
times = sorted(times[:], key=itemgetter(0))
def longestMilkInterval(times):
earliestTime = times[0]
latestTime = times[0][1]
interval = 0
for i in range (1, len(times)):
if times[i][1] > latestTime and times[i][0] <= latestTime:
if times[i][1] - earliestTime[0] > interval:
interval = times[i][1] - earliestTime[0]
latestTime = times[i][1]
else:
earliestTime = times[i]
latestTime = times[i][1]
print(earliestTime)
return interval
def longestNoMilkInterval(times):
earliestTime = times[0][1]
interval = 0
for i in range (0, len(times)):
if times[i][0] >= earliestTime:
if times[i][0] - earliestTime > interval:
interval = times[i][0] - earliestTime
break
else:
earliestTime = times[i][1]
return interval
Output should be 912 184 (>=1 cow, 0 cow).
Here is a very straightforward approach which does it in one pass, including a sort, so the complexity would be O(n*logn).
# Part 1: transform to tuples (start_time, typ)
items = []
for start, end in times:
items += [(start, 's'), (end, 'e')]
items = sorted(items)
# Part 2: compute max durations where 0 or 1+ cows are being milked
max_0_cows = max_1plus_cows = 0
last_intersection_time = items[0][0] # starting with first cow milk time
nof_cows_milked = 1
for i, (start_time, typ) in enumerate(items[1:], 1):
if items[i-1][0] == start_time and items[i-1][1] != typ:
continue
if i+1 < len(items) and items[i+1][0] == start_time and items[i+1][1] != typ:
continue
if typ == 's':
nof_cows_milked += 1
elif typ == 'e':
nof_cows_milked -= 1
# check if we cross from 1+ -> 0 or 0 -> 1+
if (typ, nof_cows_milked) in (('e', 0), ('s', 1)):
duration = start_time - last_intersection_time
if nof_cows_milked == 1:
max_0_cows = max(max_0_cows, duration)
if nof_cows_milked == 0:
max_1plus_cows = max(max_1plus_cows, duration)
last_intersection_time = start_time
print("Max time 0 cows: {}, Max time 1+ cows: {}".format(max_0_cows, max_1plus_cows))
Building of items: It puts the start/end itervals into a list of tuples (start_time, typ) so we can traverse the list and if we see a s a new cow is being milked and e then a cow is stopped being milked. This way we can have a counter nof_cows_milked at any time which is the basis for getting the "max time 0 cows milked" and "max time 1+ cows milked"
The actual longest-time-finder checks for all the transitions from 0 -> 1+ cows milked or 1+ cows -> 0 cows milked. In the first 4 lines it filters out the cases when two adjacent itervals (one farmer stops when the other farmer starts) It keeps track of those times with last_intersection_time and compares the duration to the max duration of max_0_cows and max_1_plus_cows. Again, this part is not very pretty, maybe there are more elegant ways to solve that.
[my algorithm] gives the wrong output [...] and I'm confused as to why
Your algorithm basically just checks for the longest interval of a single tuple, but doesn't check for overlapping or adjacent tuples.
Take these intervals as an visual example:
Your code just finds the interval G-H, whereas you need to find C-F. You somewhere need to keep track of how many cows are milked in parallel, so you need at least the nof_of_cows_milked counter as in my code example.

Find longest sequence of 0's in the integer list

A = [1,2,0,0,3,4,5,-1,0,2,-1,-3,0,0,0,0,0,0,0,0,-2,-3,-4,-5,0,0,0]
Return initial and ending index of longest sequence of 0's in the list.
As, longest sequence of 0's in above list is 0,0,0,0,0,0,0,0 so it should return 12,19 as starting and ending index.Please help with some one line python code.
I tried :
k = max(len(list(y)) for (c,y) in itertools.groupby(A) if c==0)
print(k)
which return 8 as the max length.
Now, how to find start and end index of longest sequence?
you can first use enumerate to zip the item with index,
and then itertools.groupby(list,operator.itemgetter(1)) to group by item,
filter only 0s using list(y) for (x,y) in list if x == 0,
and at last max(list, key=len) to get the longest sequence.
import itertools,operator
r = max((list(y) for (x,y) in itertools.groupby((enumerate(A)),operator.itemgetter(1)) if x == 0), key=len)
print(r[0][0]) # prints 12
print(r[-1][0]) # prints 19
You can try this:
A = [1,2,0,0,3,4,5,-1,0,2,-1,-3,0,0,0,0,0,0,0,0,2,-3,-4,-5,0,0,0]
count = 0
prev = 0
indexend = 0
for i in range(0,len(A)):
if A[i] == 0:
count += 1
else:
if count > prev:
prev = count
indexend = i
count = 0
print("The longest sequence of 0's is "+str(prev))
print("index start at: "+ str(indexend-prev))
print("index ends at: "+ str(indexend-1))
Output:
The longest sequence of 0's ist 8
index start at: 12
index ends at: 19
A nice concise native python approach
target = 0
A = [1,2,0,0,3,4,5,-1,0,2,-1,-3,0,0,0,0,0,0,0,0,2,-3,-4,-5,0,0,0]
def longest_seq(A, target):
""" input list of elements, and target element, return longest sequence of target """
cnt, max_val = 0, 0 # running count, and max count
for e in A:
cnt = cnt + 1 if e == target else 0 # add to or reset running count
max_val = max(cnt, max_val) # update max count
return max_val
Now that you have the length, find that k-length sequence of 0's in the original list. Expanding the stuff you'll eventually work into one line:
# k is given in your post
k_zeros = [0]*k
for i in range(len(A)-k):
if A[i:i+k] == k_zeros:
break
# i is the start index; i+k-1 is the end
Can you wrap this into a single statement now?
Ok, as one long disgusting line!
"-".join([sorted([list(y) for c,y in itertools.groupby([str(v)+"_"+str(i) for i,v in enumerate(A)], lambda x: x.split("_")[0]) if c[0] == '0'],key=len)[-1][a].split("_")[1] for a in [0,-1]])
It keeps track of indices by turning [1,2,0...] into ["1_0","2_1","0_2",..] and then doing some splitting and parsing.
Yes it's very ugly and you should go with one of the other answers but I wanted to share
This solution i submitted in Codility with 100 percent efficieny.
class Solution {
public int solution(int N) {
int i = 0;
int gap = 0;
`bool startZeroCount = false;
List<int> binaryArray = new List<int>();
while (N > 0)
{
binaryArray.Add(N % 2);
N = N / 2;
i++;
}
List<int> gapArr = new List<int>();
for (int j = i-1; j >= 0; j--)
{
if (binaryArray[j] == 1)
{
if(startZeroCount)
{
gapArr.Add(gap);
gap = 0;
}
startZeroCount = true;
}
else if(binaryArray[j] == 0)
{
if (startZeroCount)
gap++;
}
}
gapArr.Sort();
if (gapArr.Count != 0)
return gapArr[gapArr.Count - 1];
else return 0;enter code here
}
}
A = [1,2,0,0,3,4,5,-1,0,2,-1,-3,0,0,0,2,-3,-4,-5,0,0,0,0]
count = 0
prev = 0
indexend = 0
indexcount = 0
for i in range(0,len(A)):
if A[i] == 0:
count += 1
indexcount = i
else:
if count > prev:
prev = count
indexend = i
count = 0
if count > prev:
prev = count
indexend = indexcount
print("The longest sequence of 0's is "+str(prev))
print("index start at: "+ str(indexend-prev))
print("index ends at: "+ str(indexend-1))
To also consider if longest 0's sequecnces are at the end.
Output
The longest sequence of 0's is 4
index start at: 18
index ends at: 21
If you would like to completely avoid Python iteration you can do it with Numpy. E.g., for very long sequences, using for loops may be relatively slow. This method will use pre-compiled C for-loops under the hood. The disadvantage is that you have multiple for-loops here. Nonetheless, overall, below algorithm should be a speed gain on longer sequences.
import numpy as np
def longest_sequence(bool_array):
where_not_true = np.where(~bool_array)[0]
lengths_plus_1 = np.diff(np.hstack((-1,where_not_true,len(bool_array))))
index = np.cumsum(np.hstack((0,lengths_plus_1)))
start_in_lngth = np.argmax(lengths_plus_1)
start = index[ start_in_lngth]
length = lengths_plus_1[start_in_lngth] - 1
return start, length
t = np.array((0,1,0,1,1,1,0,0,1,1,0,1))
print(longest_sequence(t==0))
print(longest_sequence(t==1))
p = np.array((0,0,0,1,0,1,1,1,0,0,0,1,1,0,1,1,1,1))
print(longest_sequence(p==0))
print(longest_sequence(p==1))

Python Insertion Sort Algorithm

Basically i'm trying to write an insertion sort algorithm in python and I have no idea where i'm going wrong
#!/usr/bin/env python
# coding: utf-8
import random
Array = random.sample(range(30), 5)
First = 1
Last = len(Array)
PositionOfNext = Last – 1
while PositionOfNext >= First:
Next = Array(PositionOfNext)
Current = PositionOfNext
while (Current < Last) and (Next > Array[Current] + 1):
Current = Current + 1
(Array[Current] - 1) = Array[Current]
Array[Current] = Next
PositionOfNext = PositionOfNext - 1
print Array
Fixing some syntax issues and some of the indices.
Also replacing:
(Array[Current] - 1) = Array[Current]
by:
Array[Current - 1], Array[Current] = Array[Current], Array[Current - 1]
The code completed
#!/usr/bin/env python
# coding: utf-8
import random
Array = random.sample(range(30), 5)
print Array
First = 0
Last = len(Array) - 1
PositionOfNext = Last - 1
while PositionOfNext >= First:
Next = Array[PositionOfNext]
Current = PositionOfNext
while (Current < Last) and (Array[Current] > Array[Current + 1]):
Current = Current + 1
Array[Current - 1], Array[Current] = Array[Current], Array[Current - 1]
Array[Current] = Next
PositionOfNext = PositionOfNext - 1
print Array
def insertionSort(alist):
for index in range(1,len(alist)):
currentvalue = alist[index]
position = index
while position>0 and alist[position-1]>currentvalue:
alist[position]=alist[position-1]
position = position-1
alist[position]=currentvalue
alist = [54,26,93,17,77,31,44,55,20]
insertionSort(alist)
print(alist)
The Insertion Sort
How about:
def insertion_sort(x):
# insertion sort
# we can optimize for desc, asc if we want to
# advantages: online, O(nk) for nearly sorted
x_sorted = [x[0]]
x_unsorted = x[1::]
for xx in x_unsorted:
x_sorted.append(xx) # make room, and/or assume a sorted input list
for i in range(len(x_sorted)-1):
if xx < x_sorted[i]: # asc?
x_sorted[i+1::] = x_sorted[i:-1] # shift old values
x_sorted[i] = xx # insert new
break # nothing to do in the inner loop form here on out
i += 1
return x_sorted

leading number groups between two numbers

(Python) Given two numbers A and B. I need to find all nested "groups" of numbers:
range(2169800, 2171194)
leading numbers: 21698XX, 21699XX, 2170XX, 21710XX, 217110X, 217111X,
217112X, 217113X, 217114X, 217115X, 217116X, 217117X, 217118X, 2171190X,
2171191X, 2171192X, 2171193X, 2171194X
or like this:
range(1000, 1452)
leading numbers: 10XX, 11XX, 12XX, 13XX, 140X, 141X, 142X, 143X,
144X, 1450, 1451, 1452
Harder than it first looked - pretty sure this is solid and will handle most boundary conditions. :) (There are few!!)
def leading(a, b):
# generate digit pairs a=123, b=456 -> [(1, 4), (2, 5), (3, 6)]
zip_digits = zip(str(a), str(b))
zip_digits = map(lambda (x,y):(int(x), int(y)), zip_digits)
# this ignores problems where the last matching digits are 0 and 9
# leading (12000, 12999) is same as leading(12, 12)
while(zip_digits[-1] == (0,9)):
zip_digits.pop()
# start recursion
return compute_leading(zip_digits)
def compute_leading(zip_digits):
if(len(zip_digits) == 1): # 1 digit case is simple!! :)
(a,b) = zip_digits.pop()
return range(a, b+1)
#now we partition the problem
# given leading(123,456) we decompose this into 3 problems
# lows -> leading(123,129)
# middle -> leading(130,449) which we can recurse to leading(13,44)
# highs -> leading(450,456)
last_digits = zip_digits.pop()
low_prefix = reduce(lambda x, y : 10 * x + y, [tup[0] for tup in zip_digits]) * 10 # base for lows e.g. 120
high_prefix = reduce(lambda x, y : 10 * x + y, [tup[1] for tup in zip_digits]) * 10 # base for highs e.g. 450
lows = range(low_prefix + last_digits[0], low_prefix + 10)
highs = range(high_prefix + 0, high_prefix + last_digits[1] + 1)
#check for boundary cases where lows or highs have all ten digits
(a,b) = zip_digits.pop() # pop last digits of middle so they can be adjusted
if len(lows) == 10:
lows = []
else:
a = a + 1
if len(highs) == 10:
highs = []
else:
b = b - 1
zip_digits.append((a,b)) # push back last digits of middle after adjustments
return lows + compute_leading(zip_digits) + highs # and recurse - woohoo!!
print leading(199,411)
print leading(2169800, 2171194)
print leading(1000, 1452)
def foo(start, end):
index = 0
is_lower = False
while index < len(start):
if is_lower and start[index] == '0':
break
if not is_lower and start[index] < end[index]:
first_lower = index
is_lower = True
index += 1
return index-1, first_lower
start = '2169800'
end = '2171194'
result = []
while int(start) < int(end):
index, first_lower = foo(start, end)
range_end = index > first_lower and 10 or int(end[first_lower])
for x in range(int(start[index]), range_end):
result.append(start[:index] + str(x) + 'X'*(len(start)-index-1))
if range_end == 10:
start = str(int(start[:index])+1)+'0'+start[index+1:]
else:
start = start[:index] + str(range_end) + start[index+1:]
result.append(end)
print "Leading numbers:"
print result
I test the examples you've given, it is right. Hope this will help you
This should give you a good starting point :
def leading(start, end):
leading = []
hundreds = start // 100
while (end - hundreds * 100) > 100:
i = hundreds * 100
leading.append(range(i,i+100))
hundreds += 1
c = hundreds * 100
tens = 1
while (end - c - tens * 10) > 10:
i = c + tens * 10
leading.append(range(i, i + 10))
tens += 1
c += tens * 10
ones = 1
while (end - c - ones) > 0:
i = c + ones
leading.append(i)
ones += 1
leading.append(end)
return leading
Ok, the whole could be one loop-level deeper. But I thought it might be clearer this way. Hope, this helps you...
Update :
Now I see what you want. Furthermore, maria's code doesn't seem to be working for me. (Sorry...)
So please consider the following code :
def leading(start, end):
depth = 2
while 10 ** depth > end : depth -=1
leading = []
const = 0
coeff = start // 10 ** depth
while depth >= 0:
while (end - const - coeff * 10 ** depth) >= 10 ** depth:
leading.append(str(const / 10 ** depth + coeff) + "X" * depth)
coeff += 1
const += coeff * 10 ** depth
coeff = 0
depth -= 1
leading.append(end)
return leading
print leading(199,411)
print leading(2169800, 2171194)
print leading(1000, 1453)
print leading(1,12)
Now, let me try to explain the approach here.
The algorithm will try to find "end" starting from value "start" and check whether "end" is in the next 10^2 (which is 100 in this case). If it fails, it will make a leap of 10^2 until it succeeds. When it succeeds it will go one depth level lower. That is, it will make leaps one order of magnitude smaller. And loop that way until the depth is equal to zero (= leaps of 10^0 = 1). The algorithm stops when it reaches the "end" value.
You may also notice that I have the implemented the wrapping loop I mentioned so it is now possible to define the starting depth (or leap size) in a variable.
The first while loop makes sure the first leap does not overshoot the "end" value.
If you have any questions, just feel free to ask.

Python Dynamic Knapsack

Right now I am attempting to code the knapsack problem in Python 3.2. I am trying to do this dynamically with a matrix. The algorithm that I am trying to use is as follows
Implements the memoryfunction method for the knapsack problem
Input: A nonnegative integer i indicating the number of the first
items being considered and a nonnegative integer j indicating the knapsack's capacity
Output: The value of an optimal feasible subset of the first i items
Note: Uses as global variables input arrays Weights[1..n], Values[1...n]
and table V[0...n, 0...W] whose entries are initialized with -1's except for
row 0 and column 0 initialized with 0's
if V[i, j] < 0
if j < Weights[i]
value <-- MFKnapsack(i - 1, j)
else
value <-- max(MFKnapsack(i -1, j),
Values[i] + MFKnapsack(i -1, j - Weights[i]))
V[i, j} <-- value
return V[i, j]
If you run the code below that I have you can see that it tries to insert the weight into the the list. Since this is using the recursion I am having a hard time spotting the problem. Also I get the error: can not add an integer with a list using the '+'. I have the matrix initialized to start with all 0's for the first row and first column everything else is initialized to -1. Any help will be much appreciated.
#Knapsack Problem
def knapsack(weight,value,capacity):
weight.insert(0,0)
value.insert(0,0)
print("Weights: ",weight)
print("Values: ",value)
capacityJ = capacity+1
## ------ initialize matrix F ---- ##
dimension = len(weight)+1
F = [[-1]*capacityJ]*dimension
#first column zeroed
for i in range(dimension):
F[i][0] = 0
#first row zeroed
F[0] = [0]*capacityJ
#-------------------------------- ##
d_index = dimension-2
print(matrixFormat(F))
return recKnap(F,weight,value,d_index,capacity)
def recKnap(matrix, weight,value,index, capacity):
print("index:",index,"capacity:",capacity)
if matrix[index][capacity] < 0:
if capacity < weight[index]:
value = recKnap(matrix,weight,value,index-1,capacity)
else:
value = max(recKnap(matrix,weight,value,index-1,capacity),
value[index] +
recKnap(matrix,weight,value,index-1,capacity-(weight[index]))
matrix[index][capacity] = value
print("matrix:",matrix)
return matrix[index][capacity]
def matrixFormat(*doubleLst):
matrix = str(list(doubleLst)[0])
length = len(matrix)-1
temp = '|'
currChar = ''
nextChar = ''
i = 0
while i < length:
if matrix[i] == ']':
temp = temp + '|\n|'
#double digit
elif matrix[i].isdigit() and matrix[i+1].isdigit():
temp = temp + (matrix[i]+matrix[i+1]).center(4)
i = i+2
continue
#negative double digit
elif matrix[i] == '-' and matrix[i+1].isdigit() and matrix[i+2].isdigit():
temp = temp + (matrix[i]+matrix[i+1]+matrix[i+2]).center(4)
i = i + 2
continue
#negative single digit
elif matrix[i] == '-' and matrix[i+1].isdigit():
temp = temp + (matrix[i]+matrix[i+1]).center(4)
i = i + 2
continue
elif matrix[i].isdigit():
temp = temp + matrix[i].center(4)
#updates next round
currChar = matrix[i]
nextChar = matrix[i+1]
i = i + 1
return temp[:-1]
def main():
print("Knapsack Program")
#num = input("Enter the weights you have for objects you would like to have:")
#weightlst = []
#valuelst = []
## for i in range(int(num)):
## value , weight = eval(input("What is the " + str(i) + " object value, weight you wish to put in the knapsack? ex. 2,3: "))
## weightlst.append(weight)
## valuelst.append(value)
weightLst = [2,1,3,2]
valueLst = [12,10,20,15]
capacity = 5
value = knapsack(weightLst,valueLst,5)
print("\n Max Matrix")
print(matrixFormat(value))
main()
F = [[-1]*capacityJ]*dimension
does not properly initialize the matrix. [-1]*capacityJ is fine, but [...]*dimension creates dimension references to the exact same list. So modifying one list modifies them all.
Try instead
F = [[-1]*capacityJ for _ in range(dimension)]
This is a common Python pitfall. See this post for more explanation.
for the purpose of cache illustration, I generally use a default dict as follows:
from collections import defaultdict
CS = defaultdict(lambda: defaultdict(int)) #if i want to make default vals as 0
###or
CACHE_1 = defaultdict(lambda: defaultdict(lambda: int(-1))) #if i want to make default vals as -1 (or something else)
This keeps me from making the 2d arrays in python on the fly...
To see an answer to z1knapsack using this approach:
http://ideone.com/fUKZmq
def zeroes(n,m):
v=[['-' for i in range(0,n)]for j in range(0,m)]
return v
value=[0,12,10,20,15]
w=[0,2,1,3,2]
v=zeroes(6,5)
def knap(i,j):
global v
if i==0 or j==0:
v[i][j]= 0
elif j<w[i] :
v[i][j]=knap(i-1,j)
else:
v[i][j]=max(knap(i-1,j),value[i]+knap(i-1,j-w[i]))
return v[i][j]
x=knap(4,5)
print (x)
for i in range (0,len(v)):
for j in range(0,len(v[0])):
print(v[i][j],end="\t\t")
print()
print()
#now these calls are for filling all the boxes in the matrix as in the above call only few v[i][j]were called and returned
knap(4,1)
knap(4,2)
knap(4,3)
knap(4,4)
for i in range (0,len(v)):
for j in range(0,len(v[0])):
print(v[i][j],end="\t\t")
print()
print()

Categories

Resources