5

I have an integer matrix that should act like a buffer:

x = {{0, 0, 0, 0, 0}, {1, 1, 1, 1, 1}, {2, 2, 2, 2, 2}};

Now if I add a new row {3, 3, 3, 3, 3}, the new matrix should look like:

x = {{1, 1, 1, 1, 1}, {2, 2, 2, 2, 2}, {3, 3, 3, 3, 3}};

Is there a clever way of doing this without copying all elements around?

fi_34k7
  • 53
  • 3
  • 2
    Multiple answers below are technically correct, but they all involve different tradeoffs. Can you expand on your expected use a bit? How large will these matrices be? How often do expect to add a row, compared to the number of times you will access data from the matrices? Will you access individual elements of the matrix, or will it only be read as an entire entity from beginning to end? Do you want to be able to free parts of the matrix from time to time? If so, just from the end, or from the beginning, or from an arbitrary row? – Philip Starhill Nov 02 '10 at 14:04
  • The matrix isn't large (like 100 elements in total). I'll always access the whole matrix, the "old" row can go away (fifo-queue behavior), updates happen very often. – fi_34k7 Nov 02 '10 at 14:08
  • 2
    In that case the modulo approach proposed by @ruslik is probably your best bet. Just allocate an array that can handle the maximum size, maintain a pointer to the current head, and wrap around the end of the array when you run out of room. – Philip Starhill Nov 02 '10 at 14:15

6 Answers6

8

If your matrix is defined as an int ** and you separately allocate each row, then you would only have to swap the row pointers.

mikerobi
  • 20,527
  • 5
  • 46
  • 42
  • That sounds good. I really need to learn to think more pointer-oriented. :) – fi_34k7 Nov 02 '10 at 14:03
  • Only problem is that unlike the linked list solution proposed elsewhere, here you have to pre-allocate pointers to the max number of rows you will have. Otherwise, it is a more efficient way than linked list. **EDIT** I just noticed that in your question you remain with 3 rows after adding the new row. If this is representative, then you are just fine with @mikerobi's answer. You just need to manage your pointers once rows are swapped. – ysap Nov 02 '10 at 14:05
  • @ysap, the occasional O(n) operation to increase the number of rows, will usually be more efficient that a frequent O(n), to access a value. – mikerobi Nov 02 '10 at 14:14
  • I generally agree, however at the end of the day, it boils down to analyzing the exact behaviour of the application. – ysap Nov 02 '10 at 14:28
6

How about modulo operation?

If you access the elements as matrix[x + SZ * y] you could change it to:

matrix[x + SZ * ((y + FIRST_ROW) % SZ)] .

In this way to implement this shift you just put the new line {3, 3, 3..} where line {0, 0, 0} was, and increment the FIRST_ROW counter to point to the new starting row.

ruslik
  • 14,714
  • 1
  • 39
  • 40
  • +1 based on the extra specification in the comments to the original question, this is probably the alternative that will give the best performance. – Philip Starhill Nov 02 '10 at 14:16
  • I now implemented it and it works fine. Thanks again, and thanks guys for all the other answers! – fi_34k7 Nov 02 '10 at 14:33
1

Use a linked list.

struct node
{
    int row[5];
    struct node *next;
};

Appending a row is as simple as walking the list to the end, then replacing the NULL next pointer with a new node (whose next pointer is NULL).

nmichaels
  • 49,466
  • 12
  • 107
  • 135
  • This works for the algorithm, but won't it give extremely slow results when you actually do things with the matrix? – alternative Nov 02 '10 at 13:57
  • That depends on what you're doing with it. If you aren't extending your matrix all the time, you're probably better off soaking up the occasional memcpy. – nmichaels Nov 02 '10 at 13:59
  • Note that in the question the new row is not "added" to the matrix, but rather replaces the 1st row. So, if you choose the linked list solution, make sure you nullify your first item in the list. – ysap Nov 02 '10 at 14:09
  • Yeah, I didn't notice that when I wrote it. If the matrix doesn't need to grow, @mikerobi's answer is a much better way to go. – nmichaels Nov 02 '10 at 14:11
1

Can you increment x so that it points to the second row, then free the first row? Obviously, you would need to allocate a row at a time and this would not guarantee that the matrix is contiguous. If you require that, you could allocate one big block of memory to hold your matrix and then clobber the unused parts when you reach the end.

Matt K
  • 13,370
  • 2
  • 32
  • 51
1

If you use an array of pointers to arrays (rather than an ordinary two-dimensional array), you can copy just the pointers to rows instead of copying all elements.

And if you're okay with overallocating the array of pointers, you could maybe add a new pointer to the end and advance the pointer to the "start" of the array. But this wouldn't be a good idea if you potentially want to do this sort of shift many times. And of course you'd want to make sure you have the original pointer somewhere so you can properly free() your resources.

aschepler
  • 70,891
  • 9
  • 107
  • 161
1

Lazy to write code example - you can use modulo arithmetics to address the rows. When pushing a new row, simply increase a starting offset variable, add the matrix height and modulo the result by matrix height. This way you get a circular matrix with no need to copy the whole matrix and keeping the matrix array compact.

Karel Petranek
  • 15,005
  • 4
  • 44
  • 68