1d array or 2d array when solving dynamic programming problems - python

My question is how can you identify when to use a 1d array or 2d array for a dynamic programming problem. For instance, I stumbled upon the problem number of ways to make change
Here is an example:
inputs n = 12 and denominations = [2, 3, 7],
suppose you can pick an unlimited (infinite) amount of coins of each of the denominations you have. In how many ways can you make change for 12. The answer is 4
I got to the answer using dynamic programming and here is my code
def numberOfWaysToMakeChange(n, denoms):
if n == 0 or len(denoms) == 0:
return 1
ways = [[0 for _ in range(n + 1)] for _ in range(len(denoms))]
for row in ways:
row[0] = 1
for i in range(n + 1):
if i % denoms[0] == 0:
ways[0][i] = 1
for i in range(1, len(denoms)):
for j in range(1, n + 1):
if denoms[i] > j:
ways[i][j] = ways[i - 1][j]
else:
ways[i][j] = ways[i - 1][j] + ways[i][j - denoms[i]]
return ways[-1][-1]
result = numberOfWaysToMakeChange(12, [2, 3, 7])
print(result)
But online I found an answer that works as well that looks like the following
ways = [0 for _ in range(n + 1)]
ways[0] = 1
for denom in denoms:
for amount in range(1, n+1):
if denom <= amount:
ways[amount] += ways[amount - denom]
return ways[n]
How can you identify when you can use a 1d array for these kind of questions?

Related

Stuck in writing python list reduction program

