LIS on two arrays - dynamic-programming

I feel lost on how to approach this question,
Given two integer array of size 𝑛
, 𝑚
, I want to merge these two arrays into one such that order of element in each array doesn't change and size of their Longest Increasing Subsequence become maximum.
Once we choose an element of A or B, we cannot choose an earlier element of that sequence
My goal is to find maximum possible length of longest increasing subsequence.
This is what I have so far:
def sequences(a, b, start_index=0, min_val=None):
limits = a[start_index], b[start_index]
lower = min(limits)
higher = max(limits)
if min_val is not None and min_val > lower:
lower = min_val
options = range(lower, higher + 1)
is_last = start_index == len(a) - 1
for val in options:
if is_last:
yield [val]
else:
for seq in sequences(a, b, start_index+1, min_val=val+1):
yield [val, *seq]
for seq in sequences([1,3,1,6], [6,5,4,4]):
print(seq)
However, this results in: [1, 3, 4, 5], [1, 3, 4, 6], [2, 3, 4, 5], [2, 3, 4, 6].
The expected output should be:
array1: [1,3,1,6]
array2: [6,5,4,4]
We take 1(from array1), 3(from array1), 4(from array2), 6(from array1)
Giving us LIS: [1,3,4,6].
We got this by not choosing an earlier element from a sequence once we are at a certain value.
How do I stop it from unwanted recursion?

Related

Count sets of increasing trend in a list

I have to count the number of subsets where there is an increasing trend based on user input of the list and the length of the subset.
List:[1,2,3,4,5,6,7,8,9]
k: The length of the trend.
for example...if k=3 and the data points increase for three consecutive numbers, then it is counted as one.
Here the input is the list and the length of the list and k
Example:
Input: List:{1,2,3,4,5,6}
k=3
Output: 4. {(1,2,3),(2,3,4),(3,4,5),(4,5,6)}
You can use the following :
def group_by_trend(data, k):
return [data[i*k :min((i+1)*k, len(data))]
for i in range(round((len(data)+1)/k))]
# test
k = 3
List = [1,2,3,4,5,6,7,8,9]
print(group_by_trend(List, k))
output:
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

check if a number has its equivalent negative number in a list

need to traverse through a list and check if list members has its equivalent negative numbers also in same list
Note: list will not have duplicates and considering performance as well
l = [2, -3, 1, 3, 5, 0, -5, -2]
d=defaultdict(list)
for num in l:
if num>0:
d['pos'].append(num)
else:
d['neg'].append(num)
print (d)
not sure how to proceed further. Could you help please
l = [2, -3, 1, 3, 5, 0, -5, -2]
output: [[2,-2],[-3,3],[5,-5]]
As there cannot be duplicates and you thus do not need to consider exclusive use of each number, you may use a set, like so,
numbers = [2, -3, 1, 3, 5, 0, -5, -2]
unique_numbers = set(numbers)
paired = [[number, -number] for number in numbers
if number > 0 and -number in unique_numbers]
print(paired)
For a result of [[2, -2], [3, -3], [5, -5]].
Sets support O(1) membership checks. Constructing one from an existing list costs O(n), where n is the number of elements in the list. Iterating over the list to compute the pairs is again O(n), such that it runs in O(n) total, at the additional cost of some more memory for the set (again around n).
If you don't care much about performance,
[[x, -x] for x in l if x > 0 and -x in l]

understanding the working principle of sorted function python [duplicate]

