Finding subset sum using dynamic programming - python

I'm practicing Dynamic Programming and I'm struggling with debugging my code. The idea is to find if a sum is possible given a list of numbers. Here's my code:
a = [2,3,7,8,10]
sum = 11
b = list(range(1, sum+1))
m = [[False for z in range(len(b))] for i in range(len(a))]
for i, x in enumerate(b):
for j, y in enumerate(a):
if x==y:
m[j][i]=True
elif y<x:
m[j][i] = m[j-1][i]
else:
m[j][i] = m[j-1][i] or m[j-i][y-x]
for i, n in enumerate(m):
print(a[i], n)
And here is the output:
2 [False, True, False, False, False, False, False, False, False, False, False]
3 [False, True, True, False, False, False, False, False, False, False, False]
7 [False, True, True, False, True, True, True, False, False, False, False]
8 [False, True, True, False, True, True, True, True, False, False, False]
10 [False, True, True, False, True, True, True, True, True, True, False]
As I understand it, in my else statement, the algorithm is supposed to go up 1 row and then look at the difference of x and y and check if that slot is possible. So for instance in the most obvious case, the last element in the last row. That would be 10(y)-11(x) which should go all the way back to index 1 on the row above it, which as we know it's True. Not entirely sure what I'm doing wrong, any help in understanding this would be greatly appreciated.

Given you only feed positive values, I don't quite follow why you need a two dimensional list. You can simply use a 1d list:
coins = [2,3,7,8,10]
sum = 11
Next we initialize the list possible that states whether it is possible to obtain a certain value. We set possible[0] to True since this sum can be accomplished with no coins.
possible = [False for _ in range(sum+1)]
possible[0] = True
Now you iterate over each coin, and over the list and "upgrade" the value if possible:
for coin in coins:
for i in range(sum-coin,-1,-1):
if possible[i]:
possible[i+coin] = True
After that, the list possible shows for each value from 0 up to (and including sum) whether you can construct it. So if possible[sum] is True, the sum can be constructed.
For the given coins and sum, one gets:
>>> possible
[True, False, True, True, False, True, False, True, True, True, True, True]
So values 0, 2, 3, 5, 7, 8, 9, 10, 11 are constructible with the coins.
Edit: track the coins
You can also keep track of the coins by slightly modifying the code:
possible = [None for _ in range(sum+1)]
possible[0] = []
for coin in coins:
for i in range(sum-coin,-1,-1):
if possible[i] is not None:
possible[i+coin] = possible[i]+[coin]
Now possible looks like:
>>> possible
[[], None, [2], [3], None, [2, 3], None, [7], [8], [2, 7], [10], [3, 8]]
So 0 can be constructed with coins [] (no coins); 2 can be constructed with [2] (one coin with value 2), 3 with [3], 5 with [2,3], etc.

Related

How to get index of numpy multidimensional array in reverse order?

For example I have this np.array:
[[True, True, False, False]
[True, False, True, False]
[False, True, False, True]
[False, False, True, True]]
I want to get the the first index that is True in each row but counting from the back of the row. So expected output is a (4,) array corresponding to each row:
[1, # First index that is True is at index 1
2, # First index that is True is at index 2
3, # First index that is True is at index 3
3] # First index that is True is at index 3
a = np.array(
[[True, True, False, False],
[True, False, True, False],
[False, True, False, True],
[False, False, True, True]]
)
idx = a.shape[1] - 1 - np.argmax(a[:,::-1], axis=1)
np.argmax() will find the index of the highest value (for each row with axis=1). Since True is equivalent to 1 and False to 0, it'll record the index of the first True value. Simply reverse the rows so you find the "last" one and then substract this from the row length to account for the fact that you're counting backwards.
you can use python to reversea row and find an element: row.reverse() and row.find(True). in numpy you can use numpy.flip(row) to reverse a row and numpy.where(row == True) to find an element in a row.
import numpy as np
x = np.array([[True, True, False, False],
[True, False, True, False],
[False, True, False, True],
[False, False, True, True]])
result = []
for row in x:
row = np.flip(row)
index = np.where(row == True)
result.append(index[0][0])
print(result)

