Confusing syntax from a search tree algorithm - python

I am currently taking 6.00.2x from MITx, and there is a line from a search tree algorithm that confuses me, could anyone help please?
val, taken = maxVal(foods, maxUnits)
This syntax does not make sense to me. maxVal is a function, so presumably foods and maxUnits are inputs. But what are val and taken, what does this line do? Nowhere in the code are there variables instantiated with those names, so I am just not sure what they are (and this line of syntax means).
PS: The complete code is as follows. The aforementioned syntax occurs on 3rd line of the function testMaxVal. foods is a list of 1) food, 2) values, and 3) calories.
def maxVal(toConsider, avail):
"""Assumes toConsider a list of items, avail a weight
Returns a tuple of the total value of a solution to the
0/1 knapsack problem and the items of that solution"""
if toConsider == [] or avail == 0:
result = (0, ())
elif toConsider[0].getCost() > avail:
#Explore right branch only
result = maxVal(toConsider[1:], avail)
else:
nextItem = toConsider[0]
#Explore left branch
withVal, withToTake = maxVal(toConsider[1:],
avail - nextItem.getCost())
withVal += nextItem.getValue()
#Explore right branch
withoutVal, withoutToTake = maxVal(toConsider[1:], avail)
#Choose better branch
if withVal > withoutVal:
result = (withVal, withToTake + (nextItem,))
else:
result = (withoutVal, withoutToTake)
return result
def testMaxVal(foods, maxUnits, printItems = True):
print('Use search tree to allocate', maxUnits,
'calories')
val, taken = maxVal(foods, maxUnits)
print('Total value of items taken =', val)
if printItems:
for item in taken:
print(' ', item)
testMaxVal(foods, 750)

As you can see, maxVal can return two outputs at the same time like at the line :
result = (withoutVal, withoutToTake)
Recover these two outputs in two variable val and taken is done by the line :
val, taken = maxVal(foods, maxUnits)

The function maxVal returns a tuple. You can return multiple values from a function in python in the form of tuple.
Example:
def connect():
connection = _connect()
message = "Connected"
if not connection:
message = "Not connected"
return connection, message
connection, message = connect()

maxVal returns a pair.
You can "deconstruct" any tuple by assigning its elements to the appropriate number of variables simultaneously.
Example:
>>> a,b,c = (1,2, "hello")
>>> a
1
>>> b
2
>>> c
'hello'

Related

Python modulo returning wrong answer

I was attempting this question on leetcode using python3.
My solution for this problem is as follows:
class Solution:
def __init__(self):
self.memo = {}
def numRollsToTarget(self, d: int, f: int, target: int) -> int:
if target < d or target > d*f: return 0
if d == 1:
if target<=f: return 1
else: return 0
key = d+f+target
if key not in self.memo:
total = 0
for i in range(1,f+1):
if key not in self.memo:
total += self.numRollsToTarget(d-1,f,target-i)
self.memo[key] = total%((10**9) + 7)
return self.memo[key]
My solution works for small inputs like:
Input: n = 1, k = 6, target = 3 Output: 1 Explanation: You throw one die with 6 faces. There is only one way to get a sum of 3.
However inputs such as
Input: n = 30, k = 30, target = 500 Output: 222616187 Explanation: The answer must be returned modulo 109 + 7.
do not pass the test cases, even when I have made sure that I return the modulo answer. For example for the second test case, the correct output is 222616187. However, my code returns 146996393. Can anyone point out where I am going wrong?
d+f+target is not a unique key for the tuple (d, f, target), which makes the memoization yield incorrect results. Using the tuple (d, f, target) itself is a simple fix.
Also it is unclear why there is a second if key not in self.memo: inside the loop. It shouldn't be possible for that condition to be changed by the computation of the sub-problems, and if it was possible then it would be the wrong way to do it because the wrong total is added up and written over the memo entry that apparently exists.

Anybody know how to use pyresttest's 'fixed_sequence' generator?