I have the following Python dict:
[(2, [3, 4, 5]), (3, [1, 0, 0, 0, 1]), (4, [-1]), (10, [1, 2, 3])]
Now I want to sort them on the basis of sum of values of the values of dictionary, so for the first key the sum of values is 3+4+5=12.
I have written the following code that does the job:
def myComparator(a,b):
print "Values(a,b): ",(a,b)
sum_a=sum(a[1])
sum_b=sum(b[1])
print sum_a,sum_b
print "Comparision Returns:",cmp(sum_a,sum_b)
return cmp(sum_a,sum_b)
items.sort(myComparator)
print items
This is what the output that I get after running above:
Values(a,b): ((3, [1, 0, 0, 0, 1]), (2, [3, 4, 5]))
2 12
Comparision Returns: -1
Values(a,b): ((4, [-1]), (3, [1, 0, 0, 0, 1]))
-1 2
Comparision Returns: -1
Values(a,b): ((10, [1, 2, 3]), (4, [-1]))
6 -1
Comparision Returns: 1
Values(a,b): ((10, [1, 2, 3]), (3, [1, 0, 0, 0, 1]))
6 2
Comparision Returns: 1
Values(a,b): ((10, [1, 2, 3]), (2, [3, 4, 5]))
6 12
Comparision Returns: -1
[(4, [-1]), (3, [1, 0, 0, 0, 1]), (10, [1, 2, 3]), (2, [3, 4, 5])]
Now I am unable to understand as to how the comparator is working, which two values are being passed and how many such comparisons would happen? Is it creating a sorted list of keys internally where it keeps track of each comparison made? Also the behavior seems to be very random. I am confused, any help would be appreciated.
The number and which comparisons are done is not documented and in fact, it can freely change from different implementations. The only guarantee is that if the comparison function makes sense the method will sort the list.
CPython uses the Timsort algorithm to sort lists, so what you see is the order in which that algorithm is performing the comparisons (if I'm not mistaken for very short lists Timsort just uses insertion sort)
Python is not keeping track of "keys". It just calls your comparison function every time a comparison is made. So your function can be called many more than len(items) times.
If you want to use keys you should use the key argument. In fact you could do:
items.sort(key=lambda x: sum(x[1]))
This will create the keys and then sort using the usual comparison operator on the keys. This is guaranteed to call the function passed by key only len(items) times.
Given that your list is:
[a,b,c,d]
The sequence of comparisons you are seeing is:
b < a # -1 true --> [b, a, c, d]
c < b # -1 true --> [c, b, a, d]
d < c # 1 false
d < b # 1 false
d < a # -1 true --> [c, b, d, a]
how the comparator is working
This is well documented:
Compare the two objects x and y and return an integer according to the outcome. The return value is negative if x < y, zero if x == y and strictly positive if x > y.
Instead of calling the cmp function you could have written:
sum_a=sum(a[1])
sum_b=sum(b[1])
if sum_a < sum_b:
return -1
elif sum_a == sum_b:
return 0
else:
return 1
which two values are being passed
From your print statements you can see the two values that are passed. Let's look at the first iteration:
((3, [1, 0, 0, 0, 1]), (2, [3, 4, 5]))
What you are printing here is a tuple (a, b), so the actual values passed into your comparison functions are
a = (3, [1, 0, 0, 0, 1])
b = (2, [3, 4, 5]))
By means of your function, you then compare the sum of the two lists in each tuple, which you denote sum_a and sum_b in your code.
and how many such comparisons would happen?
I guess what you are really asking: How does the sort work, by just calling a single function?
The short answer is: it uses the Timsort algorithm, and it calls the comparison function O(n * log n) times (note that the actual number of calls is c * n * log n, where c > 0).
To understand what is happening, picture yourself sorting a list of values, say v = [4,2,6,3]. If you go about this systematically, you might do this:
start at the first value, at index i = 0
compare v[i] with v[i+1]
If v[i+1] < v[i], swap them
increase i, repeat from 2 until i == len(v) - 2
start at 1 until no further swaps occurred
So you get, i =
0: 2 < 4 => [2, 4, 6, 3] (swap)
1: 6 < 4 => [2, 4, 6, 3] (no swap)
2: 3 < 6 => [2, 4, 3, 6] (swap)
Start again:
0: 4 < 2 => [2, 4, 3, 6] (no swap)
1: 3 < 4 => [2, 3, 4, 6] (swap)
2: 6 < 4 => [2, 3, 4, 6] (no swap)
Start again - there will be no further swaps, so stop. Your list is sorted. In this example we have run through the list 3 times, and there were 3 * 3 = 9 comparisons.
Obviously this is not very efficient -- the sort() method only calls your comparator function 5 times. The reason is that it employs a more efficient sort algorithm than the simple one explained above.
Also the behavior seems to be very random.
Note that the sequence of values passed to your comparator function is not, in general, defined. However, the sort function does all the necessary comparisons between any two values of the iterable it receives.
Is it creating a sorted list of keys internally where it keeps track of each comparison made?
No, it is not keeping a list of keys internally. Rather the sorting algorithm essentially iterates over the list you give it. In fact it builds subsets of lists to avoid doing too many comparisons - there is a nice visualization of how the sorting algorithm works at Visualising Sorting Algorithms: Python's timsort by Aldo Cortesi
Basically, for the simple list such as [2, 4, 6, 3, 1] and the complex list you provided, the sorting algorithms are the same.
The only differences are the complexity of elements in the list and the comparing scheme that how to compare any tow elements (e.g. myComparator you provided).
There is a good description for Python Sorting: https://wiki.python.org/moin/HowTo/Sorting
First, the cmp() function:
cmp(...)
cmp(x, y) -> integer
Return negative if x<y, zero if x==y, positive if x>y.
You are using this line: items.sort(myComparator) which is equivalent to saying: items.sort(-1) or items.sort(0) or items.sort(1)
Since you want to sort based on the sum of each tuples list, you could do this:
mylist = [(2, [3, 4, 5]), (3, [1, 0, 0, 0, 1]), (4, [-1]), (10, [1, 2, 3])]
sorted(mylist, key=lambda pair: sum(pair[1]))
What this is doing is, I think, exactly what you wanted. Sorting mylist based on the sum() of each tuples list

