Dynamic Programming Scope of Variable with python for the SubSets problem - python

I'm learning dynamic programming. I recently found this code to compute the amount of subsets which sum is equal to a given number. But I can't understand how mem value gets updated.
def count_sets(arr,total):
mem = {}
return dp(arr,total,len(arr)-1, mem)
def dp(arr,total, i, mem):
key = str(total)+':'+str(i)
if key in mem:
return mem[key]
if total == 0:
return 1
elif total < 0 or i < 0:
return 0
elif total < arr[i]:
to_return = dp(arr,total, i-1, mem)
else:
to_return = (dp(arr,total - arr[i], i-1, mem) \
+ dp(arr,total, i-1, mem))
mem[key] = to_return
return to_return
if __name__ == "__main__":
c = count_sets([2,4,6,10],16)
print(c) # print 2
I though the first call of dp(arr,total, i, mem) in the algorthm has {} for mem.
So if I'm only returning the amount of subsets so far (also known as to_return), why is mem being update if it's not returning, shouldn't its value only live in the scope of the function?
Could someone help me better understand the scope of the variable mem? and why it gets updated? thanks!
Another Example I tried to understand was:
def add(x):
x = x+1
def main():
a = 2
add(a)
print(a)#prints 2
main()

I'll answer my own question for future readers, thanks to #Frank I found out there are either mutable ( integers, floats, strings, Booleans, and tuples) or immutable (list, dictioanry) datatypes in python.
So global list or dictionary can be changed even when it’s used inside of a function, that's why the variable mem in my code is modified through the algorithm.

Related

Stacks and optimization - example from hackerrank

I have a question concerning stacks in Python. I tried to solve a Maximum Element task in Hackerrank:
You have an empty sequence, and you will be given N queries. Each query
is one of these three types:
1 x -Push the element x into the stack.
2 -Delete the element present at the top of the stack.
3 -Print the maximum element in the stack.
The first line of input contains an integer, N. The next N lines each
contain an above mentioned query. (It is guaranteed that each query is
valid.)
To solve it I wrote something like this:
class Stack:
def __init__(self):
self.items = []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop()
def maxEl(self):
return max(self.items)
s = Stack()
for i in range(int(input())):
n = input().split()
if n[0] == '1':
s.push(int(n[1]))
elif n[0] == '2':
s.pop()
else:
print(s.maxEl())
It works, but too slow apparently and I pass only 18 out of 28 testcases (because of timeout). I have found a similar solution, and it is fast enough, but I don't understand why:
class Stack:
def __init__(self):
self.arr = [0]
self.max = [0]
def push(self, data):
self.arr.append(data)
if self.max[-1] <= data:
self.max.append(data)
def pop(self):
if self.arr[-1] == self.max[-1]:
self.max.pop()
self.arr.pop()
N = int(input())
s = Stack()
for _ in range(N):
x = str(input())
if x[0] == '1':
s.push(int(x[2:]))
elif x[0] == '2':
s.pop()
else:
print(s.max[-1])
Can somebody explain me why my code isn't performing well? Thank you.
The two solutions are pretty similar, except for the code that returns the maximum element in the stack.
In your solution you use the max() function:
def maxEl(self):
return max(self.items)
This runs in O(n) time, since max() must check every element in the worst case.
In the other solution maximum values are stored in yet another stack, so getting the current maximum value is just an index operation, which runs in O(1) time:
s.max[-1]
There's also some cost associated with updating the stack of maximums on each push/pop, but those operations are still constant time.
Given the definition of the problem even the working solution is doing way too much. More specifically you need to remember ONLY the max in the stack; something like
s = []
for _ in range(N):
x = str(input())
if x[0] == '1':
v = int(x[2:])
s.append(v if len(s) == 0 else max(v, s[-1]))
elif x[0] == '2':
s.pop()
else:
print(s[-1])
should be sufficient.

Recursion program flow