I am stuck in writing the python code for below problem, can anyone help to find the issue with the code is much appreciated.
List Reduction:
You are given a list of integers L having size N and an integer K. You can perform the following operation on the list at most k times:
Pick any two elements of the list.
Multiply any element by 2.
Divide the other element by 2, taking the ceiling if element is odd.
Note: that after such an operation, the list is changed and the changed list will be used in subsequent operations.
You need to minimize the sum of all elements present in the list after performing at most k such operations.
Input Format:
First line contains N K as described above.
The second line contains N space separated integers, representing the list initially,
Output Format:
Print the minimum possible sum of all elements in the list at the end, after performing at most k operations.
Code:
def solve (X, arr):
Sum = 0
largestDivisible, minimum = -1, arr[0]
for i in range(0,N):
Sum += arr[i]
if(arr[i]%X == 0 and largestDivisible < arr[i]):
largestDivisible = arr[i]
if arr[i] < minimum:
minimum = arr[i]
if largestDivisible == -1:
return Sum
sumAfterOperation = (Sum-minimum-largestDivisible+(X*minimum)+(largestDivisible//X))
return min(Sum,sumAfterOperation)
N=5
X =2
#map(int, input().split())
arr = [10, 7, 4, 2, 1]
#list(map(int, input().split()))
out_ = solve(X, arr)
print (out_)
output: 20
expected output: 19
Not optimal program.
Idea: Multiplying the minimal element and dividing the maximal element gives sequence with minimal total sum. Do you want process negative numbers? Does K take negative values?
K = int(input())
arr = list(map(int, input().split()))
for _ in range(K):
arr.sort()
arr[0] *= 2
arr[-1] = arr[-1] // 2 + arr[-1] % 2
print(sum(arr))
More effective solution.
K = int(input())
arr = list(map(int, input().split()))
for _ in range(K):
mn, mx = min(arr), max(arr)
mn_i = arr.index(mn)
if mn != mx:
mx_i = arr.index(mx)
else:
mx_i = arr.index(mx, mn_i+1)
arr[mn_i] *= 2
arr[mx_i] = arr[mx_i] // 2 + arr[mx_i] % 2
print(sum(arr))
And algorithmic solution.
K = int(input())
arr = list(map(int, input().split()))
for _ in range(K):
mn, mx = 0, 0
for i, x in enumerate(arr):
if x < arr[mn]:
mn = i
if x >= arr[mx]:
mx = i
arr[mn] *= 2
arr[mx] = arr[mx] // 2 + arr[mx] % 2
print(sum(arr))

What is the fastest way to multiply different numbers by consecutive numbers?

My main goal is to make this code faster, like converting it to an equation or something else
 This code multiplies the digits of the number system with 'n' and the
digits size of 'l', and provided that there is no digit less than the digit to the left
For example:
If n = 3 and l = 2
The result will be 25 which is the product of multiplying the cells of these lists :
[1, 1]
[1, 2]
[1, 3]
[2, 2]
[2, 3]
[3, 3]
def plus(num_list,n,index):
if (num_list[0] == n and index == 0): return 0
if num_list[index] < n:
num_list[index]+=1
num_list = num_list[:index+1]+[num_list[index] for x in range((len(num_list)-1)-index)]
else:
return plus(num_list,n,index-1)
return num_list
while True:
n = int(input('n = '))
l = int(input('l = '))
rez = 0
num_list = [1 for x in range(l)]
while num_list:
print(num_list)
m = 1
for i in num_list:
m*=i
rez+= m
num_list = plus(num_list,n,l-1)
print(rez)
Changing the code a bit to print out 10 values for each l from 1 to 10 gives:
l=1: 1,3,6,10,15,21,28,36,45,55,
l=2: 1,7,25,65,140,266,462,750,1155,1705,
l=3: 1,15,90,350,1050,2646,5880,11880,22275,39325,
l=4: 1,31,301,1701,6951,22827,63987,159027,359502,752752,
l=5: 1,63,966,7770,42525,179487,627396,1899612,5135130,12662650,
l=6: 1,127,3025,34105,246730,1323652,5715424,20912320,67128490,193754990,
l=7: 1,255,9330,145750,1379400,9321312,49329280,216627840,820784250,2758334150,
l=8: 1,511,28501,611501,7508501,63436373,408741333,2141764053,9528822303,37112163803,
l=9: 1,1023,86526,2532530,40075035,420693273,3281882604,20415995028,106175395755,477297033785,
l=10: 1,2047,261625,10391745,210766920,2734926558,25708104786,189036065010,1144614626805,5917584964655,
These numbers can be looked up in the online encyclopedia of integer sequences. Which gives sequences such as A001296, A001297 and A001298. They have a description "Sterling numbers of the second kind S(n+l, n)". Which are referenced in Mathworld and Wikipedia.
Formulas for the first few l's:
l==1: n*(1+n)/2
l==2: n*(1+n)*(2+n)*(1+3*n)/24
l==3: n^2 (n + 1)^2 (n + 2) (n + 3) / 48
l==4: (n+4)*(n+3)*(n+2)*(n+1)*n *(15*n^3 + 30*n^2 + 5*n - 2)/5760
PS: Sympy, Python's symbolic math library, has a function stirling to calculate its values. It only works with fixed integers, not with symbolic variables.
from sympy.functions.combinatorial.numbers import stirling
n = int(input('n = '))
l = int(input('l = '))
print(stirling(n+l, l))
for l in range(1, 11):
print([stirling(n+l,n) for n in range(1,21)])
The formula at Wikipedia can also be written using more basic sympy functions.
def s(n, k):
return Sum(((-1) ** (k - i)) * binomial(k, i) * i ** n / factorial(k), (i, 0, k)).doit()

Dynamic change-making algorithm that returns actual list of coins used

I'm trying to adjust the code from the wikipedia:
https://en.wikipedia.org/wiki/Change-making_problem#Implementation
To also output the list of coins used, not only the number of coins used. That is, for instance:
change_making([6, 8, 12], 52) outputs 5 which is correct (12+12+12+8+8 = 52).
The problem is that I want to get output in this format [12, 12, 12, 8, 8] instead of just 5 and I have no idea how to do that.
The code in question:
def _get_change_making_matrix(set_of_coins, r):
m = [[0 for _ in range(r + 1)] for _ in range(len(set_of_coins) + 1)]
for i in range(r + 1):
m[0][i] = i
return m
def change_making(coins, n):
"""This function assumes that all coins are available infinitely.
n is the number that we need to obtain with the fewest number of coins.
coins is a list or tuple with the available denominations."""
m = _get_change_making_matrix(coins, n)
for c in range(1, len(coins) + 1):
for r in range(1, n + 1):
# Just use the coin coins[c - 1].
if coins[c - 1] == r:
m[c][r] = 1
# coins[c - 1] cannot be included.
# We use the previous solution for making r,
# excluding coins[c - 1].
elif coins[c - 1] > r:
m[c][r] = m[c - 1][r]
# We can use coins[c - 1].
# We need to decide which one of the following solutions is the best:
# 1. Using the previous solution for making r (without using coins[c - 1]).
# 2. Using the previous solution for making r - coins[c - 1] (without using coins[c - 1]) plus this 1 extra coin.
else:
m[c][r] = min(m[c - 1][r], 1 + m[c][r - coins[c - 1]])
return m[-1][-1]
Any help/suggestion would be greatly appreciated.
------------- EDIT -------------
The solution (comments removed):
def _change_making(coins, n):
m = [[0 for _ in range(n + 1)] for _ in range(len(coins) + 1)]
for i in range(n + 1):
m[0][i] = i
for c in range(1, len(coins) + 1):
for r in range(1, n + 1):
if coins[c - 1] == r:
m[c][r] = 1
elif coins[c - 1] > r:
m[c][r] = m[c - 1][r]
else:
m[c][r] = min(m[c - 1][r], 1 + m[c][r - coins[c - 1]])
i = len(coins)
j = n
ret = {k: 0 for k in coins}
while j != 0:
if m[i][j - coins[i - 1]] == m[i][j] - 1:
ret[coins[i - 1]] += 1
j = j - coins[i - 1]
else:
i = i - 1
return ret
To find the closest * solution:
def change_making(coins, n):
try:
return _generate_packing(coins, n)
except:
return generate_packing(coins, n + 1)
For instance change_making([2, 5], 8)
{2: 2, 5: 1}
Because 9 is the closest possible solution.
By closest I mean a solution that is possible to satisfy but above the original request. For instance if we need to return £8 in change and we do not have the exact change, well, we will return £9 because we do have change for that.
Here are steps how you can do it -
1)Start with i=len(coins) and j=n ie end of your array(or list) m
2)Now we know a coin of value coins(i-1) is chosen if m[i][j] uses exactly one more coin than m[i][j-coins[i-1]].
3)If this doesnt happen we check the other coins(coins at lower index in list) for same condition.
Example-
At start we have value 52 and we have solved that it needs 5 coins using your your function.
We use first coin of 12 only if for value 40(ie 52 -12) we need 4 coins and similarly for for 2nd and 3rd 12 valued coin.
But we cant use fourth 12 coin as value 4(ie 16-12) cant be achieved using 1 coin.
Here is code snippet to do same(you can it use at end of your function instead of return statement) -
i=len(coins)
j = n
while(j!=0):
if m[i][j-coins[i-1]] == m[i][j]-1:
print(coins[i-1])
j=j-coins[i-1]
else:
i=i-1

