Python 生成所有可能的二进制矩阵,使每一行和每一列对每个矩阵加起来最多为1

Python 生成所有可能的二进制矩阵,使每一行和每一列对每个矩阵加起来最多为1,python,algorithm,numpy,matrix,reinforcement-learning,Python,Algorithm,Numpy,Matrix,Reinforcement Learning,我试图为给定的n生成所有可能的平方二元矩阵,这样: 对于每个二进制矩阵: 1) the rows sum up to at most 1 2) the columns sum up to at most 1 例如: 对于n=2,有效矩阵为: [0 0] [0 0] [0 0] [0 1] [0 0] [1 0] [0 1] [0 0] [0 1] [1 0] [1 0] [0 0] [1 0] [0 1] 我需要生成所有这些矩阵 在python中,对于n=k,我现在有下面的蛮力方

我试图为给定的n生成所有可能的平方二元矩阵,这样: 对于每个二进制矩阵:

1) the rows sum up to at most 1
2) the columns sum up to at most 1
例如: 对于n=2,有效矩阵为:

[0 0]
[0 0]

[0 0]
[0 1]

[0 0]
[1 0]

[0 1]
[0 0]

[0 1]
[1 0]

[1 0]
[0 0]

[1 0]
[0 1]
我需要生成所有这些矩阵

在python中,对于n=k,我现在有下面的蛮力方法

    allwords = list(it.product(*([(0, 1)] * (n**2)))) # Generate all possible binary matrices
    allarrays = map(np.asarray, allwords)  # Convert to array # Convert them toarray
    allmatrices = [a.reshape(n, self.n) for a in allarrays]  # Matrixify # Make a matrix
# The following checks if the matrix has row sum at most 1 and column sum at most 1
    validActions = [x for x in allmatrices if contains(x)]  # Final list has only vlaid matrices
包含定义为

def contains(x): # Checks if row and column sums are at most 1 for each entry
        colSums = np.sum(x, axis=0)
        rowSums = np.sum(x, axis=1)
        return (np.all(colSums <= 1) and np.all(rowSums <= 1)) and np.all(x >= 0)
这在n=5或更高的情况下几乎是中断的,所以我需要一种更聪明的方法来做到这一点


目标是最终为强化学习创建一个离散状态空间,并将该离散状态空间中的每个条目映射到一个有效的二进制矩阵。有效的二进制矩阵是具有行和列的每一个矩阵,它们最多相加1。

< P>可以考虑递归的解决方案。考虑生成所有这样的NXN矩阵。首先生成顶行:它在某个位置有一个1,或者全是零。对于第一种情况,生成所有可能的n-1xn-1矩阵,然后通过在顶部插入新行,为每个列位置和每个较小的矩阵生成一个nxn矩阵。例如,如果您有子矩阵

0 1
1 0
您可以生成3x3矩阵

1 0 0   0 1 0   0 0 1
0 0 1   0 0 1   0 1 0
0 1 0   1 0 0   1 0 0
通过在顶部插入一行和一个新的额外列

然后,要处理全零行,只需生成所有可能的n-1xn矩阵,并在顶部添加一个零行


从数学上讲,这将生成您想要的所有矩阵

>你可以考虑递归的解决方案。考虑生成所有这样的NXN矩阵。首先生成顶行:它在某个位置有一个1,或者全是零。对于第一种情况,生成所有可能的n-1xn-1矩阵,然后通过在顶部插入新行,为每个列位置和每个较小的矩阵生成一个nxn矩阵。例如,如果您有子矩阵

0 1
1 0
您可以生成3x3矩阵

1 0 0   0 1 0   0 0 1
0 0 1   0 0 1   0 1 0
0 1 0   1 0 0   1 0 0
通过在顶部插入一行和一个新的额外列

然后,要处理全零行,只需生成所有可能的n-1xn矩阵,并在顶部添加一个零行


从数学上讲,这将生成您想要的所有矩阵