Select numbers from a list with given probability p

Lets say I have a list of numbers [1, 2, 3, ..., 100]. Now I want to select numbers from the list where each number is either accepted or rejected with a given probability 0 < p < 1 . The accepted numbers are then stored in a separate list. How can I do that?
The main problem is choosing the number with probability p. Is there an inbuilt function for that?
The value of p is given by the user.
You can use random.random() and a list comprehension:
import random
l = [1,2,3,4,5,6,7,8,9]
k = [x for x in l if random.random() > 0.23] # supply user input value here as 0.23
print(l)
print(k)
Output:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[2, 3, 4, 5, 6, 7]
to check each element of the list if it has a probability of > 0.23 of staying in the list.
Sidenote:
random.choices() has the ability to accept weights:
random.choices(population, weights=None, *, cum_weights=None, k=1)
but those only change the probability inside the given list for drawing one of the elements (absolute or relative weights are possible) - thats not working for "independent" probabilities though.

Checking whether a list of numbers contains ANY five number sequence

I have a list that only includes positive integers:
my_list = [1, 2, 4, 7, 9, 10, 15, 16]
The list is sorted.
What would be the most "pythonic" way to check whether the list contains any sequence of x consequtive number? It shouldn't matter whether the sequence starts at the beginning of the list, or ends at the end of the list - as long as it's in the list, it should return true. For instance, if I wanted to check if the following list contains a 4-number sequence:
my_list = [1, 3, 4, 5, 6, 8, 10]
It should return true due to [3, 4, 5, 6]
I have seen multiple StackOverflow questions about "finding number sequences in lists", however none of them dealt with looking for a sequence in only a portion of the list. (What I found was useful only if the goal was to check whether the entire list is sequential.)
Here's a one liner:
def has_sequence(L, seq_len):
return any(list(L[i:i+seq_len]) == list(range(L[i],L[i]+seq_len))
for i in range(len(L)-seq_len+1))
def findRun(L, n):
runlen = 0
for i in range(1, len(L)):
if L[i] == L[i-1]+1:
runlen += 1
else:
runlen = 0
if runlen == n-1:
print("Found a run starting at", i-n+1)
Output:
In [451]: L = [1, 3, 4, 5, 6, 8, 10]
In [452]: findRun(L, 4)
Found a run starting at 1
You can try this:
after grouping them with the difference, you just check if any of the groups contains your preferred number of consecutive sequence(in this case 4).
from itertools import groupby
from operator import itemgetter
my_list = [1, 3, 4, 5, 6, 8, 10]
check = False
for key, group in groupby(enumerate(my_list), lambda (i, val): i - val):
g = map(itemgetter(1), group)
if len(g) == 4:
check = True
print(check)
True
I hope this is what you looking for.

Resources