From 124803ae4b80ad360d36f97b286a5dd444a0a586 Mon Sep 17 00:00:00 2001 From: Paulden Date: Thu, 19 Jan 2023 03:36:17 +0800 Subject: [PATCH] Add Patience Sort (#439) --- DIRECTORY.md | 1 + README.md | 1 + src/general/convex_hull.rs | 4 +- src/general/nqueens.rs | 2 +- src/math/gaussian_elimination.rs | 6 +-- src/math/quadratic_residue.rs | 2 +- src/sorting/README.md | 21 +++++++- src/sorting/mod.rs | 2 + src/sorting/patience_sort.rs | 82 ++++++++++++++++++++++++++++++ src/sorting/quick_sort.rs | 2 +- src/sorting/tim_sort.rs | 6 +-- src/string/boyer_moore_search.rs | 4 +- src/string/levenshtein_distance.rs | 2 +- 13 files changed, 120 insertions(+), 15 deletions(-) create mode 100644 src/sorting/patience_sort.rs diff --git a/DIRECTORY.md b/DIRECTORY.md index 3b6b321e4fa..98f5771d128 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -166,6 +166,7 @@ * [Sleep Sort](https://github.com/TheAlgorithms/Rust/blob/master/src/sorting/sleep_sort.rs) * [Stooge Sort](https://github.com/TheAlgorithms/Rust/blob/master/src/sorting/stooge_sort.rs) * [Tim Sort](https://github.com/TheAlgorithms/Rust/blob/master/src/sorting/tim_sort.rs) + * [Patience Sort](https://github.com/TheAlgorithms/Rust/blob/master/src/sorting/patience_sort.rs) * String * [Aho Corasick](https://github.com/TheAlgorithms/Rust/blob/master/src/string/aho_corasick.rs) * [Anagram](https://github.com/TheAlgorithms/Rust/blob/master/src/string/anagram.rs) diff --git a/README.md b/README.md index 6d2dbe7694c..68814b8eb06 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ These are for demonstration purposes only. - [x] [Bucket](./src/sorting/bucket_sort.rs) - [x] [Timsort](./src/sorting/tim_sort.rs) - [x] [Sleep](./src/sorting/sleep_sort.rs) +- [x] [Patience](./src/sorting/patience_sort.rs) ## [Graphs](./src/graph) diff --git a/src/general/convex_hull.rs b/src/general/convex_hull.rs index 272004adee2..c053fb421e6 100644 --- a/src/general/convex_hull.rs +++ b/src/general/convex_hull.rs @@ -5,9 +5,9 @@ fn sort_by_min_angle(pts: &[(f64, f64)], min: &(f64, f64)) -> Vec<(f64, f64)> { .iter() .map(|x| { ( - ((x.1 - min.1) as f64).atan2((x.0 - min.0) as f64), + (x.1 - min.1).atan2(x.0 - min.0), // angle - ((x.1 - min.1) as f64).hypot((x.0 - min.0) as f64), + (x.1 - min.1).hypot(x.0 - min.0), // distance (we want the closest to be first) *x, ) diff --git a/src/general/nqueens.rs b/src/general/nqueens.rs index c776a35b6f6..7baddea9153 100644 --- a/src/general/nqueens.rs +++ b/src/general/nqueens.rs @@ -20,7 +20,7 @@ pub fn nqueens(board_width: i64) -> Result, &'static str> { if board_rows[current_row] == board_rows[review_index] || (left >= 0 && left == board_rows[current_row]) - || (right < board_width as i64 && right == board_rows[current_row]) + || (right < board_width && right == board_rows[current_row]) { conflict = true; break; diff --git a/src/math/gaussian_elimination.rs b/src/math/gaussian_elimination.rs index c2f98cb98f5..5282a6e0659 100644 --- a/src/math/gaussian_elimination.rs +++ b/src/math/gaussian_elimination.rs @@ -37,7 +37,7 @@ fn echelon(matrix: &mut [Vec], i: usize, j: usize) { let size = matrix.len(); if matrix[i][i] == 0f32 { } else { - let factor = matrix[j + 1][i] as f32 / matrix[i][i] as f32; + let factor = matrix[j + 1][i] / matrix[i][i]; (i..size + 1).for_each(|k| { matrix[j + 1][k] -= factor * matrix[i][k]; }); @@ -49,9 +49,9 @@ fn eliminate(matrix: &mut [Vec], i: usize) { if matrix[i][i] == 0f32 { } else { for j in (1..i + 1).rev() { - let factor = matrix[j - 1][i] as f32 / matrix[i][i] as f32; + let factor = matrix[j - 1][i] / matrix[i][i]; for k in (0..size + 1).rev() { - matrix[j - 1][k] -= factor * matrix[i][k] as f32; + matrix[j - 1][k] -= factor * matrix[i][k]; } } } diff --git a/src/math/quadratic_residue.rs b/src/math/quadratic_residue.rs index 44696d7d8fd..f179da81db4 100644 --- a/src/math/quadratic_residue.rs +++ b/src/math/quadratic_residue.rs @@ -101,7 +101,7 @@ pub fn cipolla(a: u32, p: u32, seed: Option) -> Option<(u32, u32)> { let comp = CustomComplexNumber::new(r, 1, filed); let power = (p + 1) >> 1; let x0 = CustomComplexNumber::fast_power(comp, power).real as u32; - let x1 = p as u32 - x0 as u32; + let x1 = p as u32 - x0; if x0 < x1 { Some((x0, x1)) } else { diff --git a/src/sorting/README.md b/src/sorting/README.md index e90a21ebab0..4b0e248db35 100644 --- a/src/sorting/README.md +++ b/src/sorting/README.md @@ -189,6 +189,22 @@ __Properties__ From [Wikipedia][bucket-sort-wiki]: This is an idea that was originally posted on the message board 4chan, replacing the bucket in bucket sort with time instead of memory space. It is actually possible to sort by "maximum of all elements x unit time to sleep". The only case where this would be useful would be in examples. +### [Patience](./patience_sort.rs) +[patience-video] + + +From [Wikipedia][patience-sort-wiki]: The algorithm's name derives from a simplified variant of the patience card game. The game begins with a shuffled deck of cards. The cards are dealt one by one into a sequence of piles on the table, according to the following rules. + +1. Initially, there are no piles. The first card dealt forms a new pile consisting of the single card. +2. Each subsequent card is placed on the leftmost existing pile whose top card has a value greater than or equal to the new card's value, or to the right of all of the existing piles, thus forming a new pile. +3. When there are no more cards remaining to deal, the game ends. + +This card game is turned into a two-phase sorting algorithm, as follows. Given an array of n elements from some totally ordered domain, consider this array as a collection of cards and simulate the patience sorting game. When the game is over, recover the sorted sequence by repeatedly picking off the minimum visible card; in other words, perform a k-way merge of the p piles, each of which is internally sorted. + +__Properties__ +* Worst case performance O(n log n) +* Best case performance O(n) + [bogo-wiki]: https://en.wikipedia.org/wiki/Bogosort [bogo-image]: https://upload.wikimedia.org/wikipedia/commons/7/7b/Bogo_sort_animation.gif @@ -243,4 +259,7 @@ It is actually possible to sort by "maximum of all elements x unit time to sleep [comb-sort-wiki]: https://en.wikipedia.org/wiki/Comb_sort [sleep-sort]: -[sleep-sort-wiki]https://ja.m.wikipedia.org/wiki/バケットソート#.E3.82.B9.E3.83.AA.E3.83.BC.E3.83.97.E3.82.BD.E3.83.BC.E3.83.88 +[sleep-sort-wiki]: https://ja.m.wikipedia.org/wiki/バケットソート#.E3.82.B9.E3.83.AA.E3.83.BC.E3.83.97.E3.82.BD.E3.83.BC.E3.83.88 + +[patience-sort-wiki]: https://en.wikipedia.org/wiki/Patience_sorting +[patience-video]: https://user-images.githubusercontent.com/67539676/212542208-d3f7a824-60d8-467c-8097-841945514ae9.mp4 diff --git a/src/sorting/mod.rs b/src/sorting/mod.rs index 6ba119939c4..1eeed723a75 100644 --- a/src/sorting/mod.rs +++ b/src/sorting/mod.rs @@ -13,6 +13,7 @@ mod insertion_sort; mod merge_sort; mod odd_even_sort; mod pancake_sort; +mod patience_sort; mod pigeonhole_sort; mod quick_sort; mod radix_sort; @@ -39,6 +40,7 @@ pub use self::merge_sort::bottom_up_merge_sort; pub use self::merge_sort::top_down_merge_sort; pub use self::odd_even_sort::odd_even_sort; pub use self::pancake_sort::pancake_sort; +pub use self::patience_sort::patience_sort; pub use self::pigeonhole_sort::pigeonhole_sort; pub use self::quick_sort::{partition, quick_sort}; pub use self::radix_sort::radix_sort; diff --git a/src/sorting/patience_sort.rs b/src/sorting/patience_sort.rs new file mode 100644 index 00000000000..55ca2c8e027 --- /dev/null +++ b/src/sorting/patience_sort.rs @@ -0,0 +1,82 @@ +use std::vec; + +pub fn patience_sort(arr: &mut [T]) { + if arr.is_empty() { + return; + } + + // collect piles from arr + let mut piles: Vec> = Vec::new(); + for &card in arr.iter() { + let mut left = 0usize; + let mut right = piles.len(); + + while left < right { + let mid = left + (right - left) / 2; + if piles[mid][piles[mid].len() - 1] >= card { + right = mid; + } else { + left = mid + 1; + } + } + + if left == piles.len() { + piles.push(vec![card]); + } else { + piles[left].push(card); + } + } + + // merge the piles + let mut idx = 0usize; + while let Some((min_id, pile)) = piles + .iter() + .enumerate() + .min_by_key(|(_, pile)| *pile.last().unwrap()) + { + arr[idx] = *pile.last().unwrap(); + idx += 1; + piles[min_id].pop(); + + if piles[min_id].is_empty() { + _ = piles.remove(min_id); + } + } +} + +#[cfg(test)] +mod tests { + use crate::sorting::is_sorted; + + use super::*; + + #[test] + fn basic() { + let mut array = vec![ + -2, 7, 15, -14, 0, 15, 0, 100_33, 7, -7, -4, -13, 5, 8, -14, 12, + ]; + patience_sort(&mut array); + assert!(is_sorted(&array)); + } + + #[test] + fn empty() { + let mut array = Vec::::new(); + patience_sort(&mut array); + assert!(is_sorted(&array)); + } + + #[test] + fn one_element() { + let mut array = vec![3]; + patience_sort(&mut array); + assert!(is_sorted(&array)); + } + + #[test] + fn pre_sorted() { + let mut array = vec![-123_456, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + patience_sort(&mut array); + assert!(is_sorted(&array)); + } +} diff --git a/src/sorting/quick_sort.rs b/src/sorting/quick_sort.rs index e07605c373d..9f4317c4c35 100644 --- a/src/sorting/quick_sort.rs +++ b/src/sorting/quick_sort.rs @@ -20,7 +20,7 @@ pub fn partition(arr: &mut [T], lo: isize, hi: isize) -> isize { arr.swap(i as usize, j as usize); } } - arr.swap(i as usize, pivot as usize); + arr.swap(i as usize, pivot); i } diff --git a/src/sorting/tim_sort.rs b/src/sorting/tim_sort.rs index b81cefc83c9..4b7f46de894 100644 --- a/src/sorting/tim_sort.rs +++ b/src/sorting/tim_sort.rs @@ -28,8 +28,8 @@ fn insertion_sort(arr: &mut Vec, left: usize, right: usize) -> &Vec { fn merge(arr: &mut Vec, l: usize, m: usize, r: usize) -> &Vec { let len1 = m - l + 1; let len2 = r - m; - let mut left = vec![0; len1 as usize]; - let mut right = vec![0; len2 as usize]; + let mut left = vec![0; len1]; + let mut right = vec![0; len2]; left[..len1].clone_from_slice(&arr[l..(len1 + l)]); @@ -67,7 +67,7 @@ fn merge(arr: &mut Vec, l: usize, m: usize, r: usize) -> &Vec { } pub fn tim_sort(arr: &mut Vec, n: usize) { - let min_run = min_run_length(MIN_MERGE) as usize; + let min_run = min_run_length(MIN_MERGE); let mut i = 0; while i < n { diff --git a/src/string/boyer_moore_search.rs b/src/string/boyer_moore_search.rs index e88e1f26d0b..eb4297a3d03 100644 --- a/src/string/boyer_moore_search.rs +++ b/src/string/boyer_moore_search.rs @@ -17,8 +17,8 @@ pub fn boyer_moore_search(text: &str, pattern: &str) -> Vec { collection.insert(c, i as i32); } let mut shift: i32 = 0; - while shift <= (n - m) as i32 { - let mut j = (m - 1) as i32; + while shift <= (n - m) { + let mut j = m - 1; while j >= 0 && pattern[j as usize] == text[(shift + j) as usize] { j -= 1; } diff --git a/src/string/levenshtein_distance.rs b/src/string/levenshtein_distance.rs index 6572d7d927d..99efd05aa1c 100644 --- a/src/string/levenshtein_distance.rs +++ b/src/string/levenshtein_distance.rs @@ -19,7 +19,7 @@ pub fn levenshtein_distance(string1: &str, string2: &str) -> usize { for c1 in string1.chars() { let deletion_cost = d[i - 1] + 1; let insertion_cost = d[i] + 1; - let substitution_cost = previous_substitution_cost + if c1 == c2 { 0 } else { 1 }; + let substitution_cost = previous_substitution_cost + usize::from(c1 != c2); previous_substitution_cost = d[i]; d[i] = min3(deletion_cost, insertion_cost, substitution_cost);