Subset Sum in python using DP - python

I'm following this link to write a DP solution for Subset problem.
def subsetSum(input, target):
row, col = len(input)+1, target+1
db = [[False] * col] * row
for i in range(row):
db[i][0] = True
for i in range(1, row):
for j in range(1, col):
db[i][j]=db[i-1][j]
if db[i][j]==False and j>=input[i-1]:
db[i][j] = db[i-1][j-input[i-1]]
return db[i][j]
target = 5
input = [1,3,9,2]
subsetSum(input, target)
Interestingly after every iteration of "j", db[i-1] (the previous row where we are referring to the values) is also getting updated. I'm really lost whats happening here. Please suggest.
Please find this link for the printed statements.

The issue is in this line
db = [[False] * col] * row.
When you use the * operator, a copy of the original list is made that refers to the original list.
Consider the following example:
l = [[1]*5]*3
print(l) # prints [[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]
l[0][0] = 0
print(l) # prints [[0, 1, 1, 1, 1], [0, 1, 1, 1, 1], [0, 1, 1, 1, 1]]
Each inner list refers to the same object. Thus, when the first element of the first list is changed, all lists appear to change.
To remedy this, you can use a list comprehension:
l = [[1]*5 for _ in range(3)]
print(l) # prints [[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]
l[0][0] = 0
print(l) # prints [[0, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]
Specifically, you can replace your assignment to db with the following:
db = [[False]*col for _ in range(row)].

Related

How can I sum the first index and last index until the len(list) = 2?

