Python lambda dp calculation - python

I am trying to understand below code snippet, for each value of i, what are all the two arguments passed to min()? appreciate if you could explain how the dp[i] computation could be expanded?
class Solution(object):
def coinChange(self, coins, amount):
MAX = float('inf')
dp = [0] + [MAX] * amount
print(dp)
for i in range(1, amount + 1):
dp[i] = min([dp[i - c] if i - c >= 0 else MAX for c in coins]) + 1
print(dp)
return [dp[amount], -1][dp[amount] == MAX]

A lot of things are happening here.
The min() method is receiving a python list as an argument.
This python list is created through list comprehension. It helps to start from the back. The list comprehension is saying "For every value c in the iterable coins, append the value dp[i - c] to my list if the boolean i - c >=0 is True. Otherwise, append the value MAX.
In other words, the line [dp[i - c] if i - c >= 0 else MAX for c in coins] is equivalent to
def makelist(dp,i,MAX,coins):
ret = []
for c in coins:
if i - c >= 0:
ret.append(dp[i-c])
else:
ret.append(MAX)
return ret
The min() method will be performed on this list once it is complete, which simply find the smallest value in the list

Related

Solving the "firstDuplicate" question in Python

I'm trying to solve the following challenge from codesignal.com:
Given an array a that contains only numbers in the range from 1 to a.length, find the first duplicate number for which the second occurrence has the minimal index. In other words, if there are more than 1 duplicated numbers, return the number for which the second occurrence has a smaller index than the second occurrence of the other number does. If there are no such elements, return -1.
Example
For a = [2, 1, 3, 5, 3, 2], the output should be
firstDuplicate(a) = 3.
There are 2 duplicates: numbers 2 and 3. The second occurrence of 3 has a smaller index than the second occurrence of 2 does, so the answer is 3.
For a = [2, 4, 3, 5, 1], the output should be
firstDuplicate(a) = -1.
The execution time limit is 4 seconds.
The guaranteed constraints were:
1 ≤ a.length ≤ 10^5, and
1 ≤ a[i] ≤ a.length
So my code was:
def firstDuplicate(a):
b = a
if len(list(set(a))) == len(a):
return -1
n = 0
answer = -1
starting_distance = float("inf")
while n!=len(a):
value = a[n]
if a.count(value) > 1:
place_of_first_number = a.index(value)
a[place_of_first_number] = 'string'
place_of_second_number = a.index(value)
if place_of_second_number < starting_distance:
starting_distance = place_of_second_number
answer = value
a=b
n+=1
if n == len(a)-1:
return answer
return answer
Out of the 22 tests the site had, I passed all of them up to #21, because the test list was large and the execution time exceeded 4 seconds. What are some tips for reducing the execution time, while keeping the the code more or less the same?
As #erip has pointed out in the comments, you can iterate through the list, add items to a set, and if the item is already in a set, it is a duplicate that has the lowest index, so you can simply return the item; or return -1 if you get to the end of the loop without finding a duplicate:
def firstDuplicate(a):
seen = set()
for i in a:
if i in seen:
return i
seen.add(i)
return -1
Create a new set and find its already in the new list, if its there return the element:
def firstDuplicate(a):
dup = set()
for i in range(len(a)):
if a[i] in dup:
return a[i]
else:
dup.add(a[i])
return -1
This is just an idea, I didn't verify it but it should work. It seems there's no memory limit but just a time limit. Therefore using space to trade time is probably a practical way to do this. The computation complexity is O(n). This algorithm also depends on the condition that the number range is between 1 to len(a).
def first_duplicate(a):
len_a = len(a)
b = [len_a + 1] * len_a
for i, n in enumerate(a):
n0 = n - 1
if b[n0] == len_a + 1:
b[n0] = len_a
elif b[n0] == len_a:
b[n0] = i
min_i = len_a
min_n = -1
for n0, i in enumerate(b):
if i < min_i:
min_i = i
min_n = n0 + 1
return min_n
Update:
This solution is not as fast as the set() solution by #blhsing. However, it may not be the same if it was implemented in C - it's kinda unfair since set() is a built-in function which was implemented in C as other core functions of CPython.

Getting an index error in a python fibonacci program