python error - "IndexError: list index out of range"

I'm really new at Python so I apologize in advance if this is a really dumb question. Pretty much, I'm writing a longest common subsequence algorithm with dynamic programming.
Whenever I try to run it, I get the IndexError: list index out of range, and I don't know why because the array I'm adding values to never changes in size. Code snippet for clarity:
def LCS(sequence1, sequence2):
n = len(sequence1)
m = len(sequence2)
D = [[0 for num in range(0,n)]for number in range(0, m)]
for i in range(1, n):
for j in range(1, m):
if(sequence1[i] == sequence2[j]):
D[i][j] = D[i-1][j-1] + 1
else:
D[i][j] = max(D[i-1][j], D[i][j-1])
print D[n][m]
There seem to be two problems:
In the definition of D, you should swap n and m
D = [[0 for num in range(0, m)] for number in range(0, n)]
You have to print (or better: return) the last element of the matrix
return D[n-1][m-1] # or just D[-1][-1]
The issue is with total rows and columns of the matrix(D). Size should be (m+1)*(n+1) and then loop over the matrix. Otherwise you need to return D[m-1][n-1].
def LCS(sequence1, sequence2):
n = len(sequence1)
m = len(sequence2)
D = [[0 for num in range(0,n+1)]for number in range(0, m+1)]
for i in range(1, n+1):
for j in range(1, m+1):
if(sequence1[i-1] == sequence2[j-1]):
D[i][j] = D[i-1][j-1] + 1
else:
D[i][j] = max(D[i-1][j], D[i][j-1])
print D[n][m]
LCS('abcdef' , 'defjkl')

