46

如果我有矩阵的上三角部分,在对角线上偏移,存储为线性数组,如何(i,j)从数组的线性索引中提取矩阵元素的索引?

例如,线性数组[a0, a1, a2, a3, a4, a5, a6, a7, a8, a9是存储矩阵

0  a0  a1  a2  a3
0   0  a4  a5  a6
0   0   0  a7  a8
0   0   0   0  a9
0   0   0   0   0

我们想知道数组中的 (i,j) 索引对应于线性矩阵中的偏移量,而不需要递归。

例如,一个合适的结果k2ij(int k, int n) -> (int, int)将满足

k2ij(k=0, n=5) = (0, 1)
k2ij(k=1, n=5) = (0, 2)
k2ij(k=2, n=5) = (0, 3)
k2ij(k=3, n=5) = (0, 4)
k2ij(k=4, n=5) = (1, 2)
k2ij(k=5, n=5) = (1, 3)
 [etc]
4

5 回答 5

54

从线性指数到(i,j)指数的方程是

i = n - 2 - floor(sqrt(-8*k + 4*n*(n-1)-7)/2.0 - 0.5)
j = k + i + 1 - n*(n-1)/2 + (n-i)*((n-i)-1)/2

(i,j)从索引到线性索引的逆运算是

k = (n*(n-1)/2) - (n-i)*((n-i)-1)/2 + j - i - 1

在 Python 中验证:

from numpy import triu_indices, sqrt
n = 10
for k in range(n*(n-1)/2):
    i = n - 2 - int(sqrt(-8*k + 4*n*(n-1)-7)/2.0 - 0.5)
    j = k + i + 1 - n*(n-1)/2 + (n-i)*((n-i)-1)/2
    assert np.triu_indices(n, k=1)[0][k] == i
    assert np.triu_indices(n, k=1)[1][k] == j

for i in range(n):
    for j in range(i+1, n):
        k = (n*(n-1)/2) - (n-i)*((n-i)-1)/2 + j - i - 1
        assert triu_indices(n, k=1)[0][k] == i
        assert triu_indices(n, k=1)[1][k] == j
于 2014-11-23T11:44:37.700 回答
5

首先,让我们以相反的顺序重新编号 a[k]。我们会得到:

0  a9  a8  a7  a6
0   0  a5  a4  a3
0   0   0  a2  a1
0   0   0   0  a0
0   0   0   0   0

然后 k2ij(k, n) 将变为 k2ij(n - k, n)。

现在,问题是,如何在这个新矩阵中计算 k2ij(k, n)。序列 0、2、5、9(对角线元素的索引)对应于三角形数(减去 1 后):a[n - i, n + 1 - i] = Ti - 1. Ti = i * (i + 1)/2,所以如果我们知道 Ti,很容易解这个方程,得到 i (请参阅链接的 wiki 文章中的公式,“三角根和三角数测试”部分)。如果 k + 1 不完全是一个三角数,这个公式仍然会给你有用的结果:四舍五入后,你会得到 i 的最大值,对于其中 Ti <= k,这个 i 的值对应于a[k] 所在的行索引(从底部开始计数)。要获得列(从右数),您应该简单地计算 Ti 的值并减去它:j = k + 1 - Ti。需要明确的是,这些并不是您的问题中的 i 和 j,您需要“翻转”它们。

我没有写出确切的公式,但我希望你明白了,在执行一些无聊但简单的计算之后,现在找到它是微不足道的。

于 2014-11-23T07:57:55.610 回答
4

以下是matlab中的一个实现,可以很容易地转换成另一种语言,比如C++。在这里,我们假设矩阵的大小为 m*m,ind 是线性数组中的索引。唯一不同的是,在这里,我们逐列计算矩阵的下三角部分,这与您的情况类似(逐行计算上三角部分)。

function z= ind2lTra (ind, m)
  rvLinear = (m*(m-1))/2-ind;
  k = floor( (sqrt(1+8*rvLinear)-1)/2 );

  j= rvLinear - k*(k+1)/2;

  z=[m-j, m-(k+1)];
于 2015-04-13T21:17:08.280 回答
2

对于记录,这是相同的函数,但使用基于 1 的索引,在 Julia 中:

function iuppert(k::Integer,n::Integer)
  i = n - 1 - floor(Int,sqrt(-8*k + 4*n*(n-1) + 1)/2 - 0.5)
  j = k + i + ( (n-i+1)*(n-i) - n*(n-1) )÷2
  return i, j
end
于 2021-07-29T18:23:58.650 回答
1

在python 2中:

def k2ij(k, n):
    rows = 0
    for t, cols in enumerate(xrange(n - 1, -1, -1)):
        rows += cols
        if k in xrange(rows):
            return (t, n - (rows - k))
    return None
于 2014-11-23T17:46:15.307 回答