Skip to content

Commit

Permalink
Add Patience Sort (TheAlgorithms#439)
Browse files Browse the repository at this point in the history
  • Loading branch information
guuzaa authored Jan 18, 2023
1 parent 0457a79 commit 124803a
Show file tree
Hide file tree
Showing 13 changed files with 120 additions and 15 deletions.
1 change: 1 addition & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
4 changes: 2 additions & 2 deletions src/general/convex_hull.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Expand Down
2 changes: 1 addition & 1 deletion src/general/nqueens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub fn nqueens(board_width: i64) -> Result<Vec<i64>, &'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;
Expand Down
6 changes: 3 additions & 3 deletions src/math/gaussian_elimination.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn echelon(matrix: &mut [Vec<f32>], 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];
});
Expand All @@ -49,9 +49,9 @@ fn eliminate(matrix: &mut [Vec<f32>], 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];
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/math/quadratic_residue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ pub fn cipolla(a: u32, p: u32, seed: Option<u64>) -> 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 {
Expand Down
21 changes: 20 additions & 1 deletion src/sorting/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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]: <no image>
[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
2 changes: 2 additions & 0 deletions src/sorting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
82 changes: 82 additions & 0 deletions src/sorting/patience_sort.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use std::vec;

pub fn patience_sort<T: Ord + Copy>(arr: &mut [T]) {
if arr.is_empty() {
return;
}

// collect piles from arr
let mut piles: Vec<Vec<T>> = 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::<i32>::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));
}
}
2 changes: 1 addition & 1 deletion src/sorting/quick_sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub fn partition<T: PartialOrd>(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
}

Expand Down
6 changes: 3 additions & 3 deletions src/sorting/tim_sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ fn insertion_sort(arr: &mut Vec<i32>, left: usize, right: usize) -> &Vec<i32> {
fn merge(arr: &mut Vec<i32>, l: usize, m: usize, r: usize) -> &Vec<i32> {
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)]);

Expand Down Expand Up @@ -67,7 +67,7 @@ fn merge(arr: &mut Vec<i32>, l: usize, m: usize, r: usize) -> &Vec<i32> {
}

pub fn tim_sort(arr: &mut Vec<i32>, 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 {
Expand Down
4 changes: 2 additions & 2 deletions src/string/boyer_moore_search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ pub fn boyer_moore_search(text: &str, pattern: &str) -> Vec<usize> {
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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/string/levenshtein_distance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 124803a

Please sign in to comment.