三角矩阵系数的索引数算法

时间:2008-10-28 09:58:15

标签: algorithm

我认为这一定很简单,但我做不到......

我有一个MxM三角矩阵,其系数逐行存储在矢量中。 例如:

M =   [ m00 m01 m02 m03 ] 
      [     m11 m12 m13 ]
      [         m22 m23 ]
      [             m33 ]

存储为

coef[ m00 m01 m02 m03 m11 m12 m13 m22 m23 m33 ]

现在我正在寻找一种非递归算法,它可以提供矩阵大小M和系数数组索引i

unsigned int row_index(i,M)

unsigned int column_index(i,M)
它引用的矩阵元素的

。所以, 如果索引计数从零开始,则为row_index(9,4) == 3column_index(7,4) == 2等。

编辑:已经给出了几个使用迭代的回复。有没有人知道代数表达式?

7 个答案:

答案 0 :(得分:20)

本回复最后的单行,解释如下: - )

系数数组具有:第一行的M个元素(索引中的第0行),第二行(第1行)的(M-1),依此类推,总共为M +(M-1) + ... + 1 = M(M + 1)/ 2个元素。

从最后开始工作会稍微容易一些,因为系数数组总是最后一行(行M-1)有1个元素,第二行有2个元素(行M- 2),行M-3的3个元素,依此类推。最后K行占用系数数组的最后K(K + 1)/ 2个位置。

现在假设你在系数数组中得到一个索引i。在大于i的位置处存在M(M + 1)/ 2-1-i个元素。拨打这个号码我'; 你想要找到最大的k,使得k(k + 1)/2≤i'。这个问题的形式是你苦难的根源 - 据我所见,你无法避免占据平方根: - )

无论如何,让我们这样做:k(k + 1)≤2i'表示(k + 1/2) 2 - 1 /4≤2i',或等效k≤(sqrt(8i) “+ 1)-1)/ 2。让我将最大的k称为K,然后有K行(稍后是总共M行),因此row_index(i,M)是M-1-K。对于列索引,i'中的K(K + 1)/ 2个元素在后面的行中,因此该行中有j'= i'-K(K + 1)/ 2个后面的元素(具有总共K + 1个元素),因此列索引为K-j'。 [或等效地,此行从结尾开始于(K + 1)(K + 2)/ 2,因此我们只需要从i中减去该行的起始位置:i- [M(M + 1)/ 2 - (K + 1)(K + 2)/ 2]。令人鼓舞的是,两个表达都给出了相同的答案!]

(伪)代码[使用ii代替i',因为某些语言不允许使用名称为i'的变量; - )]:

row_index(i, M):
    ii = M(M+1)/2-1-i
    K = floor((sqrt(8ii+1)-1)/2)
    return M-1-K

column_index(i, M):
    ii = M(M+1)/2-1-i
    K = floor((sqrt(8ii+1)-1)/2)
    return i - M(M+1)/2 + (K+1)(K+2)/2

当然,您可以通过将表达式替换为ii和K来将它们转换为单行。您可能必须注意精度误差,但是有一些方法可以找到不需要浮点计算的整数平方根。另外,引用Knuth:“注意上面代码中的错误;我只是证明它是正确的,没有尝试过。”

如果我冒昧地进一步评论:简单地将所有值保持在M×M阵列中将最多占用两倍的内存,并且根据您的问题,与算法改进相比,因子2可能无关紧要,并且可能值得交换平方根的可能昂贵的计算,以获得更简单的表达式。

[编辑:BTW,你可以证明楼层((sqrt(8ii + 1)-1)/ 2)=(isqrt(8ii + 1)-1)/ 2其中isqrt(x)= floor(sqrt( x))是整数平方根,除法是整数除法(截断; C / C ++ / Java等中的默认值) - 所以如果你担心精度问题,你只需要担心实现一个正确的整数平方根。]

答案 1 :(得分:7)

这是一个代数(主要)解决方案:

unsigned int row_index( unsigned int i, unsigned int M ){
    double m = M;
    double row = (-2*m - 1 + sqrt( (4*m*(m+1) - 8*(double)i - 7) )) / -2;
    if( row == (double)(int) row ) row -= 1;
    return (unsigned int) row;
}