这个问题实际上只是一个有nxn个板和m 我们首先定义一种描述大小为nxn的二元矩阵的替代方法。我们考虑n个数的划分为路径和循环。如果一行具有传入边,则该行具有“1”。传入边的源表示该边所在的列。示例:

0->2, 1  
Row 2 has a one in column zero since it has an incoming edge from 0
0 0 0
0 0 0
1 0 0
---
0->2->0, 1->1
Row 2 has a one in column 0 since it has an incoming edge from 0
Row 0 has a one in column 2 since it has an incoming edge from 2
Row 1 has a one in column 1 since it has an incoming edge from 1
0 0 1
0 1 0
1 0 0
---
因此,我们将首先将n个数字划分为npart分区。我们需要考虑所有分区。 示例:我们将0、1、2、3划分为两个分区:[0、1、2]、[3]。 我们将允许每个分区形成一个循环或一个路径注意,路径是非循环的。我们需要考虑循环/路径的所有组合。 示例:我们让[0,1,2]形成一个循环,[3]形成一条路径。 我们根据每个分区是循环还是路径来排列它。我们将递归地考虑这一部分来考虑所有的安排。 示例:[0,1,2]只能形成循环0-1-2-0和0-2-1-0。请注意,1-2-0-1与0-1-2-0相同。 示例:如果我们选择[0,1,2]来形成路径的元素,那么所有长度为3的排列都是有效的:即0-1-2,0-2-1,1-0-2,1-2-0,2-0-1,2-1-0。 n=2的输出

n=4给出了32,由给出的解也是如此。 n=8给出1441729,由给出的解也是如此

原始答案不正确,因为仅考虑所有行均为1的情况 这里有一个解决这个问题的方法。首先,我们认识到,您想要的任何矩阵实际上都是单位矩阵的行置换。因此,我们只需要生成n行的所有可能排列,并将每个排列应用于单位矩阵。生成排列的一种方法是使用Heap算法。下面的代码是经过一些修改后无耻地从中摘取的

import numpy as np

def printArr(a):
    print('------')
    for i in range(n): 
        for j in range(n):
            print(a[i, j],end=" ") 
        print()

def heapPermutation(a, size, n): 

    # if size becomes 1 then prints the obtained 
    # permutation 
    if (size == 1): 
        printArr(I[a]) # permute the rows of I
        return

    for i in range(size): 
        heapPermutation(a,size-1,n); 

        # if size is odd, swap first and last 
        # element 
        # else If size is even, swap ith and last element 
        if size&1: 
            a[0], a[size-1] = a[size-1],a[0] 
        else: 
            a[i], a[size-1] = a[size-1],a[i] 

n = 3
I = np.identity(n, dtype=int) # use dtype=bool if that is sufficient
a = np.arange(n)
heapPermutation(a, n, n)
结果:

------
1 0 0
0 1 0
0 0 1
------
0 1 0
1 0 0
0 0 1
------
0 0 1
1 0 0
0 1 0
------
1 0 0
0 0 1
0 1 0
------
0 1 0
0 0 1
1 0 0
------
0 0 1
0 1 0
1 0 0

因为我不知道你想对矩阵做什么,所以我选择只打印出来,而不保存它们,虽然保存每一个矩阵可能需要大量的RAM来处理大型n。

这个问题实际上只是n x n板和m 我们首先定义一种描述大小为nxn的二元矩阵的替代方法。我们考虑n个数的划分为路径和循环。如果一行具有传入边,则该行具有“1”。传入边的源表示该边所在的列。示例:

