Efficient way of generating latin squares (or randomly permute numbers in matrix uniquely on both axes - using NumPy)

前端 未结 5 1635
悲&欢浪女
悲&欢浪女 2021-01-05 00:31

For example, if there are 5 numbers 1, 2, 3, 4, 5

I want a random result like

[[ 2, 3, 1, 4, 5]
 [ 5, 1, 2, 3, 4]
 [ 3, 2, 4, 5, 1]
 [ 1, 4, 5, 2, 3]         


        
5条回答
  •  独厮守ぢ
    2021-01-05 00:59

    I experimented with a brute-force random choice. Generate a row, and if valid, add to the accumulated lines:

    def foo(n=5,maxi=200):
        arr = np.random.choice(numbers,n, replace=False)[None,:]
        for i in range(maxi):
            row = np.random.choice(numbers,n, replace=False)[None,:]
            if (arr==row).any(): continue
            arr = np.concatenate((arr, row),axis=0)
            if arr.shape[0]==n: break
        print(i)
        return arr
    

    Some sample runs:

    In [66]: print(foo())
    199
    [[1 5 4 2 3]
     [4 1 5 3 2]
     [5 3 2 1 4]
     [2 4 3 5 1]]
    In [67]: print(foo())
    100
    [[4 2 3 1 5]
     [1 4 5 3 2]
     [5 1 2 4 3]
     [3 5 1 2 4]
     [2 3 4 5 1]]
    In [68]: print(foo())
    57
    [[1 4 5 3 2]
     [2 1 3 4 5]
     [3 5 4 2 1]
     [5 3 2 1 4]
     [4 2 1 5 3]]
    In [69]: print(foo())
    174
    [[2 1 5 4 3]
     [3 4 1 2 5]
     [1 3 2 5 4]
     [4 5 3 1 2]
     [5 2 4 3 1]]
    In [76]: print(foo())
    41
    [[3 4 5 1 2]
     [1 5 2 3 4]
     [5 2 3 4 1]
     [2 1 4 5 3]
     [4 3 1 2 5]]
    

    The required number of tries varies all over the place, with some exceeding my iteration limit.

    Without getting into any theory, there's going to be difference between quickly generating a 2d permutation, and generating one that is in some sense or other, maximally random. I suspect my approach is closer to this random goal than a more systematic and efficient approach (but I can't prove it).


    def opFoo():
        numbers = list(range(1,6))
        result = np.zeros((5,5), dtype='int32')
        row_index = 0; i = 0
        while row_index < 5:
            np.random.shuffle(numbers)
            for column_index, number in enumerate(numbers):
                if number in result[:, column_index]:
                    break
                else:
                    result[row_index, :] = numbers
                    row_index += 1
            i += 1
        return i, result
    
    In [125]: opFoo()
    Out[125]: 
    (11, array([[2, 3, 1, 5, 4],
            [4, 5, 1, 2, 3],
            [3, 1, 2, 4, 5],
            [1, 3, 5, 4, 2],
            [5, 3, 4, 2, 1]]))
    

    Mine is quite a bit slower than the OP's, but mine is correct.


    This is an improvement on mine (2x faster):

    def foo1(n=5,maxi=300):
        numbers = np.arange(1,n+1)
        np.random.shuffle(numbers)
        arr = numbers.copy()[None,:]
        for i in range(maxi):
            np.random.shuffle(numbers)
            if (arr==numbers).any(): continue
            arr = np.concatenate((arr, numbers[None,:]),axis=0)
            if arr.shape[0]==n: break
        return arr, i
    

    Why is translated Sudoku solver slower than original?

    I found that with this translation of Java Sudoku solver, that using Python lists was faster than numpy arrays.

    I may try to adapt that script to this problem - tomorrow.

提交回复
热议问题