Хеш-сумма
- 1 year ago
- 0
- 0
В информатике префиксная сумма , кумулятивная сумма , инклюзивное сканирование или просто сканирование последовательности чисел x0, x1, x2, … называется последовательность чисел y0, y1, y2, …, являющаяся префиксной суммой от входной последовательности:
Например, префиксными суммами натуральных чисел являются треугольные числа :
входные числа | 1 | 2 | 3 | 4 | 5 | 6 | … |
---|---|---|---|---|---|---|---|
префиксная сумма | 1 | 3 | 6 | 10 | 15 | 21 | … |
Префиксные суммы тривиальны для вычисления в последовательных моделях вычислений, путем применения формулы y i = y i − 1 + x i для вычисления каждого выходного значения в порядке последовательности. Тем не менее, несмотря на простоту вычислений, префиксные суммы являются полезным примитивом в некоторых алгоритмах, таких как сортировка подсчетом , и они составляют основу функции сканирования более высокого порядка в функциональных языках программирования . Суммы префиксов также широко изучались в параллельных алгоритмах , и как тестовая задача, которую нужно решить, и как полезный примитив для использования в качестве подпрограммы в других параллельных алгоритмах.
Теоретически, префиксная сумма требует только , что делает ее полезной во многих алгоритмах: от вычисления точек до обработки строк.
Математически, операция взятия префиксных сумм может быть обобщена от конечных до бесконечных последовательностей; в этом смысле префиксная сумма известна как частичная сумма ряда . Префиксное суммирование или частичное суммирование образует линейное отображение на векторные пространства конечных или бесконечных последовательностей; их обратные операторы являются конечными разностями.
В терминах функционального программирования префиксная сумма может быть обобщена на любую двоичную операцию (не только операцию сложения ); функция более высокого порядка, полученная в результате этого обобщения, называется сканированием , и она тесно связана с операцией свертки . Обе операции сканирования и свертки применяют данную двоичную операцию к одной и той же последовательности значений, но отличаются тем, что сканирование возвращает всю последовательность результатов двоичной операции, тогда как свертка возвращает только конечный результат. Например, последовательность факториальных чисел может быть сгенерирована путем сканирования натуральных чисел с использованием умножения вместо сложения:
входные числа | 1 | 2 | 3 | 4 | 5 | 6 | … |
---|---|---|---|---|---|---|---|
префиксные значения | 1 | 2 | 6 | 24 | 120 | 720 | … |
Языковые и библиотечные реализации сканирования могут быть инклюзивными или эксклюзивными . Инклюзивное сканирование включает в себя ввод x i при вычислении вывода y i ( ), в то время как эксклюзивное сканирование его не включает ( ). В последнем случае реализации либо оставляют y 0 неопределенным, либо принимают особое значение « x -1 », с помощью которого можно начать сканирование. Эксклюзивные сканы являются более общими в том смысле, что инклюзивное сканирование всегда может быть реализовано в терминах эксклюзивного сканирования (путем последующего комбинирования x i с y i ), но эксклюзивное сканирование не всегда может быть реализовано в терминах инклюзивного сканирования, как в случае максимума префиксной суммы.
В следующей таблице приведены примеры функций инклюзивного и эксклюзивного сканирования, предоставляемых несколькими языками программирования и библиотеками:
Языки/библиотеки | Инклюзивное сканирование | Эксклюзивное сканирование |
---|---|---|
Haskell |
scanl1
|
scanl
|
MPI |
MPI_Scan
|
MPI_Exscan
|
C++ |
std::inclusive_scan
|
std::exclusive_scan
|
Scala |
scan
|
|
Rust | от 6 июня 2021 на Wayback Machine |
Существуют два ключевых алгоритма для параллельного вычисления префиксной суммы. Первый метод подразумевает меньшую глубину и большую предрасположенность к распараллеливанию , но такой способ недостаточно эффективен. Второй вариант более эффективен, но требует двойную глубину и предлагает меньше вариантов для распараллеливания. Ниже представлены оба алгоритма.
and представляют следующий алгоритм параллельной суммы префиксов:
Обозначение означает значение j -го элемента массива x на шаге i .
Если учитывать n процессоров для выполнения каждой итерации внутреннего цикла за постоянное время, то алгоритм выполняется за O (log n ) времени.
Эффективный параллельный алгоритм вычисления префиксной суммы может быть реализован следующим способом:
Если входная последовательность имеет размер n , то рекурсия продолжается до глубины O (log n ) , которая также ограничена временем параллельного выполнения этого алгоритма. Количество операций алгоритма равно O ( n ) , и их можно реализовать на абстрактной параллельной ЭВМ с общей памятью (PRAM) с количеством процессоров O ( n /log n ) без какого-либо асимптотического замедления путем назначения нескольких индексов каждому процессору в вариантах алгоритма, для которых больше элементов, чем процессоров.
Каждый из предыдущих алгоритмов выполняется за O (log n ) . Тем не менее, первый делает ровно log 2 n шагов, а второй требует 2 log 2 n − 2 шага. Для приведенных примеров с 16 входами Алгоритм 1 является 12-параллельным (49 единиц работы, разделенных на 4), а Алгоритм 2 — только 4-параллельным (26 единиц работы, разделенных на 6). Однако Алгоритм 2 эффективен с точки зрения работы он выполняет только постоянный коэффициент (2) объема работы, требуемого последовательным алгоритмом, а Алгоритм 1 неэффективен он выполняет асимптотически больше работы (логарифмический коэффициент), чем требуется последовательно. Следовательно, Алгоритм 1 предпочтительнее, когда возможна реализация большого количества параллельных процессов, иначе имеет преимущество Алгоритм 2.
Параллельные алгоритмы для префиксных сумм часто могут быть обобщены на другие ассоциативные двоичные операции сканирования, , и они также могут быть эффективно вычислены на современном параллельном оборудовании, таком как GPU (Графический процессор). Идея создания в аппаратном обеспечении функционального блока, предназначенного для вычисления многопараметрической префиксной суммы, была запатентована .
Многие параллельные реализации используют двухэтапную процедуру, в которой частичная префиксная сумма вычисляются в первом этапе для каждого блока обработки; префиксная сумма этих частичных сумм затем вычисляется и передается обратно в блоки обработки для второго этапа, используя теперь известный префикс в качестве начального значения. Асимптотически этот метод занимает примерно две операции чтения и одну операцию записи для каждого элемента.
Реализация алгоритма параллельного вычисления префиксной суммы, как и другие параллельные алгоритмы, должна учитывать архитектуру распараллеливания платформы . Существует множество алгоритмов, которые адаптированы для платформ, работающих с разделяемой памятью , а также алгоритмы, которые хорошо подходят для платформ, использующих , пользуясь при этом обменом сообщений в качестве единственной формы межпроцессного взаимодействия.
Следующий алгоритм предполагает модель машины с разделяемой памятью ; все обрабатывающие элементы PE (от англ. processing elements) имеют доступ к одной и той же памяти. Вариант этого алгоритма реализован в многоядерной стандартной библиотеке шаблонов (MCSTL) , параллельной реализации стандартной библиотеки шаблонов C++ , которая предоставляет адаптированные версии для параллельных вычислений различных алгоритмов.
Чтобы одновременно вычислить префиксную сумму из элементов данных с элементами обработки, данные делятся на блоков, каждый из которых содержит элементов (для простоты мы будем считать, что делится на ). Обратите внимание, что, хотя алгоритм делит данные на блоков, параллельно работают только обрабатывающих элементов.
В первом цикле каждый PE вычисляет локальную префиксную сумму для своего блока. Последний блок вычислять не нужно, так как эти префиксные суммы рассчитываются только как смещения префиксных сумм последующих блоков, и последний блок по определению не является подходящим.
Смещенные , которые хранятся в последней позиции каждого блока, накапливаются в собственной префиксной сумме и сохраняются в последующих позициях. Если мало, то последовательный алгоритм выполняется достаточно быстро, для больших этот шаг может выполняться параллельно.
Теперь перейдем ко второму циклу. На этот раз первый блок обрабатывать не нужно, так как ему не нужно учитывать смещение предыдущего блока. Однако теперь включается последний блок, и суммы префиксов для каждого блока рассчитываются с учетом смещений блоков суммы префиксов, рассчитанных в предыдущем цикле.
function prefix_sum(elements) {
n := size(elements)
p := number of processing elements
prefix_sum := [0...0] of size n
do parallel i = 0 to p-1 {
// i := индекс текущего PE
from j = i * n / (p+1) to (i+1) * n / (p+1) - 1 do {
// Здесь хранится префиксная сумма локальных блоков
store_prefix_sum_with_offset_in(elements, 0, prefix_sum)
}
}
x = 0
for i = 1 to p {
x += prefix_sum[i * n / (p+1) - 1] // Построение префиксной суммы по первым p блокам
prefix_sum[i * n / (p+1)] = x // Сохрание результатов для использования в качестве смещений во втором цикле
}
do parallel i = 1 to p {
// i := индекс текущего PE
from j = i * n / (p+1) to (i+1) * n / (p+1) - 1 do {
offset := prefix_sum[i * n / (p+1)]
// Рассчитывание префиксной суммы в качестве смещения суммы предыдущих блоков
store_prefix_sum_with_offset_in(elements, offset, prefix_sum)
}
}
return prefix_sum
}
Алгоритм префиксной суммы гиперкуба хорошо адаптирован для платформ с и использует обмен сообщениями между элементами обработки. Предполагается, что в алгоритме участвуют PE , равного числу углов в -мерном гиперкубе .
На протяжении всего алгоритма каждый PE рассматривается как угол в гипотетическом гиперкубе со знанием общей префиксной суммы , а также префиксной суммы всех элементов до самого себя (в соответствии с упорядоченными индексами среди PE), каждая в свой гиперкуб.
В -мерном гиперкубе с PE по углам алгоритм должен повторяться раз, чтобы нульмерных гиперкуба были объединены в один -мерный гиперкуб. Предполагая модель дуплексной связи , в которой двух смежных PE в разных гиперкубах можно обменивать в обоих направлениях за один шаг связи, это означает, что запусков связи.
i := Index of own processor element (PE)
m := prefix sum of local elements of this PE
d := number of dimensions of the hyper cube
x = m; // Инвариант: префиксная сумма PE в текущем вложенном кубе
σ = m; // Инвариант: префиксная сумма всех элементов в текущем вложенном кубе
for (k=0; k <= d-1; k++){
y = σ @ PE(i xor 2^k) // Получение общей суммы префикса противоположного вложенного куба по измерению k
σ = σ + y // Суммирование префиксных сумм обоих вложенных кубов
if (i & 2^k){
x = x + y // Суммирование префиксной суммы из другого вложенного куба только в том случае, если этот PE является индексом с более высоким индексом
}
}
Конвейерный алгоритм двоичного дерева — это еще один алгоритм для платформ с распределенной памятью, который особенно хорошо подходит для сообщений большого размера.
Как и алгоритм гиперкуба, он предполагает особую коммуникационную структуру. PE гипотетически расположены в двоичном дереве (например, в дереве Фибоначчи) с инфиксной нумерацией в соответствии с их индексом в PE. Связь в таком дереве всегда происходит между родительским и дочерним узлами.
Инфиксная нумерация гарантирует, что для любого данного PE j индексы всех узлов достижимы его левым поддеревом меньше , а индексы всех узлов в правом поддереве больше . Индекс родителя больше, чем любой из индексов в поддереве PE j , если PE j — левый потомок, и меньше, если PE j — правый потомок. Это допускает следующие рассуждения:
Обратите внимание на различие между поддерево-локальной и общей префиксной суммой. Точки два, три и четыре могут привести к тому, что они сформируют круговую зависимость, но это не так. PE нижних уровней могут потребовать, чтобы общая префиксная сумма PE верхних уровней вычисляла их общую префиксную сумму, но PE верхних уровней требуют только локальную префиксную сумму поддерева для вычисления их общей префиксной суммы. Корневой узел как узел самого высокого уровня требует только локальную префиксную сумму своего левого поддерева для вычисления своей собственной префиксной суммы. Каждый PE на пути от PE 0 к корневому PE требует только локальную префиксную сумму своего левого поддерева для вычисления своей собственной префиксной суммы, в то время как каждому узлу на пути от PE p-1 (последнего PE) до PE root требуется общая префиксная сумма его родителя, чтобы вычислить его собственную общую префиксную сумму.
Это приводит к двухфазному алгоритму:
Восходящая фаза
Распространение локальной префиксной суммы поддерева
его родителю для каждого PE
j
.
Нисходящая фаза
Распространение эксклюзивной (эксклюзивный PE
j
, а также PE в его левом поддереве) суммарной префиксной суммы
всех PE с более низкими индексами, которые не включены в адресуемое поддерево PE
j
, на PE более низких уровней левого дочернего поддерева PE
j
. Распространение инклюзивной префиксной суммы ⊕ [0…j] на правое дочернее поддерево PE
j
.
Стоит отметить, что алгоритм выполняется на каждом PE и PE будут ждать пока все пакеты от всех дочерних/родительских элементов не будут получены.
k := number of packets in a message m of a PE
m @ {left, right, parent, this} := // Сообщения в разные PE
x = m @ this
// Восходящая фаза- рассчитывание локальной префиксной суммы поддерева
for j=0 to k-1: // Конвейерная передача: для каждого пакета сообщения
if hasLeftChild:
blocking receive m[j] @ left // Замена локального m[j] полученным m[j]
// Совокупная инклюзивная локальная префиксная сумма из PE с более низкими индексами
x[j] = m[j] ⨁ x[j]
if hasRightChild:
blocking receive m[j] @ right
// Мы не объединяем m[j] в локальную префиксную сумму, так как правильные потомки - это PE с более высокими индексами
send x[j] ⨁ m[j] to parent
else:
send x[j] to parent
// Нисходящая фаза
for j=0 to k-1:
m[j] @ this = 0
if hasParent:
blocking receive m[j] @ parent
// Для левого ребенка m[j] - родительская эксклюзивная префиксная сумма, для правого ребенка - инклюзивная префиксная сумма
x[j] = m[j] ⨁ x[j]
send m[j] to left // Общая префиксная сумма всех PE, меньших, чем этот или любой PE в левом поддереве
send x[j] to right // Общая префиксная сумма всех PE меньше или равных этому PE
может быть применена в случае, когда сообщение длины может быть разделено на частей и оператор ⨁ может быть применен к каждой такой части отдельно.
Если алгоритм используется без конвейерной передачи, то в дереве в любой момент времени работают только два уровня(отправляющий PE и принимающий PE), в то время как остальные РЕ ожидают. Если используется бинарное сбалансированное дерево обрабатывающих элементов, содержащее уровней, то длина пути от до , что соответствует максимальному числу непараллельных операций связи восходящей фазы. Аналогично связи по нисходящему пути также ограничены этим же значением. Считая время запуска связи и байтовое время передачи получим, что фазы ограничены по времени в неконвейерной передаче. При делении на частей, каждый из которых имеет элементов и отправляет их независимо друг от друга, первая часть будет требовать для прохождения к как часть локальной префиксной суммы и такое время будет действительно для последней части, если .
В остальных частях все РЕ могут работать параллельно и каждая третья операция взаимодействия(получение слева, получения справа, отправка родителю) отправляет пакет на следующий уровень, поэтому одна фаза может быть проделана за операций взаимодействия и обе фазы вместе требуют , что является очень хорошим показателем для сообщений длины .
Алгоритм может быть дополнительно оптимизирован путем использования полнодуплексной или телекоммуникационной модели связи и перекрытия восходящей и нисходящей фазы.
При необходимости динамического обновления набора данных, ее можно хранить в дереве Фенвика . Такая структура данных позволяет за логарифмическое время не только находить любое значение префиксной суммы, но и изменять любое значение элемента в массиве. . Так как в 1982 термин префиксной суммы еще не был достаточно распространен, появилась работа , где была представлена структура данных, называемая Деревом частичных сумм (5.1), которая заменяла название дерева Фенвика.
Для вычисления сумм произвольных прямоугольных подмассивов на многомерных массивах, представляется структурой данных, построенной на префиксных суммах. Такая таблица может быть полезна в задачах .
Сортировка подсчетом — это алгоритм целочисленной сортировки , который использует префиксную сумму гистограммы ключевых частот для вычисления положения каждого ключа в отсортированном выходном массиве. Он работает за линейное время для целочисленных ключей, которые меньше числа элементов, и часто используется как часть поразрядной сортировки , быстрого алгоритма сортировки целых чисел, которые менее ограничены по величине.
, задача преобразования связного списка в массив , содержащий ту же последовательность элементов, может рассматриваться как префиксные суммы на последовательности единиц, а затем сопоставление каждого элемента с позицией в массиве, полученной из значения его префиксной суммы. Много важных задан на деревьях могут быть решены на параллельных алгоритмах путем совмещения ранжирования списков, префиксных сумм и обходов Эйлера .
Параллельное вычисление префиксных сумм также используется в разработке двоичных сумматоров , логических схем, которые могут складывать два n -битных двоичных числа. В этом случае, последовательность битов переноса сложения может быть представлена как операция сканирования на последовательности пар входных битов с использованием функции большинства для объединения данного переноса с этими двумя битами. Каждый бит выходного числа может быть найден как эксклюзивный дизъюнктор двух входных битов с соответствующим переносом битов. Таким образом можно сконструировать сумматор, который использует O ( n ) логических элементов и O (log n ) временных шагов.
В модели вычислительных машин с параллельным произвольным доступом, префиксные суммы могут использоваться для моделирования параллельных алгоритмов, которые предполагают возможность для нескольких процессоров одновременно обращаться к одной и той же ячейке памяти на параллельных машинах, которые запрещают одновременный доступ. Посредством сети сортировки , набор параллельных запросов на доступ к памяти может быть упорядочен в последовательность, так что доступ к одной и той же ячейке является непрерывным в пределах последовательности. Затем можно использовать операции сканирования, чтобы определить, какой из обращений записи в запрошенные ячейки завершился успешно, и распределить результаты операций чтения из памяти по нескольким процессорам, которые запрашивают один и тот же результат.
В докторской диссертации параллельные префиксные операции являются частью формализации модели , предоставляемой машинами, такими как Connection Machine . Connection Machine CM-1 и CM-2 предоставила гиперкубическую сеть, в которой мог быть реализован вышеупомянутый алгоритм 1, тогда как CM-5 предоставил сеть для реализации алгоритма 2.
При построении кодов Грея , последовательностей двоичных значений со свойством того, что значения последовательных последовательностей отличаются друг от друга в одной битовой позиции, число n может быть преобразовано в значение кода Грея в позиции n , просто взяв исключающее «или» n и n /2 (число, образованное смещением n вправо на одну битовую позицию). Обратная операция, декодирующая кодированное по Грею значение x в двоичное число, является более сложной, но может быть выражена как префиксная сумма битов x , где каждая операция суммирования в пределах префиксной суммы выполняется по модулю два. Префиксная сумма этого типа может быть эффективно выполнена с использованием побитовых логических операций, доступных на современных компьютерах, путем вычисления исключающего «или» или x с каждым из чисел, образованных смещением x влево на число битов, которое является степенью двух.
Параллельный префикс (с использованием умножения в качестве основной ассоциативной операции) также можно использовать для построения быстрых алгоритмов параллельной полиномиальной интерполяции . В частности, его можно использовать для вычисления коэффициентов деления разности в форме Ньютона интерполяционного полинома. Этот подход, основанный на префиксах, может также использоваться для получения обобщенных разделенных разностей для (конфлюентной) интерполяции Эрмита , а также для параллельных алгоритмов для систем Вандермонда .