Вы, вероятно, возненавидите это, но для следующей (небольшой для отображения) строки матрицы:
1 0 0 5 9 0 0 0
вы можете иметь битовый массив (в данном случае 8 битов), где для каждого бита был установлен или очищен для отражения, если в этой позиции был ненулевой или ноль в этой позиции.
Тогда вы просто сохраняете ненулевые числа в обычном массиве, упакованном вместе, как:
{ 1, 5, 9 }
и двоичные флаги
0x98 // двоичный код 1001 1000
И чтобы выполнить итерацию по строке матрицы, вы просто использовали бы битовые операции для цикла по массиву битов:
while (! /* not at the end of the bit array */ ) {
f = get_next_from_bit_array(); // This is just bitwise shift and bitwise &
if (!f) {
val = 0;
} else {
val = compressed_row[i];
i++;
}
do_action(val);
}
Мой код здесь только для демонстрации и не очень C ++, но я надеюсь, что вы поняли идею.
Использование битового массива позволит вам исследовать гораздо меньшую область памяти для разреженных строк, что означает меньший доступ к памяти и лучшую локальность кэша.
Если матрицы, с которыми вы работаете, чрезвычайно разрежены, вы можете расширить это и для другого измерения (имея разреженный массив строк), но вероятность того, что у вас будут целые строки пустыми, вероятно, мала.