Error in Longest Increasing Subsequence in Python - python

I am trying to implement the Longest Increasing Subsequence in Python by refering the video here.
I think I have done it correct. A Dry Run of the code also looks fine to me. But the output is incorrect.
d = [3, 2, 6, 4, 5, 1]
l = []
l.append([d[0]])
for i in range(1, len(d)):
l.append([])
for j in range(0, i):
if d[j] < d[i] and len(l[i]) < len(l[j]) + 1:
l[i] = l[j]
l[i].append(d[i])
print(l)
Expected Output: [[3], [2], [2, 6], [2, 4], [2, 4, 5], [1]]
Actual Output: [[3], [2, 6, 4, 5], [2, 6, 4, 5], [2, 6, 4, 5], [2, 6, 4, 5], [1]]
Any Help Would Be Appreciated!

This is just another referencing problem.
for i in range(1, len(d)):
l.append([])
for j in range(0, i):
if d[j] < d[i] and len(l[i]) < len(l[j]) + 1:
l[i] = l[j]
l[i].append(d[i])
Note the line l[i] = l[j], that makes l[i] the same list as l[j], so when you modify l[i] later, l[j] gets modified too. You probably want a copy here:
l[i] = l[j][:]
l[i] = list(l[j])
l[i] = l[j].copy() # Python 3.3 or up
These 3 lines are equivalent so pick one you like.

Related

How to get all consecutive sequences of numbers from given set of numbers?

I will start the description of the problem with an example so that the question is clear.
List of numbers: [1,2,3,4]
How can I get each successive string: [[1],[1,2],[1,2,3],[1,2,3,4],[2],[2,3],[2,3,4],[3],[3,4],[4]]
I've been trying to solve this problem for a while and managed to get [[1, 2, 3, 4], [2, 3, 4], [3, 4], [4]] with lots of useless repetitions.
list = [1,2,3,4]
i = 0
j = 0
tempList = []
sequancesList = []
while i < len(list):
while j < len(list):
tempList.append(list[j])
sequancesList.append(tempList)
j += 1
i += 1
j = i
tempList = []
def deleteDuplicates(list):
noduplist = []
for element in list:
if element not in noduplist:
noduplist.append(element)
return noduplist
finalSequancesList = deleteDuplicates(sequancesList)
print(finalSequancesList)
Here's how to do it:
list = [1,2,3,4]
sequancesList = []
for i in range(len(list)):
tmp = []
for j in range(i,len(list)):
tmp.append(list[j])
sequancesList.append(tmp[:])
print(sequancesList)
-> [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [2], [2, 3], [2, 3, 4], [3], [3, 4], [4]]
Here is a generator that does exactly that:
def iter_successive(input_list):
for i in range(len(input_list)):
for j in range(i+1,len(input_list)+1):
yield input_list[i:j]
>>> list(iter_successive(input_list))
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [2], [2, 3], [2, 3, 4], [3], [3, 4], [4]]
Comparable one-liner solution:
def iter_successive(input_list):
return (input_list[i:j] for i in range(len(input_list))
for j in range(i+1,len(input_list)+1))
Edit: As was stated in the comments, the approach below works only if the list contains whole, ascending numbers and not in other instances.
There are two ways to do this, either as a nested for-loop or a one-liner using list-comprehension. Both versions below:
Nested for-loop
nrs = list(range(1,5))
result = []
for i in nrs:
for j in range(i, max(nrs)+1):
result.append(list(range(i,j+1)))
print(result)
List-comprehension
result = [list(range(i,j+1)) for i in nrs for j in range(i, max(nrs)+1)]
print(result)

Question about returning combination N taken K

