mirror of https://codeberg.org/topola/topola.git
fix(math/cyclic_search): Use classic binary search instead
It is unnecessary to use exponential search here, and the computation complexity of binary search is easier to understand and obvious O(log n). This degrades performance minimally in biased edge cases, and improves performance in the hopefully common "roughly half of values are false, and other half is true" case.
This commit is contained in:
parent
864cf9085a
commit
42cf8f3a69
|
|
@ -129,7 +129,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search for the largest index inside the bounds which still fulfills the condition
|
/// Search for the largest index inside the bounds which still fulfills the condition
|
||||||
fn exponential_search<T, EF>(
|
fn binary_search<T, EF>(
|
||||||
eval: &EF,
|
eval: &EF,
|
||||||
expected_value: T,
|
expected_value: T,
|
||||||
mut bounds: core::ops::Range<usize>,
|
mut bounds: core::ops::Range<usize>,
|
||||||
|
|
@ -143,28 +143,21 @@ where
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut largest_checked = bounds.start;
|
while bounds.start + 1 < bounds.end {
|
||||||
while (bounds.start + 1) < bounds.end {
|
let middle = bounds.start + (bounds.end - bounds.start) / 2;
|
||||||
let len = bounds.end - bounds.start;
|
debug_assert_ne!(middle, bounds.start);
|
||||||
for level in 0..64u8 {
|
debug_assert_ne!(middle, bounds.end);
|
||||||
let mut index = 1 << level;
|
|
||||||
if index >= len {
|
// binary search for partition bounds
|
||||||
break;
|
if eval(middle) == expected_value {
|
||||||
|
bounds.start = middle;
|
||||||
|
} else {
|
||||||
|
bounds.end = middle;
|
||||||
}
|
}
|
||||||
index += bounds.start;
|
|
||||||
if eval(index) != expected_value {
|
|
||||||
bounds.end = index;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
largest_checked = index;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bounds.start = largest_checked;
|
debug_assert_eq!(eval(bounds.start), expected_value);
|
||||||
// this implies that `bounds.start` doesn't have to get checked again
|
Some(bounds.start)
|
||||||
}
|
|
||||||
|
|
||||||
debug_assert_eq!(eval(largest_checked), expected_value);
|
|
||||||
Some(largest_checked)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform a breadth-first search on an induced binary tree on the list,
|
/// Perform a breadth-first search on an induced binary tree on the list,
|
||||||
|
|
@ -221,8 +214,8 @@ where
|
||||||
true => (&mut pos_true, &mut pos_false),
|
true => (&mut pos_true, &mut pos_false),
|
||||||
};
|
};
|
||||||
|
|
||||||
*pos_start = exponential_search(&eval, val_start, bounds.start..*pos_next).unwrap();
|
*pos_start = binary_search(&eval, val_start, bounds.start..*pos_next).unwrap();
|
||||||
*pos_next = exponential_search(&eval, !val_start, *pos_start + 1..bounds.end).unwrap();
|
*pos_next = binary_search(&eval, !val_start, *pos_start + 1..bounds.end).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
(Some(pos_false), Some(pos_true))
|
(Some(pos_false), Some(pos_true))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue