bottom up dynamic programming - python

I was trying to write a dynamic programming that counts the number of ways in which the road can be paved by using stones that are 2, 3, 5 meters. When I put 2, it gave me an error and starting from 2 to 20, it was supposed to give an output of
1, 1, 1, 3, 2, 5, 6, 8, 14, 16, 27, 36, 51, 77, 103, 155, 216, 309,
448
My code gives this result when I start the input from 3. Did I misunderstand something here?
def waysRoad(n):
if n<0:
return 0
dp = [0] * (n+1)
dp[0] = 1
dp[1] = 0
for i in range(2, n):
sum = 0
if i >=2:
sum += dp[i-2]
if i >=3:
sum += dp[i-3]
if i >=5:
sum += dp[i-5]
dp[i] = sum
return dp[i]

To fill n-th list entry, you need to use loop limit n+1:
for i in range(2, n + 1):
also it is worth to change return to
return dp[n]

Related

Maximum increasing subsequence

I am trying to solve the longest increasing subsequence problem using recursion.
My code below seems logical to me, but it is not correct.
Where the recursion is missing up with the problem in the code?
def max_increasing_subseq(seq):
if len(seq) == 1:
return 1
if not seq:
return 0
if is_increasing(seq):
return len(seq)
global_max = 0
for i in range(1, len(seq)):
new_seq = seq[:i] + seq[i + 1 :]
size = max_increasing_subseq(new_seq)
global_max = max(global_max, size)
return global_max
def is_increasing(s):
return s == sorted(s)
print(max_increasing_subseq([4, 18, 20, 10, 12, 15, 19])) # --> 5 Correct
print(max_increasing_subseq([12, 9, 2, 5, 4, 32, 90, 20])) # --> 3 Wrong, Correct= 6
print(max_increasing_subseq([42, 50, 51, 60, 55, 70, 4, 5, 70])) # --> 6 Wrong, Correct= 5

How to find the max array from both sides

Given an integer array A, I need to pick B elements from either left or right end of the array A to get maximum sum. If B = 4, then you can pick the first four elements or the last four elements or one from front and three from back etc.
Example input:
A = [5, -2, 3, 1, 2]
B = 3
The correct answer is 8 (by picking 5 from the left, and 1 and 2 from the right).
My code:
def solve(A, B):
n = len(A)
# track left most index and right most index i,j
i = 0
j = n-1
Sum = 0
B2 = B # B for looping and B2 for reference it
# Add element from front
for k in range(B):
Sum += A[k]
ans = Sum
# Add element from last
for _ in range(B2):
# Remove element from front
Sum -= A[i]
# Add element from last
Sum += A[j]
ans = max(ans, Sum)
return ans
But the answer I get is 6.
Solution
def max_bookend_sum(x, n):
bookends = x[-n:] + x[:n]
return max(sum(bookends[i : i + n]) for i in range(n + 1))
Explanation
Let n = 3 and take x,
>>> x = [4, 9, -7, 4, 0, 4, -9, -8, -6, 9]
Grab the "right" n elements, concatenate with the "left" n:
>>> bookends = x[-n:] + x[:n]
>>> bookends # last three elements from x, then first three
[-8, -6, 9, 4, 9, -7]
Take "sliding window" groups of n elements:
>>> [bookends[i : i + n] for i in range(n + 1)]
[[-8, -6, 9], [-6, 9, 4], [9, 4, 9], [4, 9, -7]]
Now, instead of producing the sublists sum them instead, and take the max:
>>> max(sum(bookends[i : i + n]) for i in range(n + 1))
22
For your large array A from the comments:
>>> max(sum(bookends[i : i + n]) for i in range(n + 1))
6253
Solution based on sum of the left and right slices:
Data = [-533, -666, -500, 169, 724, 478, 358, -38, -536, 705, -855, 281, -173, 961, -509, -5, 942, -173, 436, -609,
-396, 902, -847, -708, -618, 421, -284, 718, 895, 447, 726, -229, 538, 869, 912, 667, -701, 35, 894, -297, 811,
322, -667, 673, -336, 141, 711, -747, -132, 547, 644, -338, -243, -963, -141, -277, 741, 529, -222, -684,
35] # to avoid var shadowing
def solve(A, B):
m, ln = None, len(A)
for i in range(B):
r = -(B-i-1) # r is right index to slice
tmp = sum(A[0:i + 1]) + sum(A[r:]) if r < 0 else 0
m = tmp if m is None else max(m, tmp)
return m
print(solve(Data, 48)) # 6253
A recursive approach with comments.
def solve(A, B, start_i=0, end_i=None):
# set end_i to the index of last element
if end_i is None:
end_i = len(A) - 1
# base case 1: we have no more moves
if B == 0:
return 0
# base case 2: array only has two elemens
if end_i - start_i == 1:
return max(A)
# next, we need to choose whether to use one of our moves on
# the left side of the array or the right side. We compute both,
# then check which one is better.
# pick the left side to sum
sum_left = A[start_i] + solve(A, B - 1, start_i + 1, end_i)
# pick the right side to sum
sum_right = A[end_i] + solve(A, B - 1, start_i, end_i - 1)
# return the max of both options
return max(sum_left, sum_right)
arr = [5, -2, 3, 1, 2]
print(solve(arr, 3)) # prints 8
The idea is if we have this list:
[5, 1, 1, 8, 2, 10, -2]
Then the possible numbers for B=3 would be:
lhs = [5, 1, 1] # namely L[+0], L[+1], L[+2]
rhs = [2, 10, -2] # namely R[-3], R[-2], R[-1]
The possible combinations would be:
[5, 1, 1] # L[+0], L[+1], L[+2]
[5, 1, -2] # L[+0], L[+1], R[-1]
[5, 10, -2] # L[+0], R[-2], R[-1]
[2, 10, -2] # R[-3], R[-2], R[-1]
As you can see, we can easily perform forward and backward iterations which will start from all L (L[+0], L[+1], L[+2]), and then iteratively replacing the last element with an R (R[-1], then R[-2], then R[-3]) up until all are R (R[-3], then R[-2], then R[-1]).
def solve(A, B):
n = len(A)
max_sum = None
for lhs, rhs in zip(range(B, -1, -1), range(0, -(B+1), -1)):
combined = A[0:lhs] + (A[rhs:] if rhs < 0 else [])
combined_sum = sum(combined)
max_sum = combined_sum if max_sum is None else max(max_sum, combined_sum)
return max_sum
for A in [
[5, 1, 1, 8, 2, 10, -2],
[5, 6, 1, 8, 2, 10, -2],
[5, 6, 3, 8, 2, 10, -2],
]:
print(A)
print("\t1 =", solve(A, 1))
print("\t2 =", solve(A, 2))
print("\t3 =", solve(A, 3))
print("\t4 =", solve(A, 4))
Output
[5, 1, 1, 8, 2, 10, -2]
1 = 5
2 = 8
3 = 13
4 = 18
[5, 6, 1, 8, 2, 10, -2]
1 = 5
2 = 11
3 = 13
4 = 20
[5, 6, 3, 8, 2, 10, -2]
1 = 5
2 = 11
3 = 14
4 = 22
public int solve(int[] A, int B) {
int sum = 0;
int i = 0;
int n = A.length -1;
for (int k = 0; k < B; k++){
sum += A[k];
}
int ans = sum;
int B2 = B -1;
for (int j = n; j > n -B; j--){
sum -= A[B2];
sum += A[j];
ans = Math.max(ans, sum);
B2--;
}
return ans;
}
}

