Find all the consecutive subsequences of alternating odd and even numbers - python

Given an integer array, find all the consecutive subsequences of alternating odd and even numbers.
Also print the total number of such subsequences.
All the subsequences should be unique.
The numbers in the list may or may not be unique.
Example:
array = [1,2,5]
output1: [[1], [1,2], [2], [2,5], [5], [1,2,5]]
output2: 6
My Code:
res = []
lst = [1,2,5]
for i in range(len(lst)-1):
res.append([lst[i]])
if abs(lst[i] - lst[i+1]) % 2 == 1:
res.append([lst[i], lst[i+1]])
print(res)
Output: [[1], [1, 2], [2], [2, 5]]
How can I get the remaining subsequences?

You care about duplicates:
def alternating_sublists(xs: list[int]) -> list[list[int]]:
results = []
for i in range(len(xs)):
if [xs[i]] not in results:
results.append([xs[i]])
for j in range(i+1, len(xs)):
if (xs[j] - xs[j-1]) % 2 != 0:
if xs[i:j+1] not in results:
results.append(xs[i:j+1])
else:
break
return results
print(list(alternating_sublists([1, 2, 5])))
print(list(alternating_sublists([1, 2, 2, 2, 1])))
print(list(alternating_sublists([1, 2, 3, 2, 3, 2, 1])))
Output:
[[1], [1, 2], [1, 2, 5], [2], [2, 5], [5]]
[[1], [1, 2], [2], [2, 1]]
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 2], [1, 2, 3, 2, 3], [1, 2, 3, 2, 3, 2], [1, 2, 3, 2, 3, 2, 1], [2], [2, 3], [2, 3, 2], [2, 3, 2, 3], [2, 3, 2, 3, 2], [2, 3, 2, 3, 2, 1], [3], [3, 2], [3, 2, 3], [3, 2, 3, 2], [3, 2, 3, 2, 1], [2, 3, 2, 1], [3, 2, 1], [2, 1]]
It's not extremely efficient (there's many lookups of lists already in the result). Depending on the application you may want a more complex data structure to save expensive 'list in large list' tests.
The basic logic is this:
each sequence has to start at some index, so try sequences starting at all possible indices for i in range(len(xs)):
the sequence with length 1 always meets your rule, so add it if it wasn't there yet
the other sequences start at index i and end at index i+1 or greater for j in range(i+1, len(xs)):
break from the loop whenever the modulo is 0 for the last two items in list you're about to add, since this sequence doesn't meet the rule, and longer ones wouldn't either.
Slightly faster and shorter, using tuples internally, but essentially the same:
def alternating_sublists2(xs: list[int]) -> list[list[int]]:
results = set()
for i in range(len(xs)):
results.add((xs[i],))
for j in range(i+1, len(xs)):
if (xs[j] - xs[j-1]) % 2 != 0:
results.add(tuple(xs[i:j+1]))
else:
break
return [list(t) for t in results]
shorter as the previous if statements are now internal to set.add()
faster because looking up tuples is faster than looking up strings, and testing membership of a set is faster than testing membership of a list
not quite as fast as you might like, since it then has to convert the result back to a list of lists, to get the result you required.
However, no guarantees on the order of the sublists in the result, so this is no good if you need the sublists in the order they are first found.

Here's a recursive solution to the problem. It iterates the elements of the list, adding the results from recursing the balance of the list when there is a change from odd to even between the current element and the next:
def odd_even(list, start=None):
result = []
for i, val in enumerate(list):
if start is None or i == 0:
if [val] not in result:
result.append([val])
if len(list) > i+1 and (list[i+1] - val) % 2 == 1:
for res in odd_even(list[i+1:], val):
if [val] + res not in result:
result = result + [[val] + res]
return result
print(odd_even([1, 2, 5]))
print(odd_even([1, 2, 2, 2, 1]))
print(odd_even([1, 2, 3, 2, 3, 2, 1]))
Output:
[[1], [1, 2], [1, 2, 5], [2], [2, 5], [5]]
[[1], [1, 2], [2], [2, 1]]
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 2], [1, 2, 3, 2, 3], [1, 2, 3, 2, 3, 2], [1, 2, 3, 2, 3, 2, 1], [2], [2, 3], [2, 3, 2], [2, 3, 2, 3], [2, 3, 2, 3, 2], [2, 3, 2, 3, 2, 1], [3], [3, 2], [3, 2, 3], [3, 2, 3, 2], [3, 2, 3, 2, 1], [2, 3, 2, 1], [3, 2, 1], [2, 1]]