I just started "Algorithmic toolbox" on Coursera, and was writing their version of the Fibonacci program in python 2.7
def fibonacci(n):
F = []
F[0] = 0
F[1] = 1
for i in range(0,n):
F[i] = F[i-1] + F[i-2]
return F[n]
fibonacci(3)
But, I keep getting this error:
Traceback (most recent call last):
File "fibonacci.py", line 11, in <module>
fibonacci(3)
File "fibonacci.py", line 3, in fibonacci
F[0] = 0
IndexError: list assignment index out of range
You can't create new elements in a Python list by assigning to non-existing indices. Your list is empty, so indices 0 and 1 don't exist.
Use list.append() instead:
def fibonacci(n):
F = [0, 1] # a list with two initial elements
for i in range(2, n + 1):
F.append(F[i-1] + F[i-2]) # add new value to the list
return F[n]
Note that the loop starts at 2, not 0, because you already have the values for 0 and 1. The stop argument to range() is not included so if you want to find the nth fibonacci number, you need to run the range up to n + 1.
Now the code works:
>>> def fibonacci(n):
... F = [0, 1]
... for i in range(2, n + 1):
... F.append(F[i-1] + F[i-2])
... return F[n]
...
>>> fibonacci(10)
55
Note that you don't really need to store all values in a list; you only need to track the last 2. You could use just two variables and swap these around for each iteration; one stores the
def fibonacci(n):
prev, curr = 0, 1 # two variables, one set to 0, the other to 1
for _ in range(n - 1): # run n - 2 times
prev, curr = curr, prev + curr
return curr
Note that this doesn't do any boundary tests on n, so for n = 0 the result is going to be incorrect (1 is returned rather than 0). This is easily remedied with a if n < 1: return 0 line at the top.
It's because when the program reaches F[0] it tries to evaluate what's in the 0th index of F. The trouble is that F is initially an empty list and thus has no 0th index. You want to append those elements using F.append(0) and F.append(1), instead.
You have to append values to the list, because its of size 0. Python lists do not autogrow when you add an item at the next index, you have to specify it, for example with the append function
F = []
F.append(0)
F.append(1)
# [0, 1]
# OR
F = [0, 1]
# [0, 1]
There are a couple problems with your code:
def fibonacci(n):
F = [] #3
F[0] = 0
F[1] = 1
for i in range(0,n):
F[i] = F[i-1] + F[i-2] #1
return F[n] #2
First iteration this will be F[0] = F[-1] + F[-2], which is well defined in Python but not what you want. You might want to review Python's indexing scheme.
F[n] refers to the n+1th element, since Python is 0 indexed. Range is [lower, upper), so it doesn't actually have i = n on the final iteration.
As Martijn commented below, the third issue is that you can't simply index into an empty list. You have to either do successive appends, or allocate the entire list as F = [0]*n.
you need to start your loop at 2 not 0.
def fibonacci(n):
F = [0,1]
if n<0:
return None
if n<2:
return F[n]
for i in range(2,n):
F[i] = F[i-1] + F[i-2]
return F[n]
fibonacci(3)

Python: Maximum recursion depth error