Find all unique combinations of a fixed size to reach a given average range

I have a range of integers e.g.
big_list = [1, 2, ..., 100]
and I need to find all fixed length subsets of the numbers in this range that have an average within k of 50 (like 45-55) for k=5. e.g. we have a fixed size of 6 with an average of around 50
sample = [71, 20, 23, 99, 25, 60]
The problem is that the lists have to be unique, with no repeated numbers.
The order doesn't matter, so [71, 20, 23, 99, 25, 60] and [20, 71, 23, 99, 25, 60] is just one combination.
I was thinking of just using itertools to generate all combinations and filtering out based on my criteria. But the run time for that would be really bad as the big list of numbers could range from a size 10 to a size 400.
How can I generate a set of lists with the above criteria
Order is trivial to address.
Just make an arrangement with contraints that i-th number is greater than (i-1)-th. In below algorithm you recurse by incrementing left by 1
To get the average in between 45 and 55 consider the recursive formula
import math
# left: previous number is left-1
# s: current sum
# d: depth
# c: combination
def fill(left, s, d, c):
if d == 0:
return print(c, sum(c)/n)
# constraint c_sum >= 45*n
# we look for minimal i such that
# sum + i + 100+99+...+(100-(depth-1)+1) >= 45*n
# sum + i + 100*(d-1) - (d-2)(d-1)/2 >= 45*n
# i >= 45*n - 100*(d-1) - (d-2)(d-1)/2 - sum
#
# constraint c_sum <= 55*n
# we look for maximal right such that
# sum + i + (i+1)+...+(i+(d-1)) <= 55*n
# sum + (d-1)*i + d(d-1)/2 <= 55*n
# i <= ( 55*n - d(d-1)/2 - sum )/(d-1)
minleft = max(left, math.ceil(minBound*n - 100*(d-1) - (d-2)*(d-1)/2 - s))
if d == 1:
maxright = min(100, maxBound*n-s)
else:
maxright = min(100, math.floor(( maxBound*n - d*(d-1)/2 - s )/(d-1)) )
for i in range(minleft, maxright+1):
newsum = s + i
c[d-1] = i
fill(i+1, newsum, d-1, c)
n = 6
minBound = 45
maxBound = 55
fill(0, 0, n, [0]*n)
after further comments, op is not interested at all into combinations as above but in combinations such that no number can appear twice across all combinations
algo can be reduced as the very basic one:
n = 300
c = list(range(1, n))
while len(c) >= 6:
print([c.pop(), c.pop(), c.pop(), c.pop(0), c.pop(0), c.pop(0)])
You can use recursion with a generator:
def combo(d, k, c = []):
if len(c) == 6:
yield c
else:
for i in d:
_c = (sum(c)+i)/float(len(c)+1)
if i not in c and (len(c) + 1 < 6 or 50-k <= _c <= 50+k):
yield from combo(d, k, c+[i])
Of course, as #japreiss pointed out, this problem will produce a very bad worst-case time complexity. A possible workaround, however, is to treat combo as an iterator pool, and simply access the produced combinations on demand elsewhere in your code. For instance, to access the first 100 results:
result = combo(range(1, 100), 5)
for _ in range(100):
print(next(result))
Output:
[1, 2, 3, 67, 98, 99]
[1, 2, 3, 67, 99, 98]
[1, 2, 3, 68, 97, 99]
[1, 2, 3, 68, 98, 99]
[1, 2, 3, 68, 99, 97]
[1, 2, 3, 68, 99, 98]
[1, 2, 3, 69, 96, 99]
[1, 2, 3, 69, 97, 98]
[1, 2, 3, 69, 97, 99]
[1, 2, 3, 69, 98, 97]
...