I am trying to list all combination of k length in a list 1 to n. I found this neat code that returns all the combinations.
def combs(a):
if len(a) == 0:
return [[]]
cs = []
for c in combs(a[1:]):
cs += [c, c+[a[0]]]
return cs
Is there a way I could modify this so that I could give a limit of the length of list it returns?
If:
k = 3
n = 5
it will only return combinations of length 3 from:
lst = [1, 2, 3, 4, 5]
hence the return would be:
[[1, 2], [1, 3], [2, 3]]
I am not allowed to use any import as a restriction
I added a new parameter k.
Note the line:
if len(c) >= k: return cs
This will break the loop early if it the array is greater than k.
def combs(a, k, l=0):
if len(a) == 0:
return [[]]
cs = []
for c in combs(a[1:], k, l+1):
cs += [c, c+[a[0]]]
if l == 0:
css = []
for i in cs:
if len(i) == k:
css += [i]
return css
return cs
print(combs([1, 2, 3, 4, 5], 3))
Hence, when k == 3, the code returns the following:
[[3, 2, 1], [4, 2, 1], [4, 3, 1], [4, 3, 2], [5, 2, 1], [5, 3, 1], [5, 3, 2], [5, 4, 1], [5, 4, 2], [5, 4, 3]]

Longest increasing subsequence, algorithm works wrong, no idea why

I made an implementation of Longest Increasing Subsequence (LIS) algorithm, as I see it would work, but results are totally mess.
def lis():
#D = map(int, raw_input().split())
D = [3, 2, 6, 4, 5, 1]
L = [[] for i in range(len(D))]
L[0].append(D[0])
for i in range(len(D)):
for j in range(0,i):
if D[i] > D[j]:
L[i] = L[j]
L[i].append(D[i])
print L
Returned result:
[[3], [2, 6, 4, 5], [2, 6, 4, 5], [2, 6, 4, 5], [2, 6, 4, 5], [1]]
What it should be:
[[3], [2], [2, 6], [2, 4], [2, 4, 5], [1]]
As I saw in debugger when we have:
L[i] = L[j]
Not only L[i] gets new values, but other lists on the main (L) list too...
I don't know how to avoid it. It looks that lists in Python are totally different than vectors languages from C family...
I'm fighting with this for a long time. Huge beer to someone who gonna find what is wrong :(
When you state L[i] = L[j] you do not copy the content of the list, you simply copy a reference: from now on L[i] and L[j] point to the same list and changes made through L[i] will reflect when you obtain L[j].
A simply fix is simply to copy the list:
def lis():
#D = map(int, raw_input().split())
D = [3, 2, 6, 4, 5, 1]
L = [[] for i in range(len(D))]
L[0].append(D[0])
for i in range(len(D)):
for j in range(0,i):
if D[i] > D[j]:
L[i] = list(L[j])
L[i].append(D[i])
print(L)
Now hoever your algorithm does not work anymore (it was not working in the first place nevertheless). When running your (fixed) code, you get:
>>> lis()
[[3, 3], [2], [2, 6], [2, 4], [2, 4, 5], [1]]
The 3 occurs twice in the first list, you can solve this by removing the .append before the for loop. So the final version is:
def lis():
#D = map(int, raw_input().split())
D = [3, 2, 6, 4, 5, 1]
L = [[] for i in range(len(D))] #removed the next line
for i in range(len(D)):
for j in range(0,i):
if D[i] > D[j]:
L[i] = list(L[j])
L[i].append(D[i])
print(L)
Which produces:
>>> lis()
[[3], [2], [2, 6], [2, 4], [2, 4, 5], [1]]
Note: based on your comment you use python-2.7, from python-3.x there is a method called .copy() on lists that you can call.
Important Note
The solution which is above is correct but if you try it out the result can be wrong for some instances.
For example - Longest Increasing Subsequence for numbers:
5 1 4 2 3 1 2 9 1
is
1 2 3 9
But this solution won't be on your L list returned by algorithm. There will be:
1 2 9
Max element from L list is needed. Then another element from D can be appended, so we have to add one more condition, here you go the correct code:
def lis():
#D = map(int, raw_input().split())
D = [5, 1, 4, 2, 3, 1, 2, 9, 1]
L = [[] for i in range(len(D))]
for i in range(len(D)):
for j in range(0,i):
if D[i] > D[j] and len(L[i]) < len(L[j])+1: #added condition
L[i] = list(L[j])
L[i].append(D[i])
print(L)
>>lis()
[[5], [1], [1, 4], [1, 2], [1, 2, 3], [1], [1, 2], [1, 2, 3, 9], [1]]

