|
| 1 | +//! Inversion count via merge sort. |
| 2 | +//! |
| 3 | +//! An inversion is a pair of indices `(i, j)` with `i < j` and `arr[i] > arr[j]`. |
| 4 | +//! Counting them naively is `O(n^2)`; piggy-backing the count onto a merge sort |
| 5 | +//! brings it down to `O(n log n)` time with `O(n)` auxiliary space. |
| 6 | +//! |
| 7 | +//! Key insight: during the merge step, when we pick an element from the right |
| 8 | +//! half because it is strictly smaller than the current left-half element, |
| 9 | +//! every remaining element in the left half forms an inversion with it. So we |
| 10 | +//! add `left.len() - left_idx` to the running counter. |
| 11 | +//! |
| 12 | +//! Equal elements are not counted as inversions. |
| 13 | +//! |
| 14 | +//! Complexity: `O(n log n)` time, `O(n)` extra space. |
| 15 | +//! Returns `u64` so counts up to `n*(n-1)/2` fit comfortably for `n` up to a |
| 16 | +//! few billion. |
| 17 | +
|
| 18 | +/// Counts the number of inversions in `arr` — pairs `(i, j)` with `i < j` and |
| 19 | +/// `arr[i] > arr[j]`. |
| 20 | +/// |
| 21 | +/// Runs a non-mutating merge sort over a cloned working buffer and tallies |
| 22 | +/// inversions during each merge. |
| 23 | +pub fn inversion_count<T: Ord + Clone>(arr: &[T]) -> u64 { |
| 24 | + if arr.len() < 2 { |
| 25 | + return 0; |
| 26 | + } |
| 27 | + let mut buf: Vec<T> = arr.to_vec(); |
| 28 | + let mut scratch: Vec<T> = arr.to_vec(); |
| 29 | + sort_count(&mut buf, &mut scratch) |
| 30 | +} |
| 31 | + |
| 32 | +fn sort_count<T: Ord + Clone>(slice: &mut [T], scratch: &mut [T]) -> u64 { |
| 33 | + let n = slice.len(); |
| 34 | + if n < 2 { |
| 35 | + return 0; |
| 36 | + } |
| 37 | + let mid = n / 2; |
| 38 | + let (left, right) = slice.split_at_mut(mid); |
| 39 | + let (sl, sr) = scratch.split_at_mut(mid); |
| 40 | + let mut count = sort_count(left, sl); |
| 41 | + count += sort_count(right, sr); |
| 42 | + count += merge_count(left, right, scratch); |
| 43 | + slice.clone_from_slice(&scratch[..n]); |
| 44 | + count |
| 45 | +} |
| 46 | + |
| 47 | +fn merge_count<T: Ord + Clone>(left: &[T], right: &[T], out: &mut [T]) -> u64 { |
| 48 | + let (mut i, mut j, mut k) = (0, 0, 0); |
| 49 | + let mut inv: u64 = 0; |
| 50 | + while i < left.len() && j < right.len() { |
| 51 | + if left[i] <= right[j] { |
| 52 | + out[k] = left[i].clone(); |
| 53 | + i += 1; |
| 54 | + } else { |
| 55 | + out[k] = right[j].clone(); |
| 56 | + // every remaining element in `left` is greater than `right[j]`. |
| 57 | + inv += (left.len() - i) as u64; |
| 58 | + j += 1; |
| 59 | + } |
| 60 | + k += 1; |
| 61 | + } |
| 62 | + while i < left.len() { |
| 63 | + out[k] = left[i].clone(); |
| 64 | + i += 1; |
| 65 | + k += 1; |
| 66 | + } |
| 67 | + while j < right.len() { |
| 68 | + out[k] = right[j].clone(); |
| 69 | + j += 1; |
| 70 | + k += 1; |
| 71 | + } |
| 72 | + inv |
| 73 | +} |
| 74 | + |
| 75 | +#[cfg(test)] |
| 76 | +mod tests { |
| 77 | + use super::inversion_count; |
| 78 | + |
| 79 | + fn brute_force<T: Ord>(arr: &[T]) -> u64 { |
| 80 | + let mut count: u64 = 0; |
| 81 | + for i in 0..arr.len() { |
| 82 | + for j in (i + 1)..arr.len() { |
| 83 | + if arr[i] > arr[j] { |
| 84 | + count += 1; |
| 85 | + } |
| 86 | + } |
| 87 | + } |
| 88 | + count |
| 89 | + } |
| 90 | + |
| 91 | + #[test] |
| 92 | + fn empty() { |
| 93 | + let a: Vec<i32> = vec![]; |
| 94 | + assert_eq!(inversion_count(&a), 0); |
| 95 | + } |
| 96 | + |
| 97 | + #[test] |
| 98 | + fn single() { |
| 99 | + assert_eq!(inversion_count(&[42]), 0); |
| 100 | + } |
| 101 | + |
| 102 | + #[test] |
| 103 | + fn sorted() { |
| 104 | + assert_eq!(inversion_count(&[1, 2, 3, 4, 5, 6]), 0); |
| 105 | + } |
| 106 | + |
| 107 | + #[test] |
| 108 | + fn reverse_sorted() { |
| 109 | + for n in 0..=10usize { |
| 110 | + let v: Vec<i32> = (0..n as i32).rev().collect(); |
| 111 | + let expected = (n as u64) * (n.saturating_sub(1) as u64) / 2; |
| 112 | + assert_eq!(inversion_count(&v), expected); |
| 113 | + } |
| 114 | + } |
| 115 | + |
| 116 | + #[test] |
| 117 | + fn small_known() { |
| 118 | + assert_eq!(inversion_count(&[2, 4, 1, 3, 5]), 3); |
| 119 | + } |
| 120 | + |
| 121 | + #[test] |
| 122 | + fn duplicates_only() { |
| 123 | + assert_eq!(inversion_count(&[1, 1, 1]), 0); |
| 124 | + assert_eq!(inversion_count(&[7, 7, 7, 7, 7]), 0); |
| 125 | + } |
| 126 | + |
| 127 | + #[test] |
| 128 | + fn duplicates_mixed() { |
| 129 | + // (3,1),(3,2),(3,2) -> 3 inversions |
| 130 | + assert_eq!(inversion_count(&[3, 1, 2, 2]), 3); |
| 131 | + } |
| 132 | + |
| 133 | + #[test] |
| 134 | + fn strings() { |
| 135 | + let v = vec!["pear", "apple", "banana"]; |
| 136 | + assert_eq!(inversion_count(&v), 2); |
| 137 | + } |
| 138 | + |
| 139 | + #[test] |
| 140 | + fn matches_brute_force_small() { |
| 141 | + // Deterministic LCG for reproducibility — no rand dependency. |
| 142 | + let mut state: u64 = 0x9E37_79B9_7F4A_7C15; |
| 143 | + for _ in 0..40 { |
| 144 | + state = state |
| 145 | + .wrapping_mul(6_364_136_223_846_793_005) |
| 146 | + .wrapping_add(1); |
| 147 | + let len = ((state >> 32) as usize) % 12; |
| 148 | + let mut v: Vec<i32> = Vec::with_capacity(len); |
| 149 | + for _ in 0..len { |
| 150 | + state = state |
| 151 | + .wrapping_mul(6_364_136_223_846_793_005) |
| 152 | + .wrapping_add(1); |
| 153 | + v.push(((state >> 40) as i32) % 7); |
| 154 | + } |
| 155 | + assert_eq!(inversion_count(&v), brute_force(&v), "mismatch on {v:?}"); |
| 156 | + } |
| 157 | + } |
| 158 | +} |
0 commit comments