Your accepted output is silly, because it's obvious that every subsequence of a "good" sequence is also "good" and there's no need to enumerate them all. Let's concentrate on finding longest alternating sequences:
def split(a):
buf = [a[0]]
for i in range(1, len(a)):
if a[i] % 2 != a[i - 1] % 2:
buf.append(a[i])
else:
yield buf
buf = [a[i]]
if buf:
yield buf
test = [1, 2, 5, 7, 3, 8, 9, 9, 10, 11]
result = list(split(test))
# [[1, 2, 5], [7], [3, 8, 9], [9, 10, 11]]
To get your expected answer, take each list from the result and generate all sublists of it. This is another, much simpler task.

This looks like a gray code sequence with additional twist:
https://en.wikipedia.org/wiki/Gray_code
Code:
import math
def powerOf2(k):
if k == 0:
return 1
else:
return 2*powerOf2(k-1)
def gray_encode(n):
return n ^ n >> 1
def count_required_sequence(lst):
n = len(lst)
sequence_nr = powerOf2(n)
results = []
results_sequence = -1
for i in range(sequence_nr):
gray = gray_encode(i)
gray_r = list("{:>010b}".format(gray))[::-1]
#print(gray_r)
count = sum(el == "1" for el in gray_r)
if count > 1:
results_sequence += 1
results.append(list())
for k in range(len(gray_r)):
if k < len(gray_r)-1:
if gray_r[k] == "1" and gray_r[k+1] == "1":
if abs(lst[k] - lst[k+1]) % 2 == 1:
results[results_sequence].append(lst[k])
results[results_sequence].append(lst[k+1])
is_there_count1 = results.count(list(set(results[results_sequence])))
results[results_sequence] = list(set(results[results_sequence]))
is_there_count = results.count(results[results_sequence])
if is_there_count > 1 or is_there_count1 > 1:
index = results.index(list(set(results[results_sequence])))
results.pop(results_sequence)
results_sequence -= 1
elif count == 1 :
results_sequence += 1
results.append(list())
pos = [index for index,value in enumerate(gray_r) if value == "1" ]
results[results_sequence].append(lst[pos[0]])
results = (list(filter(lambda a: a != [], results)))
print("results: {}".format(results))
# Driver code
if __name__ == "__main__" :
# lst = [ 1, 2, 5, 6, 7];
lst = [ 1, 2, 5 ];
count_required_sequence(lst);
Output:
results: [[1], [1, 2], [2], [2, 5], [1, 2, 5], [5]]
Change 010b to a bigger number is len(lst) is bigger then 10
gray_r = list("{:>010b}".format(gray))[::-1]

Related

How to weave two lists using recursion in Python