Fill in an array using loop with multiple variables (new to Python, old to C++ (back in the day))

Basically what I want to do is create something like this in python (this is basic idea and not actual code):
n = 3
i = n + 1
a = [1, 3, 3, 1]
b = [1, 2, 1]
while n > 1:
Check if n is even
- if n is even, then for all i in range(0,n), insert values into an array using the formula below
- b[n-i] = a[n-i-1] + a[n-i], this value will replace the previously given value of b[] above the code.
- Print out the array
- After each area is filled, n+=1, i=n+1 are applied, then the loop continues
Check if n is odd
- same process except formula is
- a[n-i] = b[n-i-1] + a[n-i], this value will replace the previously given value of a[] above the code.
- Print out the array
- After each area is filled, n+=1, i=n+1 are applied, then the loop continues
This process will loop and print each and continue on, the arrays will essentially look like this:
b = [1, 4, 6, 4, 1], a = [1 5, 10, 10, 5, 1], b = [1, 6, 15, 20, 20, 15, 6, 1], etc.
Here is the code that I currently have, however I'm getting an 'out of range' error.
n = 3
i = n + 1
b = [1, 2, 1]
a = [1, 3, 3, 1]
while n > 1:
if n%2==0:
print("even")
for i in range(0,n):
b[n-i].append(a[n-i-1]+a[n-i])
else:
print("odd")
for i in range(0,n):
print("yay")
a[n-i].append(b[n-i-1]+b[n-i])
if n%2==0:
print(b)
else:
print(a)
n +=1
i = n + 1
print("loop")
The random prints throughout the code are to test and see if it is even making it into the process. There were from a previous code and I just haven't removed them yet.
Hopefully you can help me, I can't find anything online about a loop that constantly increases the size of an array and fills it at the same time.
Sorry struggling with the code that's in the sample. From your description I can see that you want to generate Pascal's triangle. Here's a short snippet that will do this.
a = [1, 1]
for _ in range(10):
a = [1] + [x+y for (x,y) in zip(a[:-1], a[1:])] + [1]
print a
a[:-1] refers to the whole array except the last element and a[1:] refers to whole array except first element. zip combines first elements from each array into a tuple and so on. All that remains is to add them and pad the row with ones one the outside. _ is used to tell Python, I don't care about this variable - useful if you want to be explicit that you are not using the range value for anything except flow control.
Maria's answer is perfect, I think. If you want to start with your code, you can rewrite your code as below to get similar result. FYI.
n = 3
b = [1, 2, 1]
while 1 < n < 10:
if n % 2 == 0:
print("even")
b = [0] * (n + 1)
for i in range(0, n + 1):
if i == 0:
b[i] = a[0]
elif i == n:
b[i] = a[i - 1]
else:
b[n - i] = a[i - 1] + a[i]
else:
print("odd")
a = [0] * (n + 1)
for i in range(0, n + 1):
if i == 0:
a[i] = b[0]
elif i == n:
a[i] = b[i - 1]
else:
a[i] = b[i - 1] + b[i]
if n % 2 == 0:
print(b)
else:
print(a)
n += 1
print("loop")

Categories