Циклические массивы

78e9ccf0ba707de1a9596f5e9c63fc69

Во многих задачах, связанных с обработкой данных, возникает проблема нехватки памяти для хранения их. 

Например, с датчика непрерывно поступают данные с частотой дискретизации F=1000 Гц, которые сохраняются в массиве. Однако, для анализа данных используется конечное временное окно наблюдения, например, T=10 секунд. Таким образом, при поступлении нового отсчета данных необходимы лишь последние N=T*F=10000 значений.

Подобные задачи возникают при фильтрации сигналов датчиков, построении индикаторов для торговли на биржах, в нейронных сетях.

Возможно несколько вариантов накопления данных для обработки в реальном масштабе времени:

1: Массив неограниченного объема. Недостаток: Избыточные затраты памяти.

2: Два массива по N элементов. При заполнении одного переключаемся на другой.  Недостаток: сложность непрерывной обработки данных. Избыточные затраты памяти.

3: Массив N элементов. При заполнении очищаем. Недостаток: невозможность непрерывной обработки.

4: Массив N элементов. Сдвиг на элемент влево и запись нового элемента вместо N-го. Недостаток: Относительно большие затраты времени на сдвиг массива.

5: Массив N элементов циклический.   Этот метод является самым эффективным из перечисленных. Для его реализации необходимо номер очередного элемента данных преобразовать в индекс элемента массива по модулю N.

Метод такого преобразования зависит от языка программирования. Рассмотрим это на примерах.

Например: Используем язык программирования с возможностью явного указания типа переменных. Если N=256, для хранения индекса применяем тип unsigned char; N=65536 — применяем тип unsigned short, N=4294967296 — применяем тип unsigned long.

При других значениях N, для быстрого преобразования номера отсчета в индекс элемента в массиве эффективно выбирать N как степень двойки, т.е. из ряда 8,16,32,64,128,256,512,1024,2048,4096…65536.

Тогда индекс «I» по номеру элемента» j» можно вычислить через & — бинарное «И» в виде:  i=j&(N-1).

Если в языке программирования нет бинарных операций, то индекс можно вычислить через целочисленное деление i=j%N.

Целочисленное деление исполняется дольше, чем бинарное «И».

В любом случае, применение циклического массива существенно уменьшает затраты времени и памяти при обработке больших объемов данных в реальном времени.

Кроме того, циклические массивы удобно использовать для организации стеков и очередей.

Тот факт, что величина индекса массива не может превышать размер массива полностью исключает ошибки из-за выхода за пределы области расположения массива.

Вариант теста на Lua:

local size=100000;  local N=1024; local M=N-1; local t={}; for i=0,N do t[i]=0 end
start=os.clock() for i = 1, size do t[i]=i; end time = 1000000*(os.clock()- start)/size
print("1.Неогр.объем(мкс):", time)
local function shift(t) for j=2,N do t[j-1]=t[j] end end
start=os.clock() for i = 1, size do shift(t) t[N]=i; end time = 1000000*(os.clock()- start)/size
print("4.Сдвиг влево(мкс):", time)
start=os.clock() for i = 1, size do t[i&M]=i; end time = 1000000*(os.clock()- start)/size
print("5.Цикл., бинарное'И'(мкс):", time)
start=os.clock() for i = 1, size do t[i%N]=i; end time = 1000000*(os.clock()- start)/size
print("5.Цикл., целоч.деление(мкс):", time)

 результат теста для N=1024:

1.Неогр.объем (мкс): 0.02
4.Сдвиг влево (мкс): 13.07
5.Цикл., бинарное'И'(мкс): 0.01
5.Цикл., целоч.деление (мкс): 0.02

© Habrahabr.ru