Есть два случая: статические данные или динамические (переменные) данные
1. Статические данные
Для статических данных это хорошо известная проблема. Сначала вычисляем «таблицу сумм» (массив из n + 1 элементов):
st[0] = 0;
for (int i=0,n=x.size(); x<n; x++)
st[i] = st[i-1] + x[i];
затем, чтобы вычислить сумму элементов между a
и b
, вам просто нужно сделать
sum = st[b] - st[a];
Алгоритм также может использоваться в более высоких измерениях. Например, если вам нужно вычислить сумму всех значений между (x0, y0) и (x1, y1), вы можете просто сделать
sum = st[y0][x0] + st[y1][x1] - st[y0][x1] - st[y1][x0];
где st[y][x]
- сумма всех элементов выше и слева от (x, y)
.
Для вычисления таблицы сумм используется операция O (n) (где n - количество элементов, например, rows*columns
для двумерной матрицы), но для очень большие наборы данных (например, изображения) можно написать оптимальную параллельную версию , которая может работать в O (н / м) , где м - это число доступные процессоры. Это то, что я нашел довольно удивительным ...
Чтобы выполнить простое (однопоточное) вычисление таблицы сумм в 2d случае:
for (int y=0; y<h; y++)
for (int x=0; x<w; x++)
st[y+1][x+1] = st[y+1][x] + st[y][x+1] - st[y][x] + value[y][x];
2.Динамические данные
Вместо динамических данных вы можете использовать то, что можно назвать подходом "многократного разрешения":
void addDelta(std::vector< std::vector< int > >& data,
int index, int delta)
{
for (int level=0,n=data.size(); level<n; level++)
{
data[level][index] += delta;
index >>= 1;
}
}
int sumToIndex(std::vector< std::vector< int > >& data,
int index)
{
int result = 0;
for (int level=0,n=data.size(); level<n; level++)
{
if (index & 1) result += data[level][index-1];
index >>= 1;
}
return result;
}
int sumRange(std::vector< std::vector< int > >& data,
int a, int b)
{
return sumToIndex(data, b) - sumToIndex(data, a);
}
В основном на каждом «уровне» ячейка содержит суммы двух ячеек следующих более мелких уровней. Когда вы добавляете данные на самый низкий (с более высоким разрешением) уровень, вы также должны добавить их на более высокие уровни (это то, что делает addDelta
).
Чтобы вычислить сумму всех значений от 0 до x
, вы можете использовать более высокие уровни для экономии вычислений ... см. Следующий рисунок:
Наконец, чтобы получить сумму от a
до b
, вы просто вычисляете разницу между этими двумя суммами, начиная с 0.