web-dev-qa-db-ja.com

線形インデックス上三角行列

行列の上三角部分があり、対角線より上にオフセットされ、線形配列として保存されている場合、行列要素の_(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]
_
34

線形インデックスから(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
40

最初に、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ではなく、「フリップ」する必要があります。

正確な式は書きませんでしたが、おわかりいただけたでしょうか。退屈で単純な計算を実行した後で簡単に見つけられるようになりました。

5
Mikhail Maltsev

以下は、C++などの別の言語に簡単に転送できる、MATLABでの実装です。ここでは、行列のサイズが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)];
3
study

Pythonでは:

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
1
smac89