Im trying to get a percentaje from a list of numbers, but it is a little different than the usual methods.
Bassically I need to sum the first index of the list with the last index of the same list. I want the script to do this repeatedly until the lenght of the list equals 2.
Something like this:
list = [1, 1, 2, 1, 1, 1, 1, 1, 1, 1]
list = [2, 2, 3, 2, 2]
list = [4, 4, 3]
list = [7, 4] #here the lenght = 2, so it stops.
final_list = [7, 4]
percentaje = f"%{final_list[0]}{final_list[1]}"
#OUTPUT
#"%74"
Can someone help me to do this? Im not so good with loops :(
This?
L = [1, 1, 2, 1, 1, 1, 1, 1, 1, 1]
while len(L) > 2:
new_L = [L[i]+L[len(L)-1-i] for i in range(len(L)//2)]
if len(L)%2:
new_L.append(L[len(L)//2]) # add middle term alone if any
L = new_L
print(f"%{L[0]}{L[1]}")
list1 = [1, 1, 2, 1, 1, 1, 1, 1, 1, 1]
while len(list1)!=2:
for i in range (0, int(len(list1)/2)):
list1[i] = list1[i] + list1[len(list1)-1]
list1 = list1[:-1]
print(list1)
output:
[7, 4]

Generate a list based on the index of another list

I have a list:
hash_table = [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
I want to change this to:
result = [[0, 0], [1, 2], [4, 5]]
How to generate:
array: [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
map: 0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0
# start to end, generate the result like `[int(start), int(end)]`
combine them:[[0, 0], [1, 2], [4, 5]]
0 and 1 wouldn't appear in pairs. So the numbers in result must be an integer.
What I have tried:
hash_table = [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
output = [[]]
for pre, next_ in zip(hash_table, hash_table[1:]):
output[-1].append(pre)
if {next_, pre} == {0, 1}:
output.append([])
output[-1].append(hash_table[-1])
# the output is [[1], [0], [1, 1, 1], [0, 0, 0], [1, 1, 1]]
start = index = 0
result = []
while index < len(output):
# output[index]
if output[0] != 0:
res.append([start, math.ceil(len(output[index]))])
# I don't know how to handle the list "output".
# I couldn't know it. My mind has gone blank
start += len(output[index])/2
Any good ideas? I thought I made it too complicated.
You can use itertools.groupby to group the 0s and 1s:
import itertools
hash_table = [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
result = []
cur_ind = 0
for (val, vals) in itertools.groupby(hash_table):
vals = list(vals) # itertools doesn't make it a list by default
old_ind = cur_ind
cur_ind += len(vals)
if val == 0:
continue
result.append([old_ind // 2, (cur_ind - 1) // 2])
print(result)
Essentially, itertools.groupby will give an iterator of [(1, [1]), (0, [0]), (1, [1, 1, 1]), (0, [0, 0, 0]), (1, [1, 1, 1])] (more or less). We can iterate through this iterator and keep track if the index we're on by adding the length of the sublist to the current index. If the value is 1, then we have a run of ones so we append it to the results. The old_ind // 2 is integer division and is equivalent to int(old_ind / 2).
You could use groupby from itertools library:
import itertools
hash_table = [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
s = "".join(map(str, hash_table)) # s = "10111000111"
gs = [(i, list(g)) for i, g in itertools.groupby(s)]
idx, result = 0, []
for i, g in gs: # i can be '1' or '0' (i.e, if the group consist in 1's or 0's)
if i == '1':
result.append([idx/2, (idx + len(g) - 1)/2])
idx += len(g)
return result

Matrix code not working even is similar to other

I was trying to create a code for a identity matrix and came out with this code:
def identidade(n):
i =0
l = [0] * n
l1 = [l.copy()] *n
for i in range (n):
l1[i][i] = 1
print(l1)
return l1
the output is:
[[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1]]
But i came along with a very similar code in the internet:
def identity(n):
m=[[0 for x in range(n)] for y in range(n)]
for i in range(0,n):
m[i][i] = 1
return m
that returns:
[[1, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 1, 0],
[0, 0, 0, 0, 1]]
So, my question is why my code doesn't return the correct output when selecting the element in the list of lists (l1[i][i] = 1) ?
tks in advance
The actual problem here is that you are using the * operator to create (as you hope) the copies of the '[l.copy()]' list, but it actually creates references. Using the copy() inside of the square brackets just breaks the connection to the original 'l' list, but does not solve the problem with creation of references to the newly created copy.
Just try to replace the * operator with for loop - this will solve your problem.

Python generator with numpy array

I'd like to create a generator that returns a array on fly. For example:
import numpy as np
def my_gen():
c = np.ones(5)
j = 0
t = 10
while j < t:
c[0] = j
yield c
j += 1
With a simple for loop:
for g in my_gen():
print (g)
I got what I want. But with list(my_gen()), I got a list which contains always the same thing.
I digged a little deeper and I find when I yield c.tolist() instead of yield c, everything went ok...
I just cannot explain myself how come this strange behaviour...
That is because c is always pointing to the same numpy array reference, you are just changing the element inside c in the generator function.
When simply printing, it prints the complete c array at that particular moment , hence you correctly get the values printed.
But when you are using list(my_gen()) , you keep adding the same reference to c numpy array into the list, and hence any changes to that numpy array also reflect in the previously added elements in the list.
It works for you when you do yield c.tolist() , because that creates a new list from the numpy array, hence you keep adding new list objects to the list and hence changes in the future to c does not reflect in the previously added lists.
An alternative generator returns a copy of a list. I'm retaining the np.ones() as a convenient way of creating the numbers, but converting it to a list right away (just once) (array.tolist() is relatively expensive).
I yield c[:] to avoid that 'current version' problem.
def gen_c():
c = np.ones(5,dtype=int).tolist()
j = 0
t = 10
while j < t:
c[0] = j
yield c[:]
j += 1
In [54]: list(gen_c())
Out[54]:
[[0, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[2, 1, 1, 1, 1],
[3, 1, 1, 1, 1],
[4, 1, 1, 1, 1],
[5, 1, 1, 1, 1],
[6, 1, 1, 1, 1],
[7, 1, 1, 1, 1],
[8, 1, 1, 1, 1],
[9, 1, 1, 1, 1]]
In [55]: np.array(list(gen_c()))
Out[55]:
array([[0, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[2, 1, 1, 1, 1],
[3, 1, 1, 1, 1],
[4, 1, 1, 1, 1],
[5, 1, 1, 1, 1],
[6, 1, 1, 1, 1],
[7, 1, 1, 1, 1],
[8, 1, 1, 1, 1],
[9, 1, 1, 1, 1]])
Ok, I think because in this generator, since I'm returning the same reference, generator yield always the same thing. If I yield np.array(c), that'll work...

how to use the b = [[1]*3]*3 in a proper way?

All,
code:
#=A===================
>>> b = [[1]*3]*3
>>> b
[[1, 1, 1], [1, 1, 1], [1, 1, 1]]
>>> b[0][0] = 0
>>> b
[[0, 1, 1], [0, 1, 1], [0, 1, 1]]
>>>
#=B===================
>>> b = [[1, 1, 1], [1, 1, 1], [1, 1, 1]]
>>> b[0][0] = 0
>>> b
[[0, 1, 1], [1, 1, 1], [1, 1, 1]]
Can I use the format may looks like "b = [[1]*3]*3" to get the same behavior as the "b = [[1, 1, 1], [1, 1, 1], [1, 1, 1]]" to reduce type in?
as "b = [[1]*3]*3" may return a "reference" based list, is it useful for daily works? any sample?
Thanks!
KC
The problem with your code is that it references the same list, so when you do b[0][0] = 0, you are really updating the value at the reference(in which all three arrays point to).
To get the desired results, I would do(using list comprehension):
>>> b = [[1]*3 for _ in range(3)]
>>> b
[[1, 1, 1], [1, 1, 1], [1, 1, 1]]
>>> b[0][0] = 0
>>> b
[[0, 1, 1], [1, 1, 1], [1, 1, 1]]
which actually recreates a list, so it references different lists, rather than the same one in your answer.
b = [[1]*3 for _ in range(3)] is equivalent to:
b = []
for _ in range(3):
b.append([1]*3)
You could build your list with a list comprehension instead:
b = [[1]*3 for _ in range(3)]
Your problem is that [[1, 1, 1]] * 3 creates a list of three references to the same [1, 1, 1] list. To do what you want, you have to create copies of [1, 1, 1] using list() or using slice notation:
first_list = [1, 1, 1]
second_list = [first_list[:], first_list[:], first_list[:]]
or
second_list = [list(first_list), list(first_list), list(first_list)]
To do the above in one line, you could do this:
second_list = [[1] * 3 for i in range(3)]
At section A:
you have a list with 3 items, but all items points to one object:
[1]*3
like this:
a = [1,1,1]
b = [a,a,a]
But at the section B , you have a list with different items

Categories