I have some textbook code that calls itself recursively. I don't understand the program flow. Here is the code:
def Recur_Factorial_Data(DataArray):
numbers = list(DataArray)
num_counter = 0
list_of_results = []
for num_float in numbers:
n = int(num_float)
1. result = Recur_Factorial(n)
list_of_results.append(result)
def Recur_Factorial(num):
if num == 1:
2. return num
else:
result = num*Recur_Factorial(num-1)
3. return result
if num < 0:
return -1
elif num == 0:
return 0
else:
return 0
In Recur_Factorial_Data, I loop through the data elements and call Recur_Factorial, which returns its value back to the calling function (Recur_Factorial_Data). I would expect that the lines marked 2 ("return num") and 3 ("return result") would always return a value back to the calling function, but that's not the case. For example, where the initial value (from the array DataArray) is 11, the function repeatedly calls itself until num is 1; at that point, the program falls to the line marked 2, but it does not loop back to the line marked 1. Instead, it falls through to the next line. The same happened on the line marked 3 -- I would expect it to return the result back to the calling function, but it does that in some cases and not in others.
I hope this is enough description to understand my question -- I just don't know why each return does not loop back to return the result to the calling function.
EDIT: The question at Understanding how recursive functions work is very helpful, and I recommend it to anyone interested in recursion. My question here is a bit different -- I asked it in the context of the program flow of the Python code where the recursive function is called.
If it call itself recursively 10 times, it will be at the 10th level of recursion and should go back 10 times at the point where there was a recursive call with an intermediate result and only then exit from the recursive function to the place where it was called. Learn more about recursion
Also try to rearrange instructions in Recur_Factorial function in this way:
def Recur_Factorial(num):
if num < 0:
return -1
elif num == 0:
return 0
elif num == 1:
return num
else:
return num * Recur_Factorial(num-1)

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

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.

Instead of continue, rerun function

I'm wondering how to do the following in Python.
If I have a function with a for loop, it is possible to with an if statement to skip certain numbers.
This is an implementation of fisher-yates d got from activestate.com.
import random
def shuffle(ary):
a=len(ary)
b=a-1
for d in range(b,0,-1):
e=random.randint(0,d)
if e == d:
continue
ary[d],ary[e]=ary[e],ary[d]
return ary
Now continue simply goes to the next value for d. How can I, instead of doing continue, rerun the function with the original parameter ary?
Note that the function is just some example code, I'm curious on how to do this in general.
Also, maintaining a copy of the array might not be possible if the list is big, so thats not really a solution imo.
This is a common recursive pattern. However, your case is a little different than usual because here you need to make a copy of your input list to use when you recurse if the shuffling fails.:
import random
def shuffle(ary):
initial = ary[:]
a=len(ary)
b=a-1
for d in range(b,0,-1):
e=random.randint(0,d)
if e == d:
return shuffle(initial)
ary[d],ary[e]=ary[e],ary[d]
return ary
ary = [1,2,3,4,5,6]
print shuffle(ary)
Also note that Wikipedia gives a (non-recursive) python implementation of the very similar Sattolo's algorithm.
from random import randrange
def sattoloCycle(items):
i = len(items)
while i > 1:
i = i - 1
j = randrange(i) # 0 <= j <= i-1
items[j], items[i] = items[i], items[j]
return
If I read the article correctly, to re-acquire Fisher-Yates, you'd just do one simple change:
from random import randrange
def FisherYates(items):
i = len(items)
while i > 1:
i = i - 1
j = randrange(i+1) # 0 <= j <= i
items[j], items[i] = items[i], items[j]
return
def function(list):
len(list)-1
for i in range(len(list)-1,0,-1):
e= randint(0,i)
while e > i:
e= randint(0,i)
"do something to the list"
return array
?
def function(list):
for i in (a for a in range(len(list)-1,0,-1) if randint(0,a) > a):
#do something with list
#do something else with remainder.
Not exactly what you asked for. Just wanted to remind you of this possibility.
you can copy the parameter to a temp variable. then call the function with the temp variable and use return;
def function(list):
listCopy = list;
len(list)-1
for i in range(len(list)-1,0,-1):
e= randint(0,i)
if e > i:
return function(listCopy)
else
"do something with the list"
return array

Categories