Home > Software engineering >  Find all the consecutive subsequences of alternating odd and even numbers
Find all the consecutive subsequences of alternating odd and even numbers

Time:09-27

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?


CodePudding user response:

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.

CodePudding user response:

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]]

CodePudding user response:

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.

CodePudding user response:

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