计算所有子矩阵的满级排序数

时间:2014-01-22 10:50:32

标签: python performance algorithm math numpy

我想计算其中元素为1或-1的n个矩阵有多少m具有其所有floor(m/2)+1 by n子矩阵都具有满秩的属性。我当前的方法是天真和缓慢的,并在以下python / numpy代码中。它只是迭代所有矩阵并测试所有子矩阵。

import numpy as np
import itertools
from scipy.misc import comb

m = 8
n = 4

rowstochoose = int(np.floor(m/2)+1)

maxnumber = comb(m, rowstochoose, exact = True)

matrix_g=(np.array(x).reshape(m,n) for x in itertools.product([-1,1], repeat = m*n))

nofound = 0
for A in matrix_g:
    count = 0
    for rows in itertools.combinations(range(m), int(rowstochoose)):
       if (np.linalg.matrix_rank(A[list(rows)]) == int(min(n,rowstochoose))):
           count+=1
       else:
           break
    if (count == maxnumber):
         nofound+=1   
print nofound, 2**(m*n)

有更好/更快的方法吗?我想对n和m进行最多20次的计算,但任何重大改进都会很棒。

上下文。我有兴趣为https://math.stackexchange.com/questions/640780/probability-that-every-vector-is-not-orthogonal-to-half-of-the-others获取一些确切的解决方案。


作为比较实现的数据点。 n,m = 4,4应输出26880。 n,m=5,5对我来说太慢了。对于n = 2和m = 2,3,4,5,6,输出应为8, 0, 96, 0, 1280


当前状态2014年2月2日:

  • leewangzhong的答案很快,但对于m>是不正确的。 n。 leewangzhong正在考虑如何解决它。
  • Hooked的答案不适用于m> n。

4 个答案:

答案 0 :(得分:3)

