c++arraysnumpylinear-algebratriangular

Linear index for a diagonal run of an upper triangular matrix


Given a NxN matrix, I would like to linearly index into its upper right triangle, following a diagonal by diagonal pattern, starting after the main diagonal.

For example, given a 4x4 matrix

X 0 3 5
X X 1 4
X X X 2
X X X X

I'm looking for a non recursive (closed form) function mapping linear indices from 0 to 5 to (x,y) achieving

f(0) = (0, 1)
f(1) = (1, 2)
f(2) = (2, 3)
f(3) = (0, 2)
f(4) = (1, 3)
f(5) = (0, 3)

Related for row by row runs:


Solution

  • Thanks to @loopy-walt's observation, we have an answer! Using the result from Linear index upper triangular matrix, a transformation of the result

    (i, j) |-> (j-i-1, j)
    

    Gives the expected outcome.

    Here is a C++ implementation.

    #include<tuple>
    #include<cmath>
    
    // Linear indexing of the upper triangle, row by row
    std::tuple<size_t, size_t> k2ij(size_t n, size_t k){
      size_t i = n - 2 - (size_t)std::floor(std::sqrt(4*n*(n-1) - (8*k) -7)/2.0 - 0.5);
      size_t j = k + i + 1 - n*(n-1)/2 + (n-i)*((n-i)-1)/2;
      return {i,j};
    }
    
    // Linear indexing of the upper triangle, diagonal by diagonal
    std::tuple<size_t, size_t> d2ij(size_t n, size_t d){
      const auto [i, j] = k2ij(n, d);
      return {j-i-1, j}; // Conversion from row by row to diag by diag
    }
    
    #include<iostream>
    #include<set>
    int main(int argc, char** argv) {
    
      size_t n = 4;
      size_t top = n*(n-1)/2;
    
      for(size_t d=0; d<top; ++d){
        const auto [i,j] = d2ij(n, d);
        std::cout << "d2ij(" << n << ", " << d << ") = (" << i << ", " << j << ")" << std::endl;
      }
    
      return 0;
    }
    

    Producing

    d2ij(4, 0) = (0, 1)
    d2ij(4, 1) = (1, 2)
    d2ij(4, 2) = (2, 3)
    d2ij(4, 3) = (0, 2)
    d2ij(4, 4) = (1, 3)
    d2ij(4, 5) = (0, 3)
    

    Note: if someone wishes the form f(d) instead, a lambda can be used to capture the dimension 'n'

    auto f = [n](size_t d){return d2ij(n, d);};
    const auto [i,j] = f(5);
    

    Thanks to everybody that took the time to read and help!