I want to weave two lists and output all the possible results.
For example,
input: two lists l1 = [1, 2], l2 = [3, 4]
output: [1, 2, 3, 4], [1, 3, 2, 4], [1, 3, 4, 2], [3, 1, 2, 4], [3, 1, 4, 2], [3, 4, 1, 2]
Note: I need to keep the order in each list (e.g. 1 is always before 2, and 3 is always before 4)
The way I am solving this is by removing the head from one list, recursing, and then doing the same thing with the other list. The code is below:
all_possibles = []
def weaveLists(first, second, added):
if len(first) == 0 or len(second) == 0:
res = added[:]
res += first[:]
res += second[:]
all_possibles.append(res)
return
cur1 = first[0]
added.append(cur1)
first = first[1:]
weaveLists(first, second, added)
added = added[:-1]
first = [cur1] + first
cur2 = second[0]
added.append(cur2)
second = second[1:]
weaveLists(first, second, added)
added = added[:-1]
second = [cur2] + second
weaveLists([1, 2], [3, 4], [])
print(all_possibles)
The result I got is:
[[1, 2, 3, 4], [1, 3, 2, 4], [1, 3, 4, 2], [1, 3, 1, 2, 4], [1, 3, 1, 4, 2], [1, 3, 1, 4, 1, 2]]
I couldn't figure out why for the last three lists, the heading 1 from the first list is not removed.
Can anyone help? Thanks!
The reason you get those unexpected results is that you mutate added at this place:
added.append(cur1)
...this will affect the caller's added list (unintentionally). While the "undo" operation is not mutating the list:
added = added[:-1]
This creates a new list, and therefore this "undo" action does not roll back the change in the list of the caller.
The easy fix is to replace the call to append with:
added = added + [cur1]
And the same should happen in the second block.
It is easier if you pass the new values for the recursive call on-the-fly, and replace those two code blocks with just:
weaveLists(first[1:], second, added + [first[0]])
weaveLists(first, second[1:], added + [second[0]])
Here is another way to do it: we generate the possible indices of the items of the first list inside the weaved list, and fill the list accordingly.
We can generate the indices with itertools.combinations: it's the combinations of the indices of the weaved list, taking len(first_list) of them each time.
from itertools import combinations
​
def weave(l1, l2):
total_length = len(l1) + len(l2)
# indices at which to put items from l1 in the weaved output
for indices in combinations(range(total_length), r=len(l1)):
out = []
it1 = iter(l1)
it2 = iter(l2)
for i in range(total_length):
if i in indices:
out.append(next(it1))
else:
out.append(next(it2))
yield out
Sample run:
l1 = [1, 2]
l2 = [3, 4]
​
for w in weave(l1, l2):
print(w)
​
[1, 2, 3, 4]
[1, 3, 2, 4]
[1, 3, 4, 2]
[3, 1, 2, 4]
[3, 1, 4, 2]
[3, 4, 1, 2]
Another sample run with a longer list:
l1 = [1, 2]
l2 = [3, 4, 5]
​
for w in weave(l1, l2):
print(w)
​
[1, 2, 3, 4, 5]
[1, 3, 2, 4, 5]
[1, 3, 4, 2, 5]
[1, 3, 4, 5, 2]
[3, 1, 2, 4, 5]
[3, 1, 4, 2, 5]
[3, 1, 4, 5, 2]
[3, 4, 1, 2, 5]
[3, 4, 1, 5, 2]
[3, 4, 5, 1, 2]

Distinct Group of Integer