unsigned int column_index( unsigned int i, unsigned int M ){
    unsigned int row = row_index( i, M);
    return  i - M * row + row*(row+1) / 2;
}

编辑:修复了row_index()

答案 2 :(得分:2)

必须是那个

i == col + row*(M-1)-row*(row-1)/2

因此,找到col和row的一种方法是迭代row的可能值:

for(row = 0; row < M; row++){
  col = i - row*(M-1)-row*(row-1)/2
  if (row <= col < M) return (row,column);
}

这至少是非递归的,我不知道你是否可以在没有迭代的情况下做到这一点。

从这个和其他答案中可以看出,你几乎必须计算行才能计算列,所以在一个函数中同时执行这两个操作可能是明智的。

答案 3 :(得分:2)

这些可能有一个聪明的衬垫,但(减去任何错误检查):

unsigned int row_index( unsigned int i, unsigned int M ){
    unsigned int row = 0;
    unsigned int delta = M - 1;
    for( unsigned int x = delta; x < i; x += delta-- ){
        row++;
    }
    return row;
}

unsigned int column_index( unsigned int i, unsigned int M ){
    unsigned int row = 0;
    unsigned int delta = M - 1;
    unsigned int x;
    for( x = delta; x < i; x += delta-- ){
        row++;
    }
    return M + i - x - 1;
}

答案 4 :(得分:2)

ShreevatsaR的解释非常好,帮助我解决了我的问题。但是,为列索引提供的解释和代码给出的答案与问题要求的答案略有不同。

重申一下,i之后的行中有j'= i' - K(K + 1)/ 2个元素。但是,就像所有其他行一样,该行具有M个元素。因此,(从零开始)列索引是y = M-1-j'。

相应的伪代码是:

column_index(i, M):
  ii = M(M+1)/2-1-i
  K = floor((sqrt(8ii+1)-1)/2)
  jj = ii - K(K+1)/2
  return M-1-jj

ShreevatsaR给出的答案,K - j',是从矩阵的对角线开始计数(零)时的列索引。因此,他的计算给出column_index(7,4)= 0,而不是如问题中所指定的,column_index(7,4)= 2.

答案 5 :(得分:1)

我想了一下,我得到了以下结果。请注意,您只需一次获得行和列。

假设:行开始为0.列从0开始。索引从0开始

符号

N =矩阵大小(原问题中为M)

m =元素的索引

伪代码是

function ind2subTriu(m,N)
{
  d = 0;
  i = -1;
  while d < m
  {
    i = i + 1
    d = i*(N-1) - i*(i-1)/2
  }
  i0 = i-1;
  j0 = m - i0*(N-1) + i0*(i0-1)/2 + i0 + 1;
  return i0,j0
}

一些八度/ matlab代码

function [i0 j0]= ind2subTriu(m,N)
 I = 0:N-2;
 d = I*(N-1)-I.*(I-1)/2;
 i0 = I(find (d < m,1,'last'));
 j0 = m - d(i0+1) + i0 + 1;

您怎么看?

截至2011年12月,在GNU / Octave中添加了非常好的代码。他们可能会扩展sub2ind和ind2sub。目前,代码可以作为私有函数ind2sub_trilsub2ind_tril

找到

答案 6 :(得分:0)

花了一些时间来了解你的需求! :)

unsigned int row_index(int i, int m)
{
    int iCurrentRow = 0;
    int iTotalItems = 0;
    for(int j = m; j > 0; j--)
    {
        iTotalItems += j;

        if( (i+1) <= iTotalItems)
            return iCurrentRow;

        iCurrentRow ++;
    }

    return -1; // Not checking if "i" can be in a MxM matrix.
}

抱歉忘记了其他功能.....

unsigned int column_index(int i, int m)
{
    int iTotalItems = 0;
    for(int j = m; j > 0; j--)
    {
        iTotalItems += j;

        if( (i+1) <= iTotalItems)
            return m - (iTotalItems - i);
    }

    return -1; // Not checking if "i" can be in a MxM matrix.
}