I am having a problem with 'Maximum recursion depth exceeded' in python
I converted a java(I dont know java so it wasn't easy) function to python function and it did work for small lists but when I use large lists I get that error. I tried to do sys.setrecursionlimit(10000) but it seem that there is a problem because it will not finish, maybe because I converted the java code to python in a wrong way.
this is the python code of the function
def fun(a, b):
inf = 10000
c=[]
boolean = [[0 for x in xrange(len(b))] for x in xrange(len(a))]
dp = [[inf for x in xrange(len(b))] for x in xrange(len(a))]
def maxMatching(i, j):
if i == -1:
return 0
if j == -1:
return inf
if dp[i][j] != inf:
return dp[i][j]
val1 = maxMatching(i, j - 1)
val2 = abs(a[i] - b[j]) + maxMatching(i - 1, j - 1)
if cmp(val1, val2) > 0:
dp[i][j] = val2
boolean[i][j] = True
else:
dp[i][j] = val1
return dp[i][j]
def add_to_list(i, j):
if i == -1 or j == -1:
return
if boolean[i][j]:
c.append(b[j])
add_to_list(i - 1, j - 1)
else:
add_to_list(i, j - 1)
maxMatching(len(a) - 1, len(b) - 1)
add_to_list(len(a) - 1, len(b) - 1)
return sorted(c, reverse=True)
a=[20, 19, 13]
b=[21, 20, 14, 11, 5]
c=fun(a, b)
assert c == [21, 20, 14]
the function should return a list from list b which are the nearest points from list a
I thought that convert this function to iterative will resolve the problem.
my question is , how to make that function 100% iterative instead of recursive ?
thanks
To remove the recursion, you need to make your functions iterative.
For add to list, this is easy. Something like this should work.
def add_to_list(i, j):
while i != -1 and j == -1:
if boolean[i][j]:
c.append(b[j])
i = i - 1
j = j - 1
else:
j = j - 1
For maxMatching, this is also possible, but it takes some more work. However, do you notice that your recursion builds the dp table from top left to bottom right? And that you use the values of the dp to calculate the value maxMatching more to the right and to the bottom?
So what you can do is to create a helper table (like dp and boolean) and construct that from top to bottom and left to right. For each of the cells you calculate the value based on the values as you would now, but instead of using recursion, you use the value from the helper table.
This method is called Dynamic Programming, which is building a solution based on the solutions of smaller problems. Many problems that can be defined using some form of mathematical resursion can be solved using Dynamic Programming. See http://en.wikipedia.org/wiki/Dynamic_programming for more examples.

Getting index error in the code in python

def fact(n):
fac = 1
while (n>1):
fac = fac*n
n -= 1
return fac
z = 0
t = int(raw_input())
nz = []
for i in range(0,t):
c = 0
n = int(raw_input())
z = fact(n)
z = list(str(z))
for j in range(len(z)-1,1,-1):
if z[j] != '0':
break
else:
c +=1
nz[i].append(c)
for k in range(0,t):
print nz[k]
Hello I am getting
Indexerror : index out of range at " nz[i].append(c)
This program should calculate trailing zeros in the factorial of N.
Can you also please help me optimize my code, so it can run also for large values of N?
nz is empty list. It doesn't have any elements, so nz[i] would always raise IndexError. Perhaps you meant nz.append(c) ie. add c at the end of nz.
This is how does append() work:
list.append(x)
Add an item to the end of the list; equivalent to a[len(a):] = [x].
so you may want to change nz[i].append(c) to nz.append(c), since your i index is already handled by the append function. You're actually assuming you have an i element in your list, which is false, since you are using an empty list
About optimizing, your problem is probably due to your recursion limit. Try import sys; sys.getrecursionlimit() in your python shell, you should see something like 1000 as result.
Swapping for an iterative version of the factorial function could be a start
def fact(n):
r = 1
for x in range (n):
r = r * (x+1)
return r

Finding median without using a sort function

As a homework assignment I have to write a script which finds the median of 3 given numbers without using a standard sort function of Python.
This is my first week in class and my first programming experience so I find it very difficult to get any further than I am right now.
Here's what I have so far:
def med3(a,b,c):
list = [a, b, c]
newlist = []
if list:
minimum = list[0]
for x in list:
if x < minimum:
minimum = x
newlist.append(minimum)
list.remove(minimum)
elif x >= minimum:
newlist.append(x)
list.remove(x)
return newlist[1]
This seems to do the trick, but only for the first two entries of the list. The loop doesn't include the third entry.
How can I make the script include all three entries?
Thanks in advance!
Sander
sum([a, b, c]) - min(a, b, c) - max(a, b, c) - no sorting!
You are modifying the list in-place while looping over it, which has consequences for what elements you see:
>>> numbers = [1,2,3]
>>> for i in numbers:
... if i == 2: numbers.remove(i)
... print i
...
1
2
Note how 3 is never printed; by removing the second entry in the list, we've shortened it by one element and the loop finds the list exhausted early.
Note that you don't need to loop over the items, a few simple comparisons will tell you what item is the median if you think about it for a second. :-)
There are a number of simpler ways to go about this, but as for your approach:
You're modifying list inside of your loop. Don't do that. :)
In your case, you should be removing elements from newlist:
def med3(a,b,c):
list = [a, b, c]
newlist = []
if list:
minimum = list[0]
for x in list:
if x < minimum:
minimum = x
newlist.pop()
newlist.append(minimum)
elif x >= minimum:
newlist.append(x)
return newlist[1]
But as an exercise, you might want to think about a few things:
Why are you putting the elements in a list and looping over them? What advantage does this have over comparing a,b,c with simply if statements?
Why the if list:?
The fastest way to do it:
def medianFast(a, b, c):
if a > b:
if b > c:
return b
elif a > c:
return c
else:
return a
else:
if b < c:
return b
elif a > c:
return a
else:
return c
Guarantees you 3 comparisons at the worst case and 2 comparisons in the best case. 2,5 comparisons in average.
Using ternary conditional we can write it shorter as:
def medianTernary(a, b, c):
return (b if b > c else (c if a > c else a)) if a > b else (b if b < c else (a if a > c else c))
If you could use sorting you would have the shortest version:
def medianSorted(a, b, c):
return sorted([a, b, c])[1]

Categories

Resources