I'm trying to use pyresttest's benchmarking framework to generate a sequence of entries in my flask_sqlalchemy-based database. I would like to read input values from a pre-defined list as advertised by this framework's benchmarking generator type 'fixed_sequence', but it's only picking up the first element of the list.
Here is the issue that explains my problem in detail, with an example: https://github.com/svanoort/pyresttest/issues/264
Any pointer in the right direction will be greatly appreciated
I looked into the code, it is jsut a bug, this feature was never used by anyone.
https://github.com/svanoort/pyresttest/blob/master/pyresttest/generators.py#L100
instead of:
```
def factory_fixed_sequence(values):
""" Return a generator that runs through a list of values in order, looping after end """
def seq_generator():
my_list = list(values)
i = 0
while(True):
yield my_list[i]
if i == len(my_list):
i = 0
return seq_generator
It should be:
def factory_fixed_sequence(values):
""" Return a generator that runs through a list of values in order, looping after end """
def seq_generator():
my_list = list(values)
i = 0
while(True):
yield my_list[i]
i += 1
if i == len(my_list):
i = 0
return seq_generator
```
The i += 1 is missing

Recursively Generating a List of n choose k combinations in Python - BUT return a list

I'm attempting to generate all n choose k combinations of a list (not checking for uniqueness) recursively by following the strategy of either include or not include an element for each recursive call. I can definitely print out the combinations but I for the life of me cannot figure out how to return the correct list in Python. Here are some attempts below:
class getCombinationsClass:
def __init__(self,array,k):
#initialize empty array
self.new_array = []
for i in xrange(k):
self.new_array.append(0)
self.final = []
self.combinationUtil(array,0,self.new_array,0,k)
def combinationUtil(self,array,array_index,current_combo, current_combo_index,k):
if current_combo_index == k:
self.final.append(current_combo)
return
if array_index >= len(array):
return
current_combo[current_combo_index] = array[array_index]
#if current item included
self.combinationUtil(array,array_index+1,current_combo,current_combo_index+1,k)
#if current item not included
self.combinationUtil(array,array_index+1,current_combo,current_combo_index,k)
In the above example I tried to append the result to an external list which didn't seem to work. I also tried implementing this by recursively constructing a list which is finally returned:
def getCombinations(array,k):
#initialize empty array
new_array = []
for i in xrange(k):
new_array.append(0)
return getCombinationsUtil(array,0,new_array,0,k)
def getCombinationsUtil(array,array_index,current_combo, current_combo_index,k):
if current_combo_index == k:
return [current_combo]
if array_index >= len(array):
return []
current_combo[current_combo_index] = array[array_index]
#if current item included & not included
return getCombinationsUtil(array,array_index+1,current_combo,current_combo_index+1,k) + getCombinationsUtil(array,array_index+1,current_combo,current_combo_index,k)
When I tested this out for the list [1,2,3] and k = 2, for both implementations, I kept getting back the result [[3,3],[3,3],[3,3]]. However, if I actually print out the 'current_combo' variable within the inner (current_combo_index == k) if statement, the correct combinations print out. What gives? I am misunderstanding something to do with variable scope or Python lists?
The second method goes wrong because the line
return [current_combo]
returns a reference to current_combo. At the end of the program, all the combinations returned are references to the same current_combo.
You can fix this by making a copy of the current_combo by changing the line to:
return [current_combo[:]]
The first method fails for the same reason, you need to change:
self.final.append(current_combo)
to
self.final.append(current_combo[:])
Check this out: itertools.combinations. You can take a look at the implementation as well.

Hash Function in Python generating error

So I am trying to get a grasp on Hash Functions and how exactly they work. I have the following code but I keep getting an error when I try and run the code.
import sys
def part_one():
foo = open('input_table.txt')
for line in foo:
id, make, model, year = line.split(",")
print(make, model)
tuple_list = (make+model,)
return tuple_list
def hash_one(num_buffers, tuple_list):
#part_one()
# A being the first constant prime number to multiply by
# B being the prime number that we add to A*sum_of_chars
tuple_list = part_one()
A = 3
B = 5
count = 0
for item in tuple_list:
for char in item:
# sum_of_chars is the total of each letter in the word
count = ord(char)
count = count + tuple_list
index = ((A * sum_of_chars + B)) % num_buffers
return index
if __name__ == '__main__':
input_table = sys.argv[1]
num_buffers = int(sys.argv[2])
chars_per_buffer = int(sys.argv[3])
sys.argv[4] = 'make'
sys.argv[5] = 'model'
lst = []
for item in range(4, len(sys.argv)):
lst.append(sys.argv[item])
print(lst)
hash_one(lst)
What is wrong with my code that is causing the error? Can anyone help me?
1
You're calling hash() with no arguments, you have to hash something.
A hash of a number will just return the same number though, so it's not very interesting. It's for hashing things like strings.
2
part_one returns nothing, therefore when you call tuple_list = part_one(), it's value is set to None, and you can't iterate though it.
3
Passing in a list through an argument then overwriting it doesn't make any sense anyway. If you want to return a list then use a return statement.
4
It's odd to set argument variables in code, they're for reading things from the command line.
5
(Not an error, but...)
You can use a slice (lst = sys.argv[4:]) as an easier way to get a sub-section of a list.

