Function nekolib::algo::parallel_bisect
source · pub fn parallel_bisect<S: StatefulPred>(s: S, q: Vec<S::Input>) -> Vec<usize>
Expand description
並列二分探索を行う。
状態によって返り値の異なる述語を考える。 各クエリに対して、初めて偽になる状態の番号を返す。 常に真となる場合、状態の個数を返す。
Requirements
状態 の述語に を与えたときの返り値を とする。 が偽となるとき、 について も偽となる。
直感的には、状態が進むにつれて真となる条件が厳しくなる述語を指す。
Idea
番目のクエリについて、区間 を管理する。 これは、 は真、 は偽になることを意味する。 状態の個数を として、初期値は とする。
状態を進めていきながら、ある に対して 状態 となったとき、 を計算する。これにより、答えの範囲が半分に絞れる。 この一連の計算を 回繰り返せばよい。
各クエリについて独立に計算するのではなく、 一つの述語を共有して並列に処理することで、計算量を削減できる。
毎ループで状態 まで遷移する必要はなく、 を計算したい が存在する最大の まで見ればよい。
Notes
永続データ構造が作れるのであれば、単にそれを用いて各クエリについて二分探索を行えばよい。 また、クエリの個数が少なく、述語の計算コストが高くない場合は、 各々について線形探索を行う方が高速な場合もありうる。
Complexity
状態 から状態 までの遷移を高々 回行う。 また、各クエリに対して述語の呼び出しを 回行う。
Examples
use nekolib::algo::parallel_bisect;
use nekolib::traits::StatefulPred;
struct Neko(i32);
impl Neko {
pub fn new() -> Self { Self(0) }
}
/// 状態 `i` において値 `10 * i` を持ち、値 `100` を最終状態とする。
/// この値より大きい値に対して真を返す。
impl StatefulPred for Neko {
type Input = i32;
fn count(&self) -> usize { 11 }
fn next(&mut self) {
if self.0 < 100 { self.0 += 10; }
}
fn pred(&self, &x: &i32) -> bool { x > self.0 }
fn reset(&mut self) { self.0 = 0; }
}
let qs = vec![0, 1, 32, 60, 89, 99, 100, 101, 500];
assert_eq!(
parallel_bisect(Neko::new(), qs),
vec![0, 1, 4, 6, 9, 10, 10, 11, 11]
);