【问题标题】:How can I remove duplicates from a list of matrices based on some equivalence relation?如何根据某些等价关系从矩阵列表中删除重复项?
【发布时间】:2017-08-17 13:13:46
【问题描述】:

给定一些对称整数矩阵列表,我想删除以下等价关系下的所有重复项:

如果{1,...,k} 上有一些排列s,则两个k x k 矩阵M1M2 是等价的,这样对于{1,...,k} 中的所有ij,我们有M1_ij = M2_s(i)s(j),即两个如果我可以通过同时排列矩阵的行和列从另一个矩阵中得到一个矩阵,那么矩阵是“相等的”。

不幸的是,我的幼稚方法(在构建列表时,检查新矩阵的任何排列是否已经在列表中)被证明太慢了。

我能想到的一些可能更快的替代方法是将所有矩阵放入列表中,将它们排列为一些“规范排列”,然后按照例如所述删除重复项。 here。但是,我也不确定如何在代码中实现这样的“规范排列”。

为了进一步缩小范围:矩阵相对较小 (k <= 4),列表将包含一些 5 或 6 位数的矩阵,并且矩阵的 dtype 必须是某种整数类型(目前 @ 987654332@,但我可以更改)。

最终列表的顺序无关紧要,每个等价类的哪个代表幸存下来也无关紧要。如果需要,整个过程可能需要几个小时,但不是几天。

有没有一些相当有效的方法来实现这一点?我是否(再次)错过了一些可以帮助我解决此问题的酷 NumPy 或 SciPy 工具?


根据要求,提供一些小示例来演示该等价关系的工作原理:

矩阵{{1,1,1},{1,2,0},{1,0,3}}{{1,1,1},{1,3,0},{1,0,2}} 是等价的,因为排列{1,2,3}->{1,3,2} 将一个转换为另一个。

矩阵{{1,1,1},{1,2,0},{1,0,3}}{{1,1,0},{1,2,1},{0,1,3}} 不等价,如果不改变对角线,就不能改变 1 的位置。

【问题讨论】:

  • 只是为了澄清:行和列的排列应该是相同的?
  • @MSeifert 加了一些例子,够清楚吗?
  • 简短说明:同一类的两个矩阵必须具有相同的特征值。这可以进行第一次测试。考虑到您的矩阵较小,它可能相当有效。
  • 说到快速测试必要条件,你可以从比较np.sort(np.diag(M))开始。
  • 为什么不在np.all(np.sort(M.ravel()), np.sort(N.ravel()))时对所有条目进行排序和比较?!

标签: python numpy


【解决方案1】:

这是一个代数答案。我怀疑应该有一个更令人满意的组合答案。

如果存在一个permutation matrix P 使得 M' = P^{-1} M P,你说两个矩阵 M 和 M' 是等价的。

让我们使用 M 和 M' 的特征分解:

M = Q^{-1} D Q

M = Q'^{-1} D'Q'

其中 D 和 D' 是包含特征值的对角矩阵,Q 和 Q' 是正交矩阵。

我们可以将等式改写为:

D = D' 直至排列(即两个矩阵应具有相同的特征值

Q' = PQ

第二个条件的测试很容易。鉴于 Q 是正交的,它相当于检查矩阵 dot(Q, Q'.T) 是否是一个置换矩阵,即它是否每行和每列只有一个“1”。


因此,算法的草稿是:

  • 拿 M 和 M'
  • 计算 M 和 M' 的特征分解 (Q, D) 和 (Q', D')(使用 np.linalg.eigh)
  • 如果它们没有相同的特征值(当然要达到数值精度),它们就不等价
  • 否则,计算 np.dot(Q, Q'.T) 并测试它是否为置换矩阵

我认为瓶颈是特征分解,但每个矩阵只需执行一次。希望第一次测试能很快丢弃​​很多矩阵。

希望这会有所帮助。

【讨论】:

  • 谢谢,我会试试这个和图一,可能结合一些“桶解决方案”来摆脱 O(n^2)。
  • 即使你有一种方法可以在小于 O(n^2) 的时间内解决等价类,在某些时候你必须比较两个矩阵,这总是 O(n^2) (除非你说你不需要 100% 的确定性并且只会比较几个元素)。
【解决方案2】:

您可以将矩阵视为表示图的邻接/权重矩阵,然后测试这两个图是否彼此同构。 networkx 有一个方便的功能(可以通过pip安装)。

import numpy as np
import networkx as nx
from networkx.algorithms.isomorphism import numerical_edge_match

# create matrices
n = 4
a = np.random.randint(0, 10, size=(n,n))
a = a + a.T # i.e. symmetric
b = np.rot90(a, k=2) # i.e. a rotated by 180 degrees
c = np.ones((n,n), dtype=np.int) # counter-example

# create graphs
ga = nx.from_numpy_matrix(a)
gb = nx.from_numpy_matrix(b)
gc = nx.from_numpy_matrix(c)

# test if isomorphic
print "a isomorphic with b:", nx.is_isomorphic(ga, gb, edge_match=numerical_edge_match('weight', 1)) # True
print "a isomorphic with c:", nx.is_isomorphic(ga, gc, edge_match=numerical_edge_match('weight', 1)) # False

【讨论】:

  • 看起来很有趣,谢谢。让我试着看看我是否可以用它来摆脱我目前的 O(n^2) 方法;如果没有,我将不得不测量这是否足够快。
  • 还可以看看graph-tool (graph-tool.skewed.de) 和igraph,因为它们的核心函数是用C 和C++ 编写的,所以速度可能更快。
【解决方案3】:

只需使用您的规范方法。 搜索矩阵中最大的条目,将其放在右上角。 然后根据条目对第一列和第一行进行排序。

A = np.array([[1,2,3,5],
     [3,6,2,6],
     [3,5,7,2],
     [1,3,6,3]])
a = np.where(A == np.amax(A))
sort_colums = np.argsort(A[a[0]].ravel())[::-1]
sort_rows = np.argsort(A[:,a[1]].ravel())[::-1]
Col_sorted = A[:,sort_colums]
Equiv_class = Col_sorted[sort_rows]
#returns [[7, 5, 3, 2],
          [6, 3, 1, 3],
          [3, 2, 1, 5],
          [2, 6, 3, 6]]

正如 cmets 中所指出的,这仅在矩阵的条目仅出现一次时才有效。如果确实发生了几次,但不是那么频繁,那么可以通过生成几个等价类来适应这种方法。

【讨论】:

  • 矩阵中不需要有一个最大的元素,我相信这很重要,不是吗? (虽然还不熟悉这些功能,但我会检查一下。)
  • 这似乎确实不适用于像[[1,1,1],[1,1,0],[1,0,1]] 这样的矩阵。 "TypeError: 只有长度为 1 的数组可以转换为 Python 标量"
  • @BaummitAugen 实际上,它只适用于具有一个特定最大值的矩阵。然而,对于这些矩阵,算法是 O(n log(n)) 将矩阵转换为等价类。我更新了代码(我犯了一个小错误)
猜你喜欢
  • 2021-05-14
  • 1970-01-01
  • 1970-01-01
  • 2013-05-28
  • 2019-11-09
  • 2017-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多