累積和
出典: フリー百科事典『ウィキペディア(Wikipedia)』 (2025/09/27 09:42 UTC 版)
累積和(るいせきわ、英: prefix sum, cumulative sum, scan)とは、計算機科学において、数列
HillisとSteeleは、以下の並列累積和アルゴリズムを提案している。[12]
for i <- 0 to floor(log2(n)) do for j <- 0 to n - 1 do in parallel if j < 2i then xi+1
j <- xi
j else xi+1
j <- xi
j![]()
ワーク効率が高い16入力の並列累積和の回路表現 ワーク効率の良い並列累積和は、以下の手順で計算できる。[5][14][15]
- 隣接する要素の和を計算する(ペアの先頭要素のインデックスが偶数であるものを対象とする)。
- 例:
- ステップ1で得た数列 に対して、再帰的に累積和を計算する。
- 結果として、新たな数列 を得る。
- 最終的な累積和 を、中間数列の値を用いて求める。
- 具体的には、各 は、これまでに計算された中間数列の要素の和で表される。
- 例:
- 最初の値 を決めた後、それ以降の各 は、数列 の半分の位置にある値をコピーするか、直前の値に数列 の一部の値を加えることで求める。
入力数列の長さを とすると、このアルゴリズムの再帰の深さは となる。したがって、並列実行時の計算時間も に抑えられる。
このアルゴリズムの総ステップ数は であり、 個のプロセッサを持つ並列ランダムアクセス機械(PRAM)上で、非対称的な遅延なしに実装可能である。これは、プロセッサの数よりも要素数が多い段階では、1つのプロセッサが複数のインデックスを処理するように割り当てることで実現される。
より一般的なfoldやscanに適用する方法
本項では、要素 要素(Haskellの表記では型a -> a -> a[16])として書いているが、関数型プログラミング言語では、
と考えることにより、foldやscanで逐次処理を表現している。foldは最終状態(型b)で、scanは状態遷移列(型[b])である。
ここで、結合法則を満たす
- 状態 状態 = 結合した状態(型b -> b -> b)
が存在すると、
- まず、f(状態, 要素) は f(初期状態, 要素) でしか計算しないので、要素から f(初期状態, 要素) へのmap変換を行う。
- その上で、状態 状態 -> 結合状態を使用すると、上記の並列アルゴリズムが適用できる。
これにより、逐次処理のfoldやscanで表現していたものに対して、並列アルゴリズムが使えるようになる。
具体例として、指数移動平均 を考える。要素は だが、状態を (指数移動平均の最後の値, 指数移動平均の長さ) のタプルとすると下記変換式により並列計算ができる。
- 初期状態 =
- f(初期状態 , 要素 ) =
- 状態 状態 = 結合状態
これをNVIDIA CUDAのThrust(C++)で実装する。要素数nが十分大きく、二項演算子の計算量が小さい場合はinclusive_scanはメモリコピーの速度で動くが[10]、要素から状態への並列map、状態の並列scan、状態から要素への並列mapが必要で、単純に実装すると3回分のメモリコピーが発生するが、Thrustではtransform_iteratorを使用すると1回分のメモリコピーにまとめることができるので、それを使用している。
#include <thrust/device_vector.h> #include <thrust/iterator/transform_output_iterator.h> struct State { float v; int len; }; struct toState { // 要素→状態 const float alpha; __device__ State operator()(const float x) const { return State{alpha * x, 1}; } }; struct toElement { // 状態→要素 __device__ float operator()(const State &s) const { return s.v; } }; struct plusStates { // 状態+状態 const float alpha; __device__ State operator()(const State &a, const State &b) const { return State{std::pow(1 - alpha, (float) b.len) * a.v + b.v, a.len + b.len}; } }; int main() { const float alpha = 0.2f; thrust::device_vector<float> input = {3, 1, 4, 1, 5, 9, 2, 6}; thrust::device_vector<float> output(input.size()); // 指数移動平均の計算 thrust::inclusive_scan( thrust::make_transform_iterator(input.begin(), toState{alpha}), thrust::make_transform_iterator(input.end(), toState{alpha}), thrust::make_transform_output_iterator(output.begin(), toElement{}), plusStates{alpha} ); // 計算結果の出力 for (float x: output) { std::cout << x << " "; } std::cout << std::endl; return 0; }出典
- ^ Blelloch, Guy (2011), Prefix Sums and Their Applications (Lecture Notes), Carnegie Mellon University
- ^ Callahan, Paul; Kosaraju, S. Rao (1995), “A Decomposition of Multi-Dimensional Point Sets with Applications to k-Nearest-Neighbors and n-Body Potential Fields”, Journal of the ACM 42 (1): 67–90, doi:10.1145/200836.200853
- ^ Cormen, Thomas H.; Leiserson, Charles E.; Rivest, Ronald L.; Stein, Clifford (2001), Introduction to Algorithms (2nd ed.), MIT Press and McGraw-Hill, pp. 168–170, ISBN 0-262-03293-7.
- ^ Cole, Richard; Vishkin, Uzi (1986), “Deterministic coin tossing with applications to optimal parallel list ranking”, Information and Control 70 (1): 32–53, doi:10.1016/S0019-9958(86)80023-7
- ^ a b Ladner, R. E.; Fischer, M. J. (1980), “Parallel Prefix Computation”, Journal of the ACM 27 (4): 831–838, doi:10.1145/322217.322232, MR 0594702
- ^ Tarjan, Robert E.; Vishkin, Uzi (1985), “An efficient parallel biconnectivity algorithm”, SIAM Journal on Computing 14 (4): 862–874, doi:10.1137/0214061
- ^ Lakshmivarahan, S.; Dhall, S.K. (1994), Parallelism in the Prefix Problem, Oxford University Press, ISBN 0-19508849-2
- ^ a b c “Chapter 39. Parallel Prefix Sum (Scan) with CUDA - GPU Gems 3”. NVIDIA Developer. 2025年3月15日閲覧。
- ^ “cub::DeviceScan — cub 2.5 documentation”. nvidia.github.io. 2025年3月18日閲覧。
- ^ a b “Single-pass Parallel Prefix Scan with Decoupled Look-back | Research”. research.nvidia.com. 2025年3月17日閲覧。
- ^ “Inclusive Scan - Intel® oneAPI DPC++ Library Developer Guide and Reference”. Intel. 2025年3月17日閲覧。
- ^ Hillis, W. Daniel; Steele, Jr., Guy L. (December 1986). “Data parallel algorithms”. Communications of the ACM 29 (12): 1170–1183. doi:10.1145/7902.7903.
- ^ Owens, John D.; Luebke, David; Govindaraju, Naga; Harris, Mark; Krüger, Jens; Lefohn, Aaron E.; Purcell, Timothy J. (2007). “A Survey of General-Purpose Computation on Graphics Hardware”. Computer Graphics Forum 26 (1): 80–113. doi:10.1111/j.1467-8659.2007.01012.x.
- ^ Ofman, Yu. (1962), (Russian)Doklady Akademii Nauk SSSR 145 (1): 48–51, MR 0168423. English translation, "On the algorithmic complexity of discrete functions", Soviet Physics Doklady 7: 589–591 1963.
- ^ Khrapchenko, V. M. (1967), “Asymptotic Estimation of Addition Time of a Parallel Adder” (Russian), Problemy Kibernet. 19: 107–122. English translation in Syst. Theory Res. 19; 105–122, 1970.
- ^ “scanl1 - Prelude”. hackage.haskell.org. 2025年3月16日閲覧。
- ^ a b “scanl - Prelude”. hackage.haskell.org. 2025年3月16日閲覧。
- 累積和のページへのリンク