1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
//! スライスの最小値・最大値を求める。
use std::cmp::Ordering::{self, Equal, Greater, Less};
/// スライスの最小値および最大値を求める。
///
/// 該当する要素が複数個あった場合、最小値は最左のもの、最大値は最右のものが選ばれる。
/// スライスが空の場合は `None` を返す。
///
/// # Suggestions
/// 最小値・最大値の添字ではなく最小値・最大値自体を返すようになっている。
/// 添字が欲しい場合は [`minmax_by_key`] を利用するのがよい? あるいは設計を変える?
///
/// # Complexity
/// [`minmax_by`] における `compare` の呼び出し回数と同じだけ、要素間の比較を行う。
///
/// # Notes
/// 詳細については [`minmax_by`] を参照。
///
/// # Examples
/// ```
/// use nekolib::algo::minmax;
///
/// assert_eq!(minmax(&[3, 2, 4, 1, 2, 0, 6]), Some((&0, &6)));
/// assert_eq!(minmax(&Vec::<i32>::new()), None);
/// ```
///
/// [`minmax_by`]: fn.minmax_by.html
/// [`minmax_by_key`]: fn.minmax_by_key.html
pub fn minmax<T: Ord>(buf: &[T]) -> Option<(&T, &T)> {
minmax_by(buf, |x: &T, y: &T| x.cmp(y))
}
/// キー `key` におけるスライスの最小値および最大値を求める。
///
/// 該当する要素が複数個あった場合、最小値は最左のもの、最大値は最右のものが選ばれる。
/// スライスが空の場合は `None` を返す。
///
/// # Complexity
/// [`minmax_by`] における `compare` の呼び出し回数と同じだけ、要素間の比較を行う。
/// また、`key` の呼び出しをその $2$ 倍の回数だけ行う。
///
/// # Implementation notes
/// 実装を [`minmax_by`] に丸投げしているので `key` を $3n$ 回程度呼び出しうるが、
/// 適切に実装することで $n$ 回に抑えられるはず。
///
/// `key` のコストが大きい場合は予め別の配列を作る方がよさそう。
///
/// # Notes
/// 詳細については [`minmax_by`] を参照。
///
/// # Examples
/// ```
/// use nekolib::algo::minmax_by_key;
///
/// let buf: Vec<_> =
/// vec![3, 5, 0, 1, 2, 0, 5].into_iter().enumerate().collect();
/// assert_eq!(minmax_by_key(&buf, |&(_, x)| x), Some((&(2, 0), &(6, 5))));
///
/// let buf: Vec<i32> = vec![];
/// assert_eq!(minmax_by_key(&buf, |&x| x), None);
/// ```
///
/// [`minmax_by`]: fn.minmax_by.html
pub fn minmax_by_key<T, K, U>(buf: &[T], mut key: K) -> Option<(&T, &T)>
where
K: FnMut(&T) -> U,
U: Ord,
{
minmax_by(buf, |x: &T, y: &T| key(&x).cmp(&key(&y)))
}
/// 比較関数 `compare` におけるスライスの最小値および最大値を求める。
///
/// 該当する要素が複数個あった場合、最小値は最左のもの、最大値は最右のものが選ばれる。
/// スライスが空の場合は `None` を返す。
///
/// # Complexity
/// 要素数を $n \\gt 0$ として、`compare` の呼び出しを
/// $\\lceil\\frac{3n}{2}\\rceil -2 \\lt 1.5n$
/// 回行う。
/// スライスが空の場合は $0$ 回の呼び出しを行う。
///
/// この比較回数は最適であり、下界は [adversary argument] によって示すことができる。
///
/// [adversary argument]: https://jeffe.cs.illinois.edu/teaching/algorithms/notes/13-adversary.pdf
///
/// # Notes
///
/// C++ では比較を `bool` の二値で行うため [`std::minmax_element`] は等価な要素の扱いに困る。
/// 最小値も最大値も最左のものを返そうとすると、比較回数の下界を達成できないはず。
/// 一方 Rust では、[`Ordering`] の三値を返すので、実装を変えることで任意のペアを返すことが可能。
///
/// [`std::minmax_element`]: https://en.cppreference.com/w/cpp/algorithm/minmax_element
/// [`Ordering`]: https://doc.rust-lang.org/std/cmp/enum.Ordering.html
///
/// # Examples
/// ```
/// use nekolib::algo::minmax_by;
///
/// let buf: Vec<_> =
/// vec![3, 9, 0, 1, 2, 0, 9].into_iter().enumerate().collect();
/// let rev = |&(_, x): &(usize, i32), &(_, y): &(usize, i32)| y.cmp(&x);
/// assert_eq!(minmax_by(&buf, rev), Some((&(1, 9), &(5, 0))));
///
/// let buf = vec![];
/// assert_eq!(minmax_by(&buf, rev), None);
/// ```
pub fn minmax_by<T, F: FnMut(&T, &T) -> Ordering>(
buf: &[T],
mut compare: F,
) -> Option<(&T, &T)> {
if buf.is_empty() {
return None;
}
if buf.len() == 1 {
return Some((&buf[0], &buf[0]));
}
let (mut min, mut max) = match compare(&buf[0], &buf[1]) {
Less | Equal => (&buf[0], &buf[1]),
Greater => (&buf[1], &buf[0]),
};
for i in (2..buf.len()).step_by(2) {
let (min_i, max_i) = match (buf.get(i), buf.get(i + 1)) {
(Some(f), None) => (f, f),
(Some(f), Some(s)) => match compare(f, s) {
Less | Equal => (f, s),
Greater => (s, f),
},
(None, _) => unreachable!(),
};
if compare(min_i, min) == Less {
min = min_i;
}
if compare(max_i, max) != Less {
max = max_i;
}
}
Some((min, max))
}
#[test]
fn test() {
use std::fmt::Debug;
fn test_inner<T: Debug + Eq + Ord>(
expected: Option<(&(usize, &T), &(usize, &T))>,
buf: &[T],
) {
let mut cmped = 0;
let counted_cmp = |x: &(usize, &T), y: &(usize, &T)| {
cmped += 1;
x.1.cmp(y.1)
};
let buf: Vec<_> = buf.iter().enumerate().collect();
let n = buf.len();
assert_eq!(minmax_by(&buf, counted_cmp), expected);
if n == 0 {
assert_eq!(cmped, 0);
} else {
assert_eq!(cmped, n / 2 + (n - 1) / 2 * 2);
}
}
test_inner(Some((&(0, &0), &(0, &0))), &[0]);
test_inner(Some((&(0, &0), &(1, &0))), &[0, 0]);
test_inner(Some((&(0, &0), &(1, &10))), &[0, 10]);
test_inner(Some((&(1, &0), &(0, &10))), &[10, 0]);
test_inner(Some((&(0, &0), &(2, &0))), &[0, 0, 0]);
test_inner(Some((&(0, &0), &(2, &20))), &[0, 10, 20]);
test_inner(Some((&(0, &0), &(2, &10))), &[0, 10, 10]);
test_inner(Some((&(0, &10), &(1, &20))), &[10, 20, 10]);
test_inner(Some((&(0, &0), &(3, &0))), &[0, 0, 0, 0]);
test_inner(Some((&(0, &0), &(3, &10))), &[0, 10, 0, 10]);
test_inner(Some((&(1, &0), &(2, &10))), &[10, 0, 10, 0]);
test_inner(Some((&(0, &0), &(3, &10))), &[0, 0, 10, 10]);
test_inner(Some((&(2, &0), &(1, &10))), &[10, 10, 0, 0]);
let rev = |&(_, x): &(usize, i32), &(_, y): &(usize, i32)| y.cmp(&x);
let buf = vec![];
assert_eq!(minmax_by(&buf, rev), None);
}