Given an array of integers, I want to determine the number of distinct groups of integers wherein the values ascend.
Given array myList = [1, 2, 3, 4, 3, 2, 2, 3, 1, 2, 1, 4, 2]
There are 4 distinct groups of integers wherein the values ascend. i.e.
[1, 2, 3, 4], [2, 2, 3], [1, 2] and [2]
Can some with experience guide me on how to achieve this in python?
Another possible answer (also assuming [1, 4] is supposed to be there instead of [2]):
In [14]: def find_ascending_groups(my_list):
...: groups = []
...: current_group = [my_list[0]]
...: for i in range(1, len(my_list)):
...: if current_group[-1] <= my_list[i]:
...: current_group.append(my_list[i])
...: else:
...: if len(current_group) > 1:
...: groups.append(current_group)
...: current_group = [my_list[i]]
...: if len(current_group) > 1:
...: groups.append(current_group)
...: print(groups)
...:
In [15]: find_ascending_groups(myList)
[[1, 2, 3, 4], [2, 2, 3], [1, 2], [1, 4]]
Here is one way to do it:
group = []
list_of_groups = []
myList = [1, 2, 3, 4, 3, 2, 2, 3, 1, 2, 1, 4, 2]
for i in range(len(myList)):
if i == len(myList)-1:
if len(group) > 0:
group.append(myList[i])
list_of_groups.append(group)
else:
if myList[i] <= myList[i+1]:
group.append(myList[i])
else:
group.append(myList[i])
if len(group) > 1:
list_of_groups.append(group)
group = []
print(list_of_groups)
Result with different test cases -
myList = [1, 2, 3, 4, 3, 2, 2, 3, 1, 2, 1, 4, 2]
Output: [[1, 2, 3, 4], [2, 2, 3], [1, 2], [1, 4]
myList = [1, 2, 3, 4, 3, 2, 2, 3, 1, 2, 1, 4, 5]
Output: [[1, 2, 3, 4], [2, 2, 3], [1, 2], [1, 4, 5]]
myList = [1, 2]
Output: [[1, 2]]
myList = [1, 2, 3, 4]
Output: [[1, 2, 3, 4]]
myList = [5, 2, 3, 4]
Output: [[2, 3, 4]]
myList = [5, 2]
Output: []
Assuming [1, 4] is supposed to be there instead of [2], you can do something like this:
n [48]: ascending_ints = []
...: t = []
...: start_idx = 0
...: while start_idx < len(myList) - 1:
...: first = True
...: for end_idx in range(start_idx + 1, len(myList), 1):
...: if myList[end_idx] >= myList[start_idx]:
...: if first:
...: t.append(myList[start_idx])
...: first = False
...: t.append(myList[end_idx])
...: else:
...: if t:
...: ascending_ints.append(t)
...: t = []
...: start_idx = end_idx
...: break
...: start_idx += 1
...:
In [49]: ascending_ints
Out[49]: [[1, 2, 3, 4], [2, 2, 3], [1, 2], [1, 4]]
This just makes the start_idx start at 0, end_idx begin at 1 and increment both. If myList[end_idx] >= myList[start_idx] then it appends it to t. If it's the first time it's happened for this iteration, it needs to also include the value at start_idx. Once the condition is no longer true, check if anything is in t, if it is, append it to ascending_ints, reset t, set start_idx = end_idx and break the for-loop.
Here is another solution that is more efficient in Python.
def find_ascending_groups(l):
groups = []
last_ascending = (l[0] <= l[1])
l.append(l[-1] - 1) # Add an additional element to make sure the last group is appended
start = 0
for i in range(1, len(l)):
is_ascending = (l[i - 1] <= l[i])
if is_ascending != last_ascending:
if is_ascending:
start = i - 1 # l[i - 1] is a local minimum, set group start
else:
groups.append(l[start:i]) # l[i - 1] is a local maximum, generate group
last_ascending = is_ascending
l.pop() # Remove added element
return groups
EDIT
I wrote a small split_when function which was recently merged into the more-itertools library. split_when takes an iterable and a function. The function takes all pairs of consecutive elements and returns whether the iterable should be splitted between those elements:
>>> myList = [1, 2, 3, 4, 3, 2, 2, 3, 1, 2, 1, 4, 2]
>>> import more_itertools
>>> list(more_itertools.split_when(myList, lambda x, y: x > y))
[[1, 2, 3, 4], [3], [2, 2, 3], [1, 2], [1, 4], [2]]
# 4>3 3>2 3>1 2>1 4>2
PREVIOUS ANSWER
For the record, you can use a fold, that is functools.reduce in Python:
>>> myList = [1, 2, 3, 4, 3, 2, 2, 3, 1, 2, 1, 4, 2]
>>> import functools
>>> functools.reduce(lambda acc, x: acc[:-1] + [acc[-1]+[x]] if acc and x >= acc[-1][-1] else acc + [[x]], myList, [])
[[1, 2, 3, 4], [3], [2, 2, 3], [1, 2], [1, 4], [2]]
Not very readable though, but if you write the lambda as a distinct function, you get a better idea of what's happening:
>>> def f(acc, x):
... if acc: # there is at least one closed group
... *prev, cur = acc # closed groups and current group
... *_, y = cur # y is the last element of the current group
... if x >= y: # if ascending
... return prev + [cur + [x]] # return with x added to the current group
...
... return acc + [[x]] # otherwise, return with x in a new group
...
>>> functools.reduce(f, myList, [])
[[1, 2, 3, 4], [3], [2, 2, 3], [1, 2], [1, 4], [2]]

how can i generate combinations as like below?

I have a list
a=[1,2,3]
I want to perform combinations(adjacent numbers) on this list and I want multiplication each combination like below
1
1 2
2
1 2 3
2 3
3
After this, I want to perform
a) 1*1= 1
b) 1*2+2*2= 6
c) 2*2= 4
d) 1*3+2*3+3*3= 18
e) 2*3+3*3= 15
f) 3*3= 9
Expected output is
[1,2,4,18,15,9]
Here is my attempted code:
def grouper(input_list, n = 2):
for i in xrange(len(input_list) - (n - 1)):
yield input_list[i:i+n]
a = [1,2,3]
for item in [a[0:m+1] for m in range(len(a))]:
for n in range(len(item)):
result.append(item[n:])
test.append(sum([k * len(item) for k in item[n:]]))
print result
print test
output
[[1], [1, 2], [2], [1, 2, 3], [2, 3], [3]]
[1, 6, 4, 18, 15, 9]
For more length
a = [1,2,3,4]
output
[[1], [1, 2], [2], [1, 2, 3], [2, 3], [3], [1, 2, 3, 4], [2, 3, 4], [3, 4], [4]]
[1, 6, 4, 18, 15, 9, 40, 36, 28, 16]
Simple using for loops
a = [1,2,3]
tmp = []
for m in range(len(a)):
tmp.append( a[0:m +1])
result = []
test = []
for item in tmp:
for n in range(len(item)):
result.append(item[n:])
test.append(sum([k * len(item) for k in item[n:]]))
print tmp
print result
print test
Output
[[1], [1, 2], [1, 2, 3]]
[[1], [1, 2], [2], [1, 2, 3], [2, 3], [3]]
[1, 6, 4, 18, 15, 9]
Create combinations:
a = [1,2,3]
# create combinations
combinations = []
for i in range(len(a)):
for j in range(len(a)):
result = a[i:j+1]
if result:
combinations.append(result)
output:
combinations [[1], [1, 2], [1, 2, 3], [2], [2, 3], [3]]
To compute the values you want:
for values in combinations:
last_val = values[-1]
computation = ''
result = 0
for val in values:
computation += "{}*{} + ".format(val, last_val)
result += val * last_val
computation = computation[:-2] + '= {}'.format(result)
print(computation)
output:
1*1 = 1
1*2 + 2*2 = 6
1*3 + 2*3 + 3*3 = 18
2*2 = 4
2*3 + 3*3 = 15
3*3 = 9
#Vikash Singh has given an almost complete solution here:
Except that there is little mismatch with the combinations:
I have managed to correct the same:
a = [1,2,3]
combinations = []
for i in range(len(a)+1):
for j in range(i):
result = a[j:i]
if result:
combinations.append(result)
print combinations
The output will be
[[1], [1, 2], [2], [1, 2, 3], [2, 3], [3]]
And the if the list is [1,2,3,4], output will be:
[[1], [1, 2], [2], [1, 2, 3], [2, 3], [3], [1, 2, 3, 4], [2, 3, 4], [3, 4], [4]]
I hope this solves OP's problem with the combination.