python list is not copying

In the following subset problem, I'm trying to make a copy of a list object
def findFourPlus(itemCount, seq, goal):
goalDifference = float("inf")
closestPartial = []
subset_sum(itemCount, seq, goal, goalDifference, closestPartial, partial=[])
print(closestPartial)
def subset_sum(itemCount, seq, goal, goalDifference, closestPartial, partial):
s = sum(partial)
# check if the partial sum is equals to target
if(len(partial) == itemCount):
if s == goal:
print(partial)
else:
if( abs(goal - s) < goalDifference):
goalDifference = abs(goal - s)
print(goalDifference)
print(partial)
print(closestPartial)
closestPartial = copy.deepcopy(partial)
for i in range(len(seq)):
n = seq[i]
remaining = seq[i+1:]
subset_sum(itemCount, remaining, goal, goalDifference, closestPartial, partial + [n])
in the subset function, I am trying to make a copy of the partial list to closestPartial. I've tried
closestPartial = partial
closestPartial = list[:]
closestPartial = list(partial)
closestPartial = copy.copy(partial)
closestPartial = copy.deepcopy(partial)
but in the end all of them seems to be futile. closestPartial remains to be an empty list (which is what I initiated to) for some reason
You are passing closestPartial in as a parameter, so the only thing that will work is an inplace update of its list. All of the examples you give replace the list that was in closestPartial with a new list. But since it wasn't the list you passed in, it doesn't update the real list.
Try:
closestPartial[:] = partial
You can get a feel for the problem by printing the list id before and after the operation.
print id(closestPartial)
...some operation
print id(closestPartial)
if the id changes, that means you created a new list and didn't update the one passed in.
EDIT
Seems I need a better explanation... when you call subset_sum, it creates a local variable called closestPartial that references whatever was passed in as a parameter, in this case a list known to the caller as closestPartial. You now have two variables pointing to the same list. If you reassign the variable, like in closestPartial = partial, those two variables now point to different lists. You didn't update the caller's pointer, you just changed the local variable. Instead, if you don't reassign, changes you make to the one list referenced by both variables are seen by the caller as well - because its the same list.
I suspect that your goalDifference is suffering from the same problem, if you change it in a function and then expect the changed value to somehow get back to the calling function.
Here's some (Python 2 style) code to illustrate what's happening:
#! /usr/bin/env python
def testA(update_func):
seq = []
num = 1
for _ in range(5):
newnum = update_func(seq, num)
print 'testA: ', num, seq, newnum
print
def testB(update_func):
seq = []
num = 1
for _ in range(5):
num = update_func(seq, num)
print 'testB: ', num, seq
print
def update0(seq, num):
#This creates a new list
seq = seq + [num]
num = num + 1
print 'update0:', num, seq
return num
def update1(seq, num):
#This updates the existing list
seq.append(num)
num += 1
print 'update1:', num, seq
return num
def update2(seq, num):
#This updates the existing list
seq[:] = seq + [num]
num += 1
print 'update2:', num, seq
return num
def update3(seq, num):
#This updates the existing list
seq += [num]
num += 1
print 'update2:', num, seq
return num
update_funcs = (update0, update1, update2, update3)
for f in update_funcs:
testA(f)
print '------\n'
for f in update_funcs:
testB(f)
Stack Overflow member Ned Batchelder's article Facts and myths about Python names and values has a good explanation, with cute diagrams.

Categories