(现在是n = m // 2 + 1的部分解决方案,以及所请求的代码。)

设k:= m // 2 + 1

这有点等同于问:“{-1,1}的m维向量的多少个集合没有线性相关的大小为min(k,n)的集合?”

对于那些矩阵,我们知道或可以假设:

  • 每个向量的第一个条目是1(如果不是,则将整数乘以-1)。这将计数减少了2 ** m。
  • 列表中的所有向量都是不同的(如果没有,任何具有两个相同向量的子矩阵都具有非满级)。这消除了很多。有选择(2 ** m,n)不同向量的矩阵。
  • 向量列表按字典顺序排序(排名不受排列影响)。所以我们真的在考虑矢量集而不是列表。这将计数减少了几倍! (因为我们需要清晰度)。

有了这个,我们有一个n = 4,m = 8的解决方案。只有八个不同的向量具有第一个条目为正的属性。 8种不同的载体中只有8种不同载体的组合(分类列表)。

array([[ 1,  1,  1,  1],
       [ 1,  1,  1, -1],
       [ 1,  1, -1,  1],
       [ 1,  1, -1, -1],
       [ 1, -1,  1,  1],
       [ 1, -1,  1, -1],
       [ 1, -1, -1,  1],
       [ 1, -1, -1, -1]], dtype=int8)

此列表中的100个大小为4的组合具有等级3.因此该属性有0个矩阵。


更通用的解决方案:

请注意,2**(n-1)个向量具有第一个坐标-1和choose(2**(n-1),m)个矩阵以进行检查。对于n = 8和m = 8,有128个向量,和1.4297027e + 12个矩阵。它可能有助于回答:“对于i = 1,...,k,有多少组合排名i?”

或者,“什么样的矩阵(具有上述假设)的排名低于满级?” 我认为答案正是如此,一个充分的条件是,“两列是彼此的倍数”。 我感觉这是真的,我测试了所有4x4,5x5和6x6矩阵。(必须搞砸了测试)因为第一列被选为同质的,并且因为所有同质向量是彼此的倍数,所以具有除第一列之外的同质列的大小为k的任何子矩阵将具有小于k的秩。

但这并非必要条件。以下矩阵是单数的(第一个加第四个等于第三个加第二个)。

array([[ 1,  1,  1,  1,  1],
       [ 1,  1,  1,  1, -1],
       [ 1,  1, -1, -1,  1],
       [ 1,  1, -1, -1, -1],
       [ 1, -1,  1, -1,  1]], dtype=int8)

由于只有两个可能的值(-1和1),所有mxn矩阵中m>2, k := m//2+1, n = k和第一列-1在每列中都有多数成员(即至少k个成员是相同的)。所以对于n = k,答案​​是0。


对于n< = 8,这是生成向量的代码。

from numpy import unpackbits, arange, uint8, int8

#all distinct n-length vectors from -1,1 with first entry -1
def nvectors(n):
    if n > 8:
        raise ValueError #is that the right error?
    return -1 + 2 * (
        #explode binary numbers to arrays of 8 zeroes and ones
        unpackbits(arange(2**(n-1),dtype=uint8)) #unpackbits only takes uint
            .reshape((-1,8)) #unpackbits flattens, so we need to shape it to 8 bits
            [:,-n:] #only take the last n bytes
            .view(int8) #need signed
    )

矩阵生成器:

#generate all length-m matrices that are combinations of distinct n-vectors
def matrix_g(n,m):
    return (array(mat) for mat in combinations(nvectors(n),m))

以下是检查长度maxrank的所有子矩阵是否具有满秩的函数。如果任何小于maxrank,它会停止,而不是检查所有组合。

rankof = np.linalg.matrix_rank
#all submatrices of at least half size have maxrank
#(we only need to check the maxrank-sized matrices)
def halfrank(matrix,maxrank):
return all(rankof(submatr) == maxrank for submatr in combinations(matrix,maxrank))

生成具有全等级的所有半矩阵的所有矩阵     def nicematrices(m,n):         maxrank = min(m // 2 + 1,n)         返回(matr为矩阵_g(n,m)中的matr,如果是halfrank(matr,maxrank))

全部放在一起:

import numpy as np
from numpy import unpackbits, arange, uint8, int8, array
from itertools import combinations

#all distinct n-length vectors from -1,1 with first entry -1
def nvectors(n):
    if n > 8:
        raise ValueError #is that the right error?
    if n==0:
        return array([])
    return -1 + 2 * (
        #explode binary numbers to arrays of 8 zeroes and ones
        unpackbits(arange(2**(n-1),dtype=uint8)) #unpackbits only takes uint
            .reshape((-1,8)) #unpackbits flattens, so we need to shape it to 8 bits
            [:,-n:] #only take the last n bytes
            .view(int8) #need signed
    )

#generate all length-m matrices that are combinations of distinct n-vectors
def matrix_g(n,m):
    return (array(mat) for mat in combinations(nvectors(n),m))

rankof = np.linalg.matrix_rank
#all submatrices of at least half size have maxrank
#(we only need to check the maxrank-sized matrices)
def halfrank(matrix,maxrank):
    return all(rankof(submatr) == maxrank for submatr in combinations(matrix,maxrank))

#generate all matrices that have all half-matrices with full rank
def nicematrices(m,n):
    maxrank = min(m//2+1,n)
    return (matr for matr in matrix_g(n,m) if halfrank(matr,maxrank))

#returns (number of nice matrices, number of all matrices)
def count_nicematrices(m,n):
    from math import factorial
    return (len(list(nicematrices(m,n)))*factorial(m)*2**m, 2**(m*n))

for i in range(0,6):
    print (i, count_nicematrices(i,i))

count_nicematrices(5,5)对我来说大约需要15秒,其中绝大多数是由matrix_rank函数执行的。

答案 1 :(得分:2)

由于还没有人回答,这里是没有代码的答案。我看到的有用对称如下。

  1. 将一行乘以-1。
  2. 将列乘以-1。
  3. 置换行。
  4. 置换列。
  5. 我会通过exhaustively generating the non-isomorphs攻击这个问题,过滤它们,并总结它们的轨道大小。 nauty对第一步和第三步非常有用。假设大多数矩阵具有很少的对称性(毫无疑问是n大的优秀假设,但先验并不明显有多大),我认为8x8是可行的,9x9是临界的,10x10是遥不可及的。

    扩展伪代码:

    1. 生成(m - 1)每个轨道的一个代表,由行和列排列产生的组作用的(n - 1)0-1矩阵,以及轨道的大小(= (m - 1)!(n - 1)!/自同构群的大小)。也许蒂姆所关联的论文的作者愿意分享他的代码;否则,见下文。

    2. 对于每个矩阵,将条目x替换为(-1)^ x。添加一行和一列1。将其轨道的大小乘以2 ^(m + n - 1)。这样可以处理符号变化的对称性。

    3. 过滤矩阵并总结剩余物的轨道大小。您可以通过自己实施Gram - Schmidt来节省一点计算时间,这样当您按字典顺序尝试所有组合时,就有机会重复使用共享前缀的部分结果。

    4. 无同构素枚举:

      McKay的模板可以用于生成(m + 1)代表的代表,用于代表m 0到0 0-1矩阵的n 0-1矩阵,其方式适合深度优先搜索。使用每个m乘以n 0-1矩阵,将二分图与m个黑色顶点,n个白色顶点以及每个条目的适当边相关联。每个m代表执行以下操作。

      1. 对于每个长度为n的向量,构造(m + 1)由n矩阵构成的图,该矩阵由代表和新向量组成,并运行nauty以获得规范标记和顶点轨道。

      2. 过滤掉与新矢量对应的顶点与数字最小的黑色顶点处于不同轨道的可能性。

      3. 使用重复的规范标签过滤掉可能性。

      4. nauty还计算自同构群的顺序。

答案 2 :(得分:2)

需要从数学的角度重新思考这个问题。即使有蛮力,也可以使用一些编程技巧来加速这个过程(因为SO是一个编程站点)。不重新计算int(min(n,rowstochoose))itertools.combinations(range(m), int(rowstochoose))的小技巧可以节省几个百分点 - 但真正的收获来自于记忆。其他人提到过它,但我认为有一个完整的,有效的代码示例可能是有用的:

import numpy as np
from scipy.misc import comb
import itertools, hashlib

m,n = 4,4

rowstochoose = int(np.floor(m/2)+1)
maxnumber    = comb(m, rowstochoose, exact = True)
combo_itr    = (x for x in itertools.product([-1,1], repeat = m*n))
matrix_itr   = (np.array(x,dtype=np.int8).reshape((n,m)) for x in combo_itr)

sub_shapes = map(list,(itertools.combinations(range(m), int(rowstochoose))))
required_rank = int(min(n,rowstochoose))

memo = {}

no_found = 0
for A in matrix_itr:
    check = True
    for s in sub_shapes:
        view  = A[s].view(np.int8)
        h    = hashlib.sha1(view).hexdigest()

        if h not in memo: 
            memo[h] =  np.linalg.matrix_rank(view)

        if memo[h] != required_rank:
            check = False
            break
    if check: no_found+=1   

print no_found, 2**(m*n)

这为4x4情况提供了近10倍的速度增益 - 如果您需要等待足够长的时间,您将看到更大矩阵的实质性改进。对于较大的矩阵而言,等级的成本可能会成比例地更加昂贵,您可以在散列时提前订购矩阵:

idx  = np.lexsort(view.T)
h    = hashlib.sha1(view[idx]).hexdigest()

对于4x4的情况,这会让它稍微恶化,但我希望在5x5的情况下能够逆转。

答案 3 :(得分:1)

算法1 - 记忆小的

我会记住已经检查过的较小的矩阵。

您可以简单地以二进制格式(0表示-1,1表示1表示)写下所有较小的矩阵。顺便说一句,你直接检查范围矩阵(0和1)而不是(-1和1) - 它是相同的。我们称这些编码为IMAGES。使用长类型,您可以使用多达64个单元格的矩阵,因此最多可达8x8。它很快。使用String,您可以根据需要使用它们。 真的,8x8绰绰有余 - 在8GB内存中我们可以放置1G长。它大概是2 ^ 30,所以,你可以记住大约25-28个元素的矩阵。

对于每种尺寸,您都会有一组图像:

for 2x2:1001,0110,1000,0100,0010,0001,0111,1011,1101,1110。

因此,您将拥有archive = NxN数组,其中每个元素都是良好矩阵的二进制图像的有序列表。 - (对于矩阵大小MxN,其中M> = N,归档中的适当位置将具有坐标M,N。如果M

  • 检查新的大型矩阵时,将其划分为小型矩阵
  • 对于每个小矩阵T.
    • 如果存档中T大小的适当位置没有列表,则创建它并填充大小为T和订单图像的所有全等级矩阵的图像。如果内存不足,请停止存档填充过程。
    • 如果T可以存档,根据大小:
      • 制作T
      • 的图像
      • 在列表中查找图像(t) - 如果它在其中,则可以,如果不是,则应该抛弃大矩阵。
    • 如果T对于存档而言太大,请检查它。

算法2 - 增加大小

另一种可能性是通过添加已经找到的较小的矩阵来创建更大的矩阵。 您应该决定矩阵的大小。

当您找到大小为MxN的“正确”矩阵时,请尝试在其顶部添加一行。应仅检查包含新行的子矩阵的新矩阵。与新专栏相同。

您应该设置精确算法,哪些大小来自哪些大小。因此,您可以最小化记住的矩阵的数量。我想到了那个序列:

  • 从2x2矩阵开始。
  • 继续3x2
  • 4x2,3x3
  • 5x2,4x3
  • 6x2,5x3,4x4
  • 7x2,6x3,5x4
  • ...

所以你只记得(M + N)/ 2-1矩阵用于搜索大小为MxN的内容。

如果每次我们可以从两个旧的大小创建新的大小,我们从更方形的大小派生,我们也可以大大地为矩阵记忆:对于“长”矩阵作为7x2我们需要记住并且只检查最后一个1x2行。对于矩阵6x3,我们应该记住它们的2x3存根,依此类推。

此外,您不需要记住最大的矩阵 - 您不会将它们用于进一步计算。

再次使用“图像”来记住矩阵。