Home > Blockchain >  How to arrange / sort the elements of an array such that it matches a specific pattern?
How to arrange / sort the elements of an array such that it matches a specific pattern?

Time:05-19

PROBLEM SETUP

I am using the following code to generate an array that contains arrays which have a given length and whose elements' sums equal to some given value (Note that repetition of digits is allowed):-

from itertools import product
  
def find_combinations(sum_value, len_value):
    lst = range(sum_value   1)

    return [
        pair 
        for pair in product(lst, repeat=len_value) 
        if sum(pair) == sum_value
    ]

# ----------------------------------------- #

sum_value = # some value
len_value = # some value

print(find_combinations(sum_value, len_value))

For example,

If the sum, sum_value, is 4, and the length, len_value, is 2, the output this code produces is:-

[
    (0, 4),
    (1, 3),
    (2, 2),
    (3, 1),
    (4, 0),
]

THE ACTUAL PROBLEM

This code works perfectly - it contains all the combinations that I was expecting. But the problem here is that the order of the array is incorrect.

That is,

In the previous example, where the required sum was 4, and the length was 2, the output should have been this:-

[
    (4, 0),
    (0, 4),
    (3, 1),
    (1, 3),
    (2, 2),
]

So if the required sum is 4, and the length is 3, the output that this code produces is:-

[
    (0, 0, 4),
    (0, 1, 3),
    (0, 2, 2),
    (0, 3, 1),
    (0, 4, 0),
    (1, 0, 3),
    (1, 1, 2),
    (1, 2, 1),
    (1, 3, 0),
    (2, 0, 2),
    (2, 1, 1),
    (2, 2, 0),
    (3, 0, 1),
    (3, 1, 0),
    (4, 0, 0),
]

But the order of the expected output is:-

[
    (4, 0, 0),
    (0, 4, 0),
    (0, 0, 4),
    (3, 1, 0),
    (0, 3, 1),
    (1, 0, 3),
    (1, 3, 0),
    (0, 1, 3),
    (3, 0, 1),
    (2, 1, 1),
    ...,
]

So my question is, how can I arrange the elements of this array such that it matches this pattern?


P.S. - You can refer to the Generic badge, from which this one is inspired.

CodePudding user response:

You could sort the array by decreasing variance (deviation from the elements mean):

sorted(all_combinations, key=lambda combi: -np.var(combi))

"numpy.var" calculates the variance of each element. Since the variance is the squared standard deviation, a huge outlier like 4 in (4, 0, 0) produces a huge variance. The "-" sorts in decreasing order.

If you need the same number combinations (like (4, 0, 0) and (0, 4, 0), which produces the same variance) in a specific order you could very slightly weight your elements by adding a small number to the element that should be sorted first:

sorted(all_combinations, key=lambda combi: -np.var(np.array(combi)   np.linspace(0.01, 0, num=len(all_combinations[0]))))

CodePudding user response:

Try this idea:

  • Start from the largest possible value (to get (sum_len, 0, ..., 0) as the first entry)
  • then, produce all possible rotations and add them to the result (I achieve this using the double ended queue structure)
  • last, you keep track of the visited tuples (to avoid duplicates)
import itertools
import collections

  
def find_combinations(sum_value, len_value):
    lst = range(sum_value, -1, -1)
    
    result = list()
    result_s = set()
    for pair in itertools.product(lst, repeat=len_value):
        if pair in result_s:
            continue
        
        if sum(pair) == sum_value:
            pair = collections.deque(pair)
            for i in range(len_value):
                t_pair = tuple(pair)
                if t_pair in result_s:
                    continue
                
                result.append(t_pair)
                result_s.add(t_pair)
                pair.rotate()
                
    return result
  • Related