8

Suppose an upper triangular matrix of integers is given. What's the best way of storing this in Java? The naive 2d int array is obviously not efficient. The solution I came up with was moved to the answers section.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
user3639557
  • 4,791
  • 6
  • 30
  • 55
  • "Best" in which way? Flexibility? Use one of the existing matrix libraries. Performance? Use a plain 1D array. (If performance is really critical and the matrix is "small", you may even consider creating a (rows*cols) array and leaving the lower left of it empty). In any case, I'd stay away from classes that are not intended to be used as a matrix (like "Tables"), or from nested data structures like lists-in-lists. Also note that for the last bit of performance (even when using an array), the *access pattern* is crucial as well, and whether you use row-major or column-major storage – Marco13 Oct 03 '14 at 23:43
  • You can answer your own question: move your solution to an answer (type it in the "Your Answer" box in this page). – Peter O. Oct 04 '14 at 00:09
  • @PeterO. Thanks. Just moved it to the answer section. – user3639557 Oct 04 '14 at 02:09

4 Answers4

4

If you want to save memory, your solution looks great - it's called a packed storage matrix. Column by column, top down, your array would look like this: 1 2 6 3 7 8 4 1 9 5

I would suggest a simpler calculation of your indices, based on the sum formula (n² + n) / 2 (row and column is zero based).

list_index = (column^2 + column) / 2 + row;

An implementation could look like the following:

public class TriangularMatrix {
    private final int[] list;

    public TriangularMatrix(int size) {
        list = new int[sumFormula(size)];
    }

    public int set(int row, int column, int value) {
        validateArguments(row, column);

        int listIndex = getListIndex(row, column);
        int oldValue = list[listIndex];
        list[listIndex] = value;

        return oldValue;
    }

    public int get(int row, int column) {
        validateArguments(row, column);

        return list[getListIndex(row, column)];
    }

    private void validateArguments(int row, int column) {
        if (row > column) {
            throw new IllegalArgumentException("Row (" + row + " given) has to be smaller or equal than column (" + column + " given)!");
        }
    }

    private int getListIndex(int row, int column) {
        return sumFormula(column) + row;
    }

    private int sumFormula(int i) {
        return (i*i + i) / 2;
    }
}

There is another question on SO discussing the (negative) performance impact, although it's about Fortran.

Gauthier Boaglio
  • 10,054
  • 5
  • 48
  • 85
stuXnet
  • 4,309
  • 3
  • 22
  • 31
  • I worked out a solution for the upper triangular matrix case which was what I needed. please have a look and let me know hat you think. – user3639557 Oct 03 '14 at 11:56
  • @user3639557 I changed my answer to match your question - my first version simply wasn't efficient for triangular matrices. – stuXnet Oct 03 '14 at 12:12
2

If the matrix is always diagonal, I would use:

List<List<Integer>> matrix = ...

If it is an sparse matrix, I would use maps:

Map<Map<Integer>> = ...

In this second case, you may need to wrap the map in a class with get and set operations in order to manage access to new rows and columns.

All this, however depends on your needs, your memory limites and matrix size.

  • I worked out another solution. please have a look and let me know hat you think. – user3639557 Oct 03 '14 at 11:52
  • @user3639557 If yout matrix is always triangular then your solution is right and optimal concerning memory usage. I recommend you to implement its wrapping class with its accesors methods providing a direct access to its elements based on a (i row,j column) index. – Pablo Francisco Pérez Hidalgo Oct 03 '14 at 12:18
2

What about Guava's Table? It is implemented using HashMaps or TreeMaps (as well as 2D array if required), but it offers much nicer API than defining a Map<Integer, Map<Integer, V>>.

Natix
  • 14,017
  • 7
  • 54
  • 69
2

I think I found the solution. Here is my solution: Assume you have a 4X4 upper triangular matrix M.

1 2 3 4
0 6 7 1
0 0 8 9
0 0 0 5

If you can map every element of M in a 1d array, that's the best solution. All you need to know is to know which [row,col] of matrix corresponds to which element of your 1d array. Here is how you do the magic:

start_index=((col_index-1)+1)+((col_index-2)+1)+...+1
end_index=start_index + col_index

For example: if I want to find where are the elements on the 3rd column of the matrix, in the array:

start_index=((3-1)+1)+((3-2)+1)+((3-3)+1)=6
end_index=6+3=9

So, all I need to do is to start at index 6 of my array, and read all the elements till index 9 (including 9th element). Following this procedure, then you can store and retrieve all the cells of the nXn matrix in (n + n^2)/2 space.

user3639557
  • 4,791
  • 6
  • 30
  • 55