For an n×n matrix, the (i, j)-th item of the upper triangle is the i×(2×n-i+1)/2+j-i-th element of the matrix.
We can also do the math in reverse and obtain the (i, j) element for the k-th element with:
i = ⌊(-√((2n+1)2-8k)+2n+1)/2⌋ and j = k+i-i×(2×n-i+1)/2
So for example:
from math import floor, sqrt
def coor_to_idx(n, i, j):
return i*(2*n-i+1)//2+j-i
def idx_to_coor(n, k):
i = floor((-sqrt((2*n+1)*(2*n+1)-8*k)+2*n+1)/2)
j = k + i - i*(2*n-i+1)//2
return i, j
For example:
>>> [idx_to_coor(4, i) for i in range(10)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]
>>> [coor_to_idx(4, i, j) for i in range(4) for j in range(i, 4)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Given the numbers are not huge (well if these are huge, calculations are no longer done in constant time), we can thus calculate the k-th coordinate in O(1), for example:
>>> idx_to_coor(1234567, 123456789)
(100, 5139)
which is equivalent to obtaining it through enumeration:
>>> next(islice(((i, j) for i in range(1234567) for j in range(i, 1234567)), 123456789, None))
(100, 5139)
Here converting indices to a coordinate can also have, for large numbers, some rounding errors due to floating point imprecision.