Count consecutive occurrences of True value in two dimentional numpy array (matrix) of Booleans [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I am new to Python, and now I am encountered with this question to count occurrences of consecutive True values in nested list or two dimensional numpy array filled with Booleans. Say I have a nested list like listX = [[True, False, True, True, True], [False, True, True, False, True], [False, True, False, False, False], [True, True, False, False, True]]. I want to count the occurrences of consecutive True values in each list, i.e. for listX[0], I would want the answer to be [1,3]. (In reality, I can have 10-25 flexible number of lists inside the nested list and each list contains 100 Boolean values.)
Based on the itertools mentioned in the answer for a previous question with one dimensional array
Count consecutive occurences of values varying in length in a numpy array, I can answer my simple example like this:
listX = [[True, False, True, True, True], [False, True, True, False, True], [False, True, False, False, False], [True, True, False, False, True]]
import numpy as np
arr = np.array(listX)
arr
>>> array([[ True, False, True, True, True],
[False, True, True, False, True],
[False, True, False, False, False],
[ True, True, False, False, True]])
import itertools
c1 = [sum(1 for _ in group) for key, group in itertools.groupby(arr[0]) if key]
c2 = [sum(1 for _ in group) for key, group in itertools.groupby(arr[1]) if key]
c3 = [sum(1 for _ in group) for key, group in itertools.groupby(arr[2]) if key]
c4 = [sum(1 for _ in group) for key, group in itertools.groupby(arr[3]) if key]
c1, c2, c3, c4
>>> ([1, 3], [2, 1], [1], [2, 1])
Since the example here just have 4 rows, I can code this way with indexing each row for 2D array, but in reality, I can have 10-25 flexible number of rows and each row contains 100 Boolean values. Is there any simpler way than this?
Convert your code applied to each row to the following lambda function:
myCount = lambda ar: [sum(1 for _ in group) for key, group in itertools.groupby(ar) if key]
Then assemble results for each row the following way:
res = []
for i in range(arr.shape[0]):
res.append(myCount(arr[i]))
To test also other cases, I extended your sample data with a row full of
False values and another row full of True:
array([[ True, False, True, True, True],
[False, True, True, False, True],
[False, True, False, False, False],
[ True, True, False, False, True],
[False, False, False, False, False],
[ True, True, True, True, True]])
The result for the above array is:
[[1, 3], [2, 1], [1], [2, 1], [], [5]]
I think, this result should be left as a pythonic nested list.
The reason is that Numpy does not support "jagged" arrays (with
rows of different length).

Numpy conditional check with stop

I need your help. I want to walk over a three dimensional array and check in one direction the distance between two elements, if it is smaller the value should be True. As soon as the distance gets higher than a certain value the rest of the values in this dimension should be set to False.
Here is an example in 1D:
a = np.array([1,2,2,1,2,5,2,7,1,2])
b = magic_check_fct(a, threshold=3, axis=0)
print(b)
# The expected output is :
> b = [True, True, True, True, True, False, False, False, False, False]
For a simple check, the result with a <= threshold would be and is not the expected output:
> b = [True, True, True, True, True, False, True, False, True, True]
Is there an efficient way to this with numpy? This whole thing is performance critical.
Thanks for your help!
One way would be to use np.minimum.accumulate along that axis -
np.minimum.accumulate(a<=threshold,axis=0)
Sample run -
In [515]: a
Out[515]: array([1, 2, 2, 1, 2, 5, 2, 7, 1, 2])
In [516]: threshold = 3
In [518]: print np.minimum.accumulate(a<=threshold,axis=0)
[ True True True True True False False False False False]
Another with thresholding and then slicing for 1D arrays -
out = a<=threshold
if ~out.all():
out[out.argmin():] = 0
Here's one more approach using 1st discrete difference:
In [126]: threshold = 3
In [127]: mask = np.diff(a, prepend=a[0]) < threshold
In [128]: mask[mask.argmin():] = False
In [129]: mask
Out[129]:
array([ True, True, True, True, True, False, False, False, False,
False])

Choose random elements from specific elements of an array

I have a 1D (numpy) array with boolean values. for example:
x = [True, True, False, False, False, True, False, True, True, True, False, True, True, False]
The array contains 8 True values. I would like to keep, for example, exactly 3 (must be less than 8 in this case) as True values randomly from the 8 that exist. In other words I would like to randomly set 5 of those 8 True values as False.
A possible result can be:
x = [True, True, False, False, False, False, False, False, False, False, False, False, True, False]
How to implement it?
One approach would be -
# Get the indices of True values
idx = np.flatnonzero(x)
# Get unique indices of length 3 less than the number of indices and
# set those in x as False
x[np.random.choice(idx, len(idx)-3, replace=0)] = 0
Sample run -
# Input array
In [79]: x
Out[79]:
array([ True, True, False, False, False, True, False, True, True,
True, False, True, True, False], dtype=bool)
# Get indices
In [80]: idx = np.flatnonzero(x)
# Set 3 minus number of True indices as False
In [81]: x[np.random.choice(idx, len(idx)-3, replace=0)] = 0
# Verify output to have exactly three True values
In [82]: x
Out[82]:
array([ True, False, False, False, False, False, False, True, False,
False, False, True, False, False], dtype=bool)
Build an array with the number of desired True and False, then just shuffle it
import random
def buildRandomArray(size, numberOfTrues):
res = [False]*(size-numberOfTrues) + [True]*numberOfTrues
random.shuffle(res)
return res
Live example

Python boolean operations on lists - inconsistent results

Suppose I have two lists:
>>> y
[False, False, True, False, True, False, False, True, False, True, False, False]
>>> z
[False, True, True, True, True, True, False, False, False, False, False, True]
Then I do the following:
>>> y or z
[False, False, True, False, True, False, False, True, False, True, False, False]
>>> z or y
[False, True, True, True, True, True, False, False, False, False, False, True]
Shouldn't the correct answer be as shown below?
[False, True, True, True, True, True, False, True, False, True, False, True]
I also get incorrect answers with and:
>>> y and z
[False, True, True, True, True, True, False, False, False, False, False, True]
>>> z and y
[False, False, True, False, True, False, False, True, False, True, False, False]
I tested 1s and 0s with odd results:
>>> y=[0,0,0,0,0]
>>> z=[1,1,1,1,1]
>>> y or z
[0, 0, 0, 0, 0]
>>> z or y
[1, 1, 1, 1, 1]
>>> y and z
[1, 1, 1, 1, 1]
>>> z and y
[0, 0, 0, 0, 0]
What am I doing incorrectly?
y or z doesn't behave how you think it does, on the individual elements. Instead, it evaluates the 'truthiness' of the first argument (y). Since y is a non-empty list, it evaluates to true. The overall statement then evaluates to y.
Similarly, z or y first looks to see if z is truthy (which it is, because it's a non empty list). Thus the statement evaluates to z without ever looking at y or the elements within it.
Here are some clearer examples:
>>> [1,2,3,4] or [5,6,7,8]
[1, 2, 3, 4]
>>> ['this','is','a','list'] or ['and','here','is','another']
['this', 'is', 'a', 'list']
An empty list evaluates as 'false-y', so in this care, the right hand list is the value of the statement:
>>> [] or ['and','here','is','another']
['and', 'here', 'is', 'another']
Swapping the order of the lists shows that the first one to evaluate as true will be the result:
>>> ['and','here','is','another'] or ['this','is','a','list']
['and', 'here', 'is', 'another']
To achieve what you want, you could do a list comprehension like
[
y_item or z_item
for y_item, z_item
in zip(y, z)
]
The correct approach for or operation:
[a or b for a, b in zip(y, z)]
The correct approach for and operation:
[a and b for a, b in zip(y, z)]
None, False, 0, '', (), [], {} and few more (mentioned here -> Truth Value Testing) are considered False.
here's some example:
[] is False, [False] is True since its not empty, check using
bool([False])
>>> [] and [False]
[]
>>> bool([] and [False])
False
[] is False, [False] is True, hence Here True
>>> [] or [False]
[False]
>>> bool([] or [False])
True

Categories

Resources