Home > Software engineering >  How to generate a matrix with permutations where any 2x2 square has 4 non-identical values?
How to generate a matrix with permutations where any 2x2 square has 4 non-identical values?

Time:12-21

So let's say my matrix looks like this (always a square):

a1 a2 a3
b1 b2 b3
c1 c2 c3

I want so that elements in the square, (a1, a2, b1, b2), (a2, a3, b2, b3), etc were not similar — meaning : a1 != a2 != b1 != b2.

I have this code for recursively generating the matrix:

def generate(elements):
    if not elements:
        return (),
    final = []
    for items in generate(elements[:-1]):
        for item in elements[-1]:
            final.append(items   (item,))
    return final

def product(*args, repeat=1):
    pools = [list(pool) for pool in args] * repeat
    return list(generate(pools))

def main():
    D = 3
    combinations = product([0, 128, 196], repeat=D)
    matrices = product(combinations, repeat=D)
    return matrices

where elements is a list of integers (unknown number), let's say [0, 128, 196] and repeat is the size of the square matrix.

I want somewhere in the function to apply the rule, so that it will only generate matrices according to that rule that I mentioned.

So in the end the final result would be all the possible variations of 3x3 matrices but with that rule applied.

Would prefer to do it without importing pandas or anything like that.

CodePudding user response:

Here is a possibility:

  • Generate all possible matrices using itertools.permutations and numpy.reshape;
  • Check if a matrix is suitable using numpy.diff to test for equal adjacent elements.
from more_itertools import distinct_permutations
from numpy import array, diff, count_nonzero

def check_matrix(m):
    d_vert = diff(m, axis=0)                # vertically adjacent
    if count_nonzero(d_vert) < d_vert.size:
        return False
    d_hori = diff(m, axis=1)                # horizontally adjacent
    if count_nonzero(d_hori) < d_hori.size:
        return False
    d_dia1 = d_vert[:,:-1]   d_hori[1:,:]   # northwest-southeast diagonally adjacent
    if count_nonzero(d_dia1) < d_dia1.size:
        return False
    d_dia2 = d_hori[:-1,:] - d_vert[:,:-1]  # southwest-northeast diagonally adjacent
    return count_nonzero(d_dia2) == d_dia2.size

def gen_matrices(h,w, elements):
    for p in distinct_permutations(elements, h*w):
        m = array(p).reshape((h,w))
        if check_matrix(m):
            yield m

for m in gen_matrices(2, 3, [1,2,3,4,1,2]):  # should be 8 results with 3 and 4 in the middle column
    print(m)
# [[1 3 1]
#  [2 4 2]]

# [[1 3 2]
#  [2 4 1]]

# [[1 4 1]
#  [2 3 2]]

# [[1 4 2]
#  [2 3 1]]

# [[2 3 1]
#  [1 4 2]]

# [[2 3 2]
#  [1 4 1]]

# [[2 4 1]
#  [1 3 2]]

# [[2 4 2]
#  [1 3 1]]

Documentation:

CodePudding user response:

solved it by adding a new function that checks each 2 lines. little bit slow but it works.

def square_chec(row1, row2):
    if row1[-1] == row2[-1]:
        return False
    for num in range(len(row1)-1):
        if row2[num 1] == row1[num] or row2[num] == row1[num] or row1[num 1] == row2[num] or row2[num] == row1[num]:
            return False
    return True

def generate(elements):
    if not elements:
        return (),
    final = []
    for items in generate(elements[:-1]):
        for item in elements[-1]:
            if items:
                if items[-1] != item:
                    if type(item) == tuple:
                        if square_chec(items[-1], item):
                            final.append(items   (item,))
                    else:
                        final.append(items   (item,))
            else:
                final.append(items   (item,))
    return final

  • Related