Pythonic way to generate a list of possible arrays

Given an array of numbers (e.g. [3, 5, 2]), I'm trying to generate a list of possible arrays that result from adding 1 to one entry in the array: [[4, 5, 2], [3, 6, 2], [3, 5, 3]].
I can get it done by the following, but wondering if there's a more pythonic way to get the result?
test = [3, 5, 2]
result = [t.copy() for _ in range(len(test))]
for index, _ in enumerate(result):
result[index][index] += 1
Here's how to do it with a list comprehension:
test = [3, 5, 2]
print [test[:i] + [v + 1] + test[i+1:] for i,v in enumerate(test)]
output
[[4, 5, 2], [3, 6, 2], [3, 5, 3]]
Here is another inline solution with list comprehension:
test = [3, 5, 2]
result = [[v+1 if i==j else v for j, v in enumerate(test)] for i in range(len(test))]
or, as noticed by PM 2Ring, you can exploit the fact that True == 1 and False == 0:
result = [[v + (i==j) for j, v in enumerate(test)] for i in range(len(test))]

Python : Generating cyclic permutations code (An unexpected code error to be clarified)

I didn't manage to correct a code I thought it would work for sure. Any advice to make the code functional is accepted.
Expected outputs of the following code is a list containing a cyclic permuation of the list
l = [1,2,3,4] (i.e : [[4, 1, 2, 3],[3, 4, 1, 2],[2, 3, 4, 1],[1, 2, 3, 4]])
Although what I get is : [[2, 3, 4, 1]]
The code :
def cycGen(l):
L=[]
while not(l in L) :
L.append(l)
for i in range(len(l)):
if l[i] == len(l) :
l[i]=1
else :
l[i] = 1 + l[i]
return L
print(cycGen([1,2,3,4]))
Another variation of the solution is to consider the following code wich seems unfortunatly not working either :
def cycGen(l):
L=[]
for k in range(len(l)):
L.append(l)
for i in range(len(l)):
if l[i] == len(l) :
l[i]=1
else :
l[i] = 1 + l[i]
return L
Help me with your generous knowlege sharing please.
You can use collections.deque:
from collections import deque
a = [1, 2, 3, 4]
d = deque(a)
for _ in range(len(a)):
d.rotate()
print(list(d))
Which gives you the output:
[4, 1, 2, 3]
[3, 4, 1, 2]
[2, 3, 4, 1]
[1, 2, 3, 4]
As mentioned in Efficient way to shift a list in python
An easy way is just:
In [12]: x = [1,2,3,4]
In [13]: [x[i:]+x[:i] for i in range(len(x))]
Out[13]: [[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]
In your first code sample, the line L.append(l) appends a "reference" (loosely speaking) to the list l to the end of L, rather than appending a copy as you seem to be expecting. Thus, when l is later modified, the reference to it contained in L is modified as well, and so when l in L is tested, l will equal the reference to itself in L, and so the loop will end. The same basic problem causes your second code sample to return multiples of the same list rather than several different lists.
To store a copy of l at the current point in time in L instead, use L.append(l[:]).
Here is an easy way:
>>> def cycGen(l):
size = len(l)
return [[l[(i+j)%size] for i in range(size)] for j in range(size)]
>>> l = [1,2,3,4]
>>> print cycGen(l)
[[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]
You could do this using a generator too:
a = [1, 2, 3, 4]
def next_pos(max):
i = 0
while True:
for n in xrange(max):
yield n + i
i += 1
pos = next_pos(len(a))
b = []
for i in xrange(len(a)):
n = []
for j in xrange(len(a)):
m = pos.next()
if m >= len(a):
m -= len(a)
n.append(a[m])
b.append(n)
print b
output:
[[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]

Categories

Resources