一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情。
先前我们实现了从数据集到模糊相似矩阵的转变。
有了相似矩阵,就引出了进一步的操作。
找一个等价矩阵,由等价矩阵实现划分。
我们的重点在于代码上的实现,具体的原理可以参考相关wiki。
模糊矩阵是从布尔矩阵推广得到的。
先前也说过,它把布尔代数的加法和乘法推广成了所谓的模糊乘法和模糊加法。
基于此,布尔矩阵的乘法也可以进一步的推广成为如下的形式:
def mul(a, b):
c = np.zeros((a.shape[0], b.shape[1]))
for i in range(a.shape[0]):
for j in range(b.shape[1]):
c[i, j] = np.max(np.minimum(a[i], b[:, j])) # 1
return c
如果认为运算行的取最大最小值是或和与运算(注意它是兼容的),那么这里完全兼容布尔代数的。
现在由推广的两个操作,得到了一种新的类似矩阵乘法的运算。我们称之为极大极小复合运算。
这个运算的用处在于另一个推广的操作:所谓的求传递闭包。
方法使用的是不断的求矩阵和自身的极大极小复合运算,直到复合操作不再是矩阵变化,这个时候矩阵被成为传递闭包矩阵。
注意到它和布尔代数的相似性,我们猜测它应该也可以通过弗洛伊德算法计算传递闭包。
至少我们猜测当模糊矩阵退化成布尔矩阵时成立。
幸运的是,有人证明了这个过程的正确性,具体可以通过谷歌搜索相关内容。 所以我们最终可以实现一个相对高效的求传递闭包的办法,至少比不断地求自身复合的复杂度稳定。
代码如下:
def floyd_warshall(x: Tensor) -> Tensor: # 计算传递闭包
dp = x.clone() # copy tensor
n = len(x)
for k in range(n):
for i in range(n):
for j in range(n):
dp[i, j] = max(dp[i, j], min(dp[i, k], dp[k, j]))
return dp