Behaviour of python lists in recursion

The below code returns all possible permutations of the numbers provided.
class Solution:
def permute(self, numbers, start, result):
if start == len(numbers):
print(numbers)
result.append(numbers[:])
return
for i in range(start, len(numbers)):
numbers[start], numbers[i] = numbers[i], numbers[start]
self.permute(numbers, start + 1, result)
numbers[start], numbers[i] = numbers[i], numbers[start]
def solution(self, numbers):
result = []
if not numbers or len(numbers) == 0:
return numbers
self.permute(numbers, 0, result)
return result
res1 = Solution().solution([1, 2, 3])
print(res1)
The final output for this instance will be
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 2, 1], [3, 1, 2]]
but when I slightly modify the permute function, the output is completely different
def permute(self, numbers, start, result):
if start == len(numbers):
print(numbers)
result.append(numbers) #changing this line
return
for i in range(start, len(numbers)):
numbers[start], numbers[i] = numbers[i], numbers[start]
self.permute(numbers, start + 1, result)
numbers[start], numbers[i] = numbers[i], numbers[start]
which gives the output
[[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]
The program works when I use
result.append([x for x in numbers])
or
result.append(numbers[:])
but not when I use
result.append(numbers)
Can someone help me understand as to why this is happening ?
If you don't create a copy of the object with the techniques you've described, you put the same object in the list again and again.
Here's a short example to illustrate the issue:
>>> a=[1,2]
>>> b=[a,a]
>>> a.append(3)
>>> b # [[1, 2, 3], [1, 2, 3]]

Generate all permutations of a list in lexicagraphical order

I am very much a beginner. I am trying to write a program that, given the number of elements (1-9) in the list as a parameter, will then output all permutations of the list in lexicographical order. In the program it is adding on each permutation as a list into a larger list that contains all the permutations in order. Although the program is not working as expected in general, one main problem I'm having is with this while loop In line 10, I want the list to stop compiling once the final permutation has been added to the list. For example, if my input parameter is n = 4, the last permutation/element should be [4,3,2,1]. However, when I run this program, that element is in the list three times at the end. I don't know how this is so when it should terminate that while loop once it has been added.
def ourPermutations(n):
x=list(range(1,n+1))
permList = []
permList+=[x]
xcopy = x[:]
finalPerm = xcopy[::-1]
while x != finalPerm:
istar = n-2
while x[istar] > x[istar+1]:
istar -= 1
jstar = n-1
while x[jstar] < x[istar]:
jstar -= 1
x[istar],x[jstar] = x[jstar],x[istar]
if istar+1 == n-1:
x = x[:]
else:
a = x[istar+1:]
a = a[::-1]
x = x[:istar+1] + a
permList += [x]
return permList
That is my main question; however, this program is still missing elements when I run it. It isn't quite working, so if you see a spot where something is obviously wrong, feel free to tell me that particular line is what is causing my problems. If it helps, this is based on this identical (and correct) version written in Mathematica 8:
ourpermutations[n_] := (
ourlist = {x=Range[1,n]};
While[
x != Reverse[Range[1,n]],
istar = n-1;
While[x[[istar]] > x[[istar+1, istar--];
jstar = n; While[x[[jstar]] < x[[istar]], jstar--];
x[[{istar, jstar}]] = x[[{jstar, istar}]];
AppendTo[ourlist, x = Join[Take[x,istar], Reverse[Drop[x,istar]]]]
];
ourlist
)
So this is what my Python code should be doing; I just can't get it to do so just yet. Thanks for any of your time and effort.
It looks like you're running into a problem because you aren't copying x soon enough and so you're sometimes modifying x after it's been added to permList. This can be solved by adding x = x[:] at the start of your while loop:
def ourPermutations(n):
x=list(range(1,n+1))
permList = []
permList+=[x]
xcopy = x[:]
finalPerm = xcopy[::-1]
while x != finalPerm:
x = x[:]
istar = n-2
while x[istar] > x[istar+1]:
istar -= 1
jstar = n-1
while x[jstar] < x[istar]:
jstar -= 1
x[istar],x[jstar] = x[jstar],x[istar]
if istar+1 == n-1:
x = x[:]
else:
a = x[istar+1:]
a = a[::-1]
x = x[:istar+1] + a
permList += [x]
return permList
>>> ourPermutations(3)
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
>>> ourPermutations(4)
[[1, 2, 3, 4], [1, 2, 4, 3], [1, 3, 2, 4], [1, 3, 4, 2], [1, 4, 2, 3], [1, 4, 3, 2], [2, 1, 3, 4], [2, 1, 4, 3], [2, 3, 1, 4], [2, 3, 4, 1], [2, 4, 1, 3], [2, 4, 3, 1], [3, 1, 2, 4], [3, 1, 4, 2], [3, 2, 1, 4], [3, 2, 4, 1], [3, 4, 1, 2], [3, 4, 2, 1], [
4, 1, 2, 3], [4, 1, 3, 2], [4, 2, 1, 3], [4, 2, 3, 1], [4, 3, 1, 2], [4, 3, 2, 1]]
A slightly more "pythonic" version might look like:
def our_permutations(n):
x = list(range(1, n+1))
perm_list = [x]
final_perm = x[::-1]
while x != final_perm:
x = x[:]
istar = n-2
while x[istar] > x[istar+1]:
istar -= 1
jstar = n-1
while x[jstar] < x[istar]:
jstar -= 1
x[istar],x[jstar] = x[jstar],x[istar]
if istar+1 != n-1:
a = x[istar+1:]
a = a[::-1]
x = x[:istar+1] + a
perm_list += [x]
return perm_list
Checking these functions against itertools.permutation shows that they produce the same answers, so it looks like your algorithm was correct except for that small mistake.

Categories

Resources