i need help getting stDev without using importmath, python

### import math
def mean(values):
return sum(values)*1.0/len(values)
def std():
pass
print(std())
def std(values):
length = len(values)
if length < 2:
return("Standard deviation requires at least two data points")
m = mean(values)
total_sum = 0
for i in range(length):
total_sum += (values[i]-m)**2
under_root = total_sum*1.0/length
return math.sqrt(under_root)
vals = [5]
stan_dev = std(vals)
print(stan_dev)
values = [1, 2, 3, 4, 5]
stan_dev = std(values)
print(stan_dev)
__________________________________________________________________________
lst = [3, 19, 21, 1435, 653342]
sum = reduce((lambda x, y: x +y), lst)
print (sum)
# list = [3, 19, 21, 1435, 653342]
i need to be able to get the stDev without using sum or len
i need to 'unpack' the stDev ???
You can do it with two loops (there are shorter ways but this is simple):
arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# Calculate the mean first
N, X = 0, 0
for xi in arr:
N += 1
X += xi
mean = X/N
# Calculate the standard deviation
DSS = 0
for xi in arr:
DSS += (xi - mean)**2
std = (DSS/N)**(1/2)
Outputs 4.5 for mean and 2.872 for std.

Python recursive function for Pascal's Triangle

I'm trying to do a recursive function that generates a pascal's triangle up till the nth row, n being the user input. This is my code so far:
def printPascal(l,n):
while n != 1:
temp = [None]*(len(l)+1)
temp[0] = 1
temp[len(l)] = 1
for i in range(1,len(temp)-1):
temp[i] = l[i] + l[i-1]
l = temp
print(temp)
n = n-1
printPascal(l,n)
n = int(input("Enter a value for n:"))
l = [1,1]
printPascal(l,n)
And this is the error it gives me:
Traceback (most recent call last):
File "C:\Users\User\Desktop\test.py", line 16, in <module>
printPascal(l,n)
File "C:\Users\User\Desktop\test.py", line 11, in printPascal
printPascal(l,n)
File "C:\Users\User\Desktop\test.py", line 7, in printPascal
temp[i] = l[i] + l[i-1]
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
The thing is i kinda understand the issue and have tried tracing it to no avail. I know that somehow in the temp[i] = l[i] + l[i-1] code either the l[i] or l[i -1] is a "None" and i don't know why.
Thanks for your time and help in this little predicament of mine.
You have too much of your code inside the "for" loop. You replace the value of l with the value of temp before you've finished filling in all the values in temp.
The for loop should only contain the first statement.
There was a little indentation error in your code.
def printPascal(l,n):
while n != 1:
temp = [0]*(len(l)+1)
temp[0] = 1
temp[len(l)] = 1
for i in range(1,len(temp)-1):
temp[i] = l[i] + l[i-1]
l = temp
print(temp)
n = n-1
printPascal(l,n)
Instead of fixing your code, I'll show you an optimized approach:
def pascal_triangle(n, triangle=[[1]]):
if n > len(triangle):
last_row = triangle[-1]
next_row = [a+b for (a, b) in zip([0] + last_row, last_row + [0])]
return pascal_triangle(n, triangle + [next_row])
return triangle
How you can run it:
n = int(input("Pascal's triangle size: "))
print(*pascal_triangle(n), sep="\n")
Example output for input 9:
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
Or using a prettier printing command:
print(*[" ".join(map(str, line)).center(40) for line in pascal_triangle(9)], sep="\n")
It would look like this:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
See this code running on ideone.com
You get this error because you not calculate and you not return sufficient information to the function.
try to debug or to follow your code..
You will fined the problem easily :)

Categories

Resources