![]() |
[C#] Эффективное хранение многомерных массивов
Проблематика:
Для хранения многомерных данных в C# предусмотрены статические массивы Multidimensional arrays и Jagged arrays. Их применение подразумевает, что размерность (число измерений) массивов известна заранее, на этапе компиляции. Если же требуется создание многомерного массива с неизвестным заранее числом измерений, то вместо статических массивов, как правило, используются сложные вложенные списки, типа List<ListOrValue>. Однако их применение неэффективно по ряду причин, среди которых: Большие накладные расходы памяти на хранение объектов, малая скорость создания массива, из-за необходимости создания большого числа объектов, невысокая скорость доступа к данным. В заметке показывается более эффективная структура для создания и хранения многомерных массивов с неизвестной заранее размерностью. Решение: Для хранения многомерных данных, используется одномерный массив. При хранении данных в одном большом одномерном массиве, минимизируются накладные расходы на память, на время создания массива, на время доступа к отдельному элементу. Индекс одномерного массива будем называть сигнатурой. Для преобразования индексов элемента исходного многомерного массива, в индекс одномерного массива используется специальная формула. Основная идея, лежащая в вычислении сигнатуры проста: будем пересчитывать все элементы многомерного массива по порядку, при этом сначала будем считать элементы по первой размерности, потом по второй и так далее. Например, для двумерного массива 3x3, будем считать элементы следующим образом: 012 345 678 Номер элемента в этой схеме как раз и является сигнатурой. Если получена сигнатура, то нетрудно «выпрямить» многомерный массив в одномерный. Одномерный массив просто хранит все элементы многомерного массива по индексу, совпадающему с номером (сигнатурой) элемента в многомерном массиве. Рассмотрим метод определения сигнатуры для произвольных многомерных массивов. Допустим, у нас есть двумерный массив NxM. Из приведенной выше схемы нетрудно видеть, что сигнатура для произвольного элемента [i, j] вычисляется следующим образом: Sig(i, j) = i+j*N Рассмотрим сигнатуру, для элемента [i, j, k] трехмерного массива, размерностью N*M*K: Sig(i, j) = i+j*N+k*N*M Теперь выведем общую формулу сигнатуры для массива размерностью N1*N2*....: Sig(i1, i2, i3 ...) = i1 + i2*N1 + i3*N1*N2 + i4*N1*N2*N3 … Коэффициенты этой формулы, стоящие при индексах: 1, N1, N1*N2, N1*N2*N3 будем называть шагами. Каждая размерность имеет свой шаг, вычиcляемый по формуле: Step(i) = 1*N1*N2*..*N(i-1) Таким образом, сигнатура есть сумма индексов, умноженных на соответствующие шаги. Сигнатура, вычисленная по такой формуле имеет несколько интересных свойств. Свойство1: Если известна сигнатура S для некоторого элемента, то можно легко вычислить сигнатуру для следующего элемента по некоторой размерности i: Snext = S+Step(i) Свойство2: Если известна сигнатура S для некоторого элемента, то можно получить исходный индекс этого элемента в произвольной размерности i: Index(i) = (S/Step(i))%Ni (где / - целочисленное деление, % - деление по модулю) Реализация: Ниже приведена реализация многомерного массива основанного на одномерном массиве для хранения данных и методе сигнатуры, для преобразования многомерных индексов в одномерный. Код:
/// <summary>Код:
//создаем пятимерный массив, с размером 10 по каждой координатеТестировались пятимерные массивы, с размерностью 10x10x10x10x10. По результатам тестирования, время создания многомерного массива MultiDimensionalArray<int> на 6% дольше создания int[,,,,]. Время доступа по сигнатуре – практически совпадает с доступом к int[,,,,] Время доступа по индексам – в 8 раз медленнее чем int[,,,,] Размер занимаемой памяти – равен int[,,,,] |
Эм, а смысл использовать это, а не обыкновенный массив, если мы не можем сделать так(псевдокодом):
m[2,3,4].append([[45],[3,4]]) Т.е. по сути у нас получается обыкновенный n-мерный массив, только с другой системой хранения. |
Цитата:
Цитата:
То есть например так: Код:
Console.ReadLine("Введите число измерений массива:")Немного поправил статью, что бы было понятно, что речь идет о числе измерений, а не о числе элментов в каждом измерении. |
В данной заметке ценность для кого-то представляет реализация.
Ведь саму процедуру придумал Кантор больше ста лет назад. ;) Правда он строил изоморфизм из произведения счётных, а не конечных множеств в счётное множество... Цитата:
Цитата:
|
все хорошо .... алгоритм нужный , и пусть физики и математики нет для 5-го измерения .... ! :D
для Си шарпа сойдет ... а для серьезных вещей умножение и деление меняется на сдвиги и в размерах(размерности) дополнение в большую сторону до степени 2-ки(для тех самых сдвигоф)! :p (если заводили речь о скорости) |
Цитата:
|
Цитата:
Цитата:
|
Цитата:
Цитата:
НО если ты предложишь реализацию с более эффективным доступом, я буду только рад :) |
Цитата:
|
Цитата:
|
| Время: 11:12 |