0->2, 1  
Row 2 has a one in column zero since it has an incoming edge from 0
0 0 0
0 0 0
1 0 0
---
0->2->0, 1->1
Row 2 has a one in column 0 since it has an incoming edge from 0
Row 0 has a one in column 2 since it has an incoming edge from 2
Row 1 has a one in column 1 since it has an incoming edge from 1
0 0 1
0 1 0
1 0 0
---
因此,我们将首先将n个数字划分为npart分区。我们需要考虑所有分区。 示例:我们将0、1、2、3划分为两个分区:[0、1、2]、[3]。 我们将允许每个分区形成一个循环或一个路径注意,路径是非循环的。我们需要考虑循环/路径的所有组合。 示例:我们让[0,1,2]表示 m是一个循环[3]形成一条路径。 我们根据每个分区是循环还是路径来排列它。我们将递归地考虑这一部分来考虑所有的安排。 示例:[0,1,2]只能形成循环0-1-2-0和0-2-1-0。请注意,1-2-0-1与0-1-2-0相同。 示例:如果我们选择[0,1,2]来形成路径的元素,那么所有长度为3的排列都是有效的:即0-1-2,0-2-1,1-0-2,1-2-0,2-0-1,2-1-0。 n=2的输出

n=4给出了32,由给出的解也是如此。 n=8给出1441729,由给出的解也是如此

原始答案不正确,因为仅考虑所有行均为1的情况 这里有一个解决这个问题的方法。首先,我们认识到,您想要的任何矩阵实际上都是单位矩阵的行置换。因此,我们只需要生成n行的所有可能排列,并将每个排列应用于单位矩阵。生成排列的一种方法是使用Heap算法。下面的代码是经过一些修改后无耻地从中摘取的

import numpy as np

def printArr(a):
    print('------')
    for i in range(n): 
        for j in range(n):
            print(a[i, j],end=" ") 
        print()

def heapPermutation(a, size, n): 

    # if size becomes 1 then prints the obtained 
    # permutation 
    if (size == 1): 
        printArr(I[a]) # permute the rows of I
        return

    for i in range(size): 
        heapPermutation(a,size-1,n); 

        # if size is odd, swap first and last 
        # element 
        # else If size is even, swap ith and last element 
        if size&1: 
            a[0], a[size-1] = a[size-1],a[0] 
        else: 
            a[i], a[size-1] = a[size-1],a[i] 

n = 3
I = np.identity(n, dtype=int) # use dtype=bool if that is sufficient
a = np.arange(n)
heapPermutation(a, n, n)
结果:

------
1 0 0
0 1 0
0 0 1
------
0 1 0
1 0 0
0 0 1
------
0 0 1
1 0 0
0 1 0
------
1 0 0
0 0 1
0 1 0
------
0 1 0
0 0 1
1 0 0
------
0 0 1
0 1 0
1 0 0

因为我不知道你想对矩阵做什么,所以我选择只打印出来,而不保存它们,虽然保存每个矩阵可能需要大量的RAM以获得较大的n。

下面是另一种方法,它基于itertools在^2空间生成所有这样的矩阵。排列:


以下是另一种基于itertools.permutations在^2空间生成所有此类矩阵的方法:


请注意,该数字随n呈指数增长:请参阅每个n有多少个矩阵的列表。是的,绝对是。我想,我主要是需要它为n=5/6工作。我只是想确保没有一种方法可以做到这一点,这样我就不会产生所有可能的结果,然后再减少它。我想这就是为什么现在这么慢的原因。请注意,这个数字随n呈指数增长:请参阅每个n有多少个矩阵的列表。是的,绝对是这样。我想,我主要是需要它为n=5/6工作。我只是想确保没有一种方法可以做到这一点,这样我就不会产生所有可能的结果,然后再减少它。我想这就是为什么现在速度变慢的原因。在你的例子中,行和列的总和都是1。OP要求最多1。修复了该解决方案。示例中的行和列总计为1。OP要求最多1。修复了解决方案。这是否解决了总和最多为1的规定?是的。每次在顶部插入新行时,也会插入只有一个1的列,因此在任何行或列中都不会有多个1。我还使用这种递归策略来生成这种形式的矩阵数量,并将其与OEIS序列进行匹配。这似乎很难理解。你能通过一个小例子来详细说明算法吗?这是否解决了求和最多为1的规定?是的。每次在顶部插入新行时,也会插入只有一个1的列,因此在任何行或列中都不会有多个1。我还使用这种递归策略来生成这种形式的矩阵数量,并将其与OEIS序列进行匹配。这似乎很难理解。你能通过一个小例子详细说明一下算法吗?