2

I'm trying already for some days to solve this problem without any success.

About this problem:

Given a sequence '2 2 4 4'.

We take consecutively 2 numbers from the sequence, For example: 2 2 ,2 4, 4 4.

If the sum of the 2 numbers is an number divisible by 2 we replace the 2 numbers and put the result of the 2 numbers for example: (2+2 = 4, 4/2 = 2) so the new sequence is (2 4 4), but here I should find all possible sequences. If no possible to find an even number that is dividable by 2, so we return the sequence.

A picture of how should it be

A picture of how should it be

The red rectangles are the sequence that I cannot get :(

my code:

def recFunc(n):
    for i in range(len(n)):
        if i+1 <= len(n)-1: #control out of range
            if ((n[i] + n[i+1]) % 2 == 0):
                newnum = int((n[i] + n[i+1])/2)
                n[i:i+2] = [newnum]
                return recFunc(n)
            else:
                if i+1 == len(n)-1:
                    return [n]
                else:
                    continue
def main(s):
    s = s.split()
    integers = [int(x) for x in s]
    final = recFunc(integers)
    print(final)

main('2 2 4 4')

What I did here is ,converted the sequence to integers , sent them to a new function. That I should receive all sequences recursively.

I iterated on the sequence and taking the first number by n[i] and the second n[i+1] (and controlling if I can have the second number to not get out of range).

The final result should be ordered in increasing mode of the size sequence, if the length of the 2 sequence is that same so we order by the first number of the sequence.

At the end I should receive ['3', '2 3', '3 4', '2 3 4']

Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
LordNord
  • 123
  • 8

3 Answers3

2

I created a few functions to accomplish the goal:

  1. least_check that give a True/False as to whether or not the sequence is a "least" (e.g. '2 4' would return False, while '3 4' would return True)
  2. find_leasts which is the recursive function that breaks a sequence down to the the next level in the tree shown in the question (e.g. '2 2 4 4' would break down to '2 4 4', '2 3 4', and '2 2 4') until it reaches all "leasts"
  3. main which creates a list of all the "leasts" yielded from the find_leasts function, and removes any duplicates (e.g. example sequence has '3' twice) and returns the list of unique "leasts"

Answer:

def least_check(n):
    check_list = [int(x) for x in n.split(' ')]
    for a, b in zip(check_list[:-1], check_list[1:]):
        if (a + b) % 2 == 0:
            return False
    return True

def find_leasts(n):
    if len(n.split(' ')) == 1:
        yield n
    for i in range(len(n.split(' '))-1):
        s = [int(x) for x in n.split(' ')]
        if (s[i] + s[i+1]) % 2 == 0:
            s[i] = int((s[i] + s[i+1]) / 2)
            s.pop(i+1)
        sub_n = ' '.join(str(j) for j in s)
        if least_check(sub_n):
            yield sub_n
        else:
            yield from find_leasts(sub_n)

def main(s):
    all_leasts = [x for x in find_leasts(s)]
    unique_leasts = list(set(all_leasts))
    return unique_leasts

seq = '2 2 4 4'
print(sorted(main(seq), key=len))

Result:

['3', '2 3', '3 4', '2 3 4']

Update:

The above solution has many split()s and ' '.join()s in order to avoid the recursive function modifying a list reference (a list name is a pointer to its memory address - if needed, see this site for further explanation) from a depth other than the current scope.

When looking at the error received on a '30 20 10 30 6 6' sequence and considering that playing with the max recursion via sys.setrecursionlimit is not recommended, I re-evaluated whether or not recursion was even necessary - and determined it is not.

Here are the functions used in the iterative solution:

  1. least_check - same as original answer
  2. break_down - takes a list and breaks it down to all lists down one level in the tree (e.g. '2 2 4 4' would break down to '2 4 4', '2 3 4', and '2 2 4')
  3. least_lister - iterates through the queue of lists that are potential leasts, until all lists in least_lists are leasts
  4. main - does all split() and ' '.join() operations and removes duplicates before returning results

Iterative Solution:

def least_check(check_list):
    for a, b in zip(check_list[:-1], check_list[1:]):
        if (a + b) % 2 == 0:
            return False
    return True

def break_down(s, ret):
    for i in range(len(s)-1):
        if (s[i] + s[i+1]) % 2 == 0:
            bd_list = s.copy()
            bd_list[i] = int((bd_list[i] + bd_list[i+1]) / 2)
            bd_list.pop(i+1)
            ret.append(bd_list)

def least_lister(n):
    least_lists = []
    if least_check(n):
        least_lists.append(n)
    else:
        i = 0
        break_down(n, least_lists)
        while i < len(least_lists):
            if least_check(least_lists[i]):
                i+=1
            else:
                break_down(least_lists[i], least_lists)
                least_lists.pop(i)
    return least_lists

def main(s):
    s_list = [int(x) for x in s.split(' ')]
    all_leasts = least_lister(s_list)
    unique_leasts = list(set([' '.join(str(j) for j in i) for i in all_leasts]))
    return unique_leasts

seq = '30 20 10 30 6 6'
print(sorted(main(seq), key=len))

Iterative Result:

['18', '19', '19 6', '25 6', '25 10', '25 14', '30 13', '30 15', '30 17', '30 13 6', '30 17 6', '30 15 12', '30 15 18']

List Reference Example

def add_to_list(n):
    n.append(len(n))
    print(n)

a = [0, 1]

add_to_list(a)
print(a)

List Reference Example Output:

[0, 1, 2]
[0, 1, 2]
tarheel
  • 4,727
  • 9
  • 39
  • 52
  • thank you very much for the reply, it saves spaces in memory by the fucntion yield, but there are many convertion beetween string and int, so if I put a lonnger string "seq = '30 20 10 30 6 6'" it goes to RecursionError: maximum recursion depth exceeded while getting the str of an object – LordNord Dec 27 '18 at 08:33
  • @LordNord My response was too long for a comment, please see the update to my answer. – tarheel Dec 28 '18 at 06:14
  • Thank you for the reply, even if the response was too long, your code works perfect, the problem is I should to solve it recursively, when I'm trying to work with return instead of yield,I don't know why but it solves only the left side of the tree (Like the picture, this tree I mean), even If I do recursively it doesn't go to the red rectangles (in the picture) , do you have any solution? – LordNord Dec 28 '18 at 07:54
  • @LordNord The reason your recursion only goes down the left side is because the list name is referring to a memory address. The effect this has is demonstrated by the List Reference Example I added to my answer. [This site](http://henry.precheur.org/python/copy_list) goes into a deeper explanation of why this is, and [questions like this](https://stackoverflow.com/questions/2612802/) go into possible workarounds. Problem is that even if you implement one, as I did in the original answer, you'll end up getting the same maxrecursion error that I did when doing the `'30 20 10 30 6 6'` sequence. – tarheel Dec 28 '18 at 19:41
2

Below is my recursive solution, based on your diagram, which can handle '30 20 10 30 6 6' without stack issues. Data conversion, sorting and redundancy reduction are handled by the main() routine. The sub_sequence() function takes an array and returns an array of arrays that match the logic of your diagram:

def sub_sequence(array):
    solutions = []

    length = len(array)
    changed = False

    if length > 1:
        for index in range(len(array) - 1):
            prefix, pair, postfix = array[:index], array[index:index + 2], array[index + 2:]

            total = sum(pair)

            if total % 2 == 0:
                solutions.extend(sub_sequence([*prefix, total // 2, *postfix]))
                changed = True

    if length < 2 or not changed:
        solutions.append(array)

    return solutions

def main(string):
    unsorted_redundant_sub_sequences = sub_sequence([int(number) for number in string.split()])
    unsorted_non_redundant_strings = set(" ".join(map(str, sequence)) for sequence in unsorted_redundant_sub_sequences)
    sorted_non_redundant_strings = sorted(unsorted_non_redundant_strings, key=lambda x: (len(x), x))
    print(sorted_non_redundant_strings)

main('30 20 10 30 6 6')

OUTPUT

> python3 test.py
['18', '19', '19 6', '25 6', '25 10', '25 14', '30 13', '30 15', '30 17', '30 13 6', '30 17 6', '30 15 12', '30 15 18']
> 
cdlane
  • 40,441
  • 5
  • 32
  • 81
0

Let's see this problem step by step.

First : Sum the first number of the array with the second one,

divide by two the result,

create a new array with the new values,

if it's even

then call recursive on the new arrey

Else put the new arrey in a static data structure

Second : At the first point we need to see all the index,

Put everything in a cycle starting with index I = 0 to length-2

Third : work on the static array, sort it as you want and print the result.

I'm not good with python but I hope this pseudo code will help you.

FrancescoPenasa
  • 220
  • 4
  • 12