Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added BogoBogo Sort, Tree Sort, Bitonic Sort, Pigeonhole Sort, Derrivative Method Alogorithms #67

Merged
merged 3 commits into from
Jan 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions src/math/derivative_method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
const DERIVATIVE_PRECISION: f64 = 0.0001;

pub fn derivative_method<F>(x: f64, y: f64, f: F) -> f64
where
F: Fn(f64, f64) -> f64,
{
let h = DERIVATIVE_PRECISION;
(f(x + h, y) - f(x, y)) / h
}

#[cfg(test)]
mod tests {
use super::*;

fn test_function(x: f64, y: f64) -> f64 {
x.powi(2) + y.powi(2)
}

#[test]
fn test_derivative() {
let x = 1.0;
let y = 2.0;
let f = test_function;
let df_dx = derivative_method(x, y, f);
let df_dy = derivative_method(y, x, f);
assert_eq!(df_dx, 2.000100000003613);
assert_eq!(df_dy, 4.0001000000078335);
}

#[test]
fn test_error() {
let x = 1.0;
let y = 2.0;
let f = test_function;
let df_dx = derivative_method(x, y, f);
let df_dy = derivative_method(y, x, f);
assert_ne!(df_dx, 2.0);
assert_ne!(df_dy, 4.0);
}

#[test]
fn test_nan() {
let x = 1.0;
let y = 2.0;
let f = test_function;
let df_dx = derivative_method(x, y, f);
let df_dy = derivative_method(y, x, f);
assert!(!df_dx.is_nan());
assert!(!df_dy.is_nan());
}

#[test]
fn test_inf() {
let x = 1.0;
let y = 2.0;
let f = test_function;
let df_dx = derivative_method(x, y, f);
let df_dy = derivative_method(y, x, f);
assert!(!df_dx.is_infinite());
assert!(!df_dy.is_infinite());
}

#[test]
fn test_zero() {
let x = 1.0;
let y = 2.0;
let f = test_function;
let df_dx = derivative_method(x, y, f);
let df_dy = derivative_method(y, x, f);
assert_ne!(df_dx, 0.0);
assert_ne!(df_dy, 0.0);
}

#[test]
fn test_subnormal() {
let x = 1.0;
let y = 2.0;
let f = test_function;
let df_dx = derivative_method(x, y, f);
let df_dy = derivative_method(y, x, f);
assert!(!df_dx.is_subnormal());
assert!(!df_dy.is_subnormal());
}
}
2 changes: 2 additions & 0 deletions src/math/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod armstrong_number;
mod baby_step_giant_step;
mod derivative_method;
mod extended_euclidean_algorithm;
mod fast_fourier_transform;
mod fast_power;
Expand Down Expand Up @@ -30,6 +31,7 @@ mod zellers_congruence_algorithm;

pub use self::armstrong_number::is_armstrong_number;
pub use self::baby_step_giant_step::baby_step_giant_step;
pub use self::derivative_method::derivative_method;
pub use self::extended_euclidean_algorithm::extended_euclidean_algorithm;
pub use self::fast_fourier_transform::{
fast_fourier_transform, fast_fourier_transform_input_permutation,
Expand Down
84 changes: 84 additions & 0 deletions src/sorting/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
### [Bitonic Sort](./bitonic_sort.rs)

Bitonic Sort is an efficient sorting algorithm based on the bitonic sequence concept and is often used in parallel processing due to its regular and repetitive structure. It works by first sorting sub-arrays in different directions and then combining them in a special merging process, ensuring a fully sorted array. This algorithm is particularly efficient when dealing with data sets whose size is a power of two.

* Parallel Processing: Bitonic sort is highly parallelizable, making it suitable for multi-threading and distributed systems, a strength that Rust can capitalize on with its robust concurrency features.
* Recursive Strategy: The algorithm uses recursive division of the array, which is elegantly handled in Rust with its strong support for recursion and memory safety.
* Efficiency: For medium-sized arrays, especially in parallel computing environments, bitonic sort can be very efficient, leveraging Rust's performance capabilities.

__Properties__
* Special Case Input: Bitonic sort is most efficient when the input size is a power of two.
* Time Complexity: In the worst-case scenario, the time complexity of bitonic sort is O(n log² n).
* Not-in-place Sorting: While it doesn't sort in place, it requires less extra space compared to other non-in-place sorting algorithms.

__Sources to read:__
* [Wikipedia](https://en.wikipedia.org/wiki/Bitonic_sorter)
* [Geeksforgeeks](https://www.geeksforgeeks.org/bitonic-sort/)
* [TutorialsPoint](https://www.tutorialspoint.com/bitonic-sort-in-data-structure)

### [Bogo Sort](./bogo_sort.rs)

From [Wikipedia][bogosort-wiki]: In computer science, bogosort (also known as permutation sort, stupid sort, slowsort or bozosort) is a sorting algorithm based on the generate and test paradigm. The function successively generates permutations of its input until it finds one that is sorted. It is not considered useful for sorting, but may be used for educational purposes, to contrast it with more efficient algorithms.
Expand All @@ -6,6 +24,18 @@ Two versions of this algorithm exist: a deterministic version that enumerates al

[bogosort-wiki]:https://en.wikipedia.org/wiki/Bogosort

### [Bogo-Bogo-sort](./bogo_bogo_sort.rs)
From [leonardini.dev][bogo-bogo-doc]: BogoBogo Sort is a humorously inefficient sorting
algorithm inspired by the original Bogosort. It adds a layer of complexity by recursively
sorting the first n-1 elements before placing the nth element. This process is repeated until
the array is sorted. The algorithm's performance is exceptionally poor, making it impractical
for sorting but a useful tool for educational purposes, especially in understanding
algorithm efficiency and recursive functions.
__Properties__
* Worst case performance (unbounded, extremely poor)
* Best case performance O(n!)
* Average case performance (unpredictable and impractical)

### [Bucket_Sort](./bucket_sort.rs)

From [Wikipedia][bucketsort-wiki]: Bucket sort, or bin sort, is a sorting algorithm that works by distributing the elements of an array into a number of buckets. Each bucket is then sorted individually, either using a different sorting algorithm, or by recursively applying the bucket sorting algorithm. It is a distribution sort, a generalization of pigeonhole sort that allows multiple keys per bucket, and is a cousin of radix sort in the most-to-least significant digit flavor. Bucket sort can be implemented with comparisons and therefore can also be considered a comparison sort algorithm. The computational complexity depends on the algorithm used to sort each bucket, the number of buckets to use, and whether the input is uniformly distributed.
Expand Down Expand Up @@ -238,5 +268,59 @@ __Sources to read:__
* [Geeksforgeeks](https://www.geeksforgeeks.org/insertion-sort/)
* [Programiz](https://www.programiz.com/dsa/insertion-sort)

### [Strand Sort](./strand_sort.rs)

Strand Sort is a sorting algorithm that works by repeatedly pulling sorted sublists out of the list to be sorted and merging them with the already sorted part. It is particularly effective for sorting lists where there are large numbers of ordered elements. The algorithm is intuitive and simple, iterating through the list, picking up elements in order, and merging these 'strands' into a final sorted list.

* Simplicity: The algorithm is straightforward and easy to implement, making it a good choice for introductory sorting algorithm education.
* Adaptive: Strand sort performs well on lists that are already partially sorted, as it can quickly identify and extract these ordered sublists.
* In-place Sorting: The nature of the algorithm allows it to be implemented in an in-place fashion, although this is not always the case.

![Alt text](image-7.png)

__Properties__
* Not-in-place Sorting: Typically, strand sort requires additional space for the strands, though in-place variants exist.
* Time Complexity: The average and worst-case time complexity of strand sort can vary greatly depending on the input but typically is O(n²).
* Stability: The algorithm is stable, maintaining the relative order of equal elements.

__Sources to read:__
* [Wikipedia](https://en.wikipedia.org/wiki/Strand_sort)
* [Geeksforgeeks](https://www.geeksforgeeks.org/strand-sort/)
* [Tutorialspoint](https://www.tutorialspoint.com/strand-sort-in-data-structure)

## Pigeonhole Sort Implementation in Rust

Pigeonhole Sort is a sorting algorithm that is efficient for sorting data with a known, limited range of key values. It works by creating an array of 'holes' (each representing a position in the range of the data) and then sorting the data by 'placing' each item into its corresponding hole. The algorithm then collects the items from each hole in order, resulting in a sorted array.

### Rust Implementation
The Rust implementation of Pigeonhole Sort involves the following steps:

* Initialization: First, it checks if the input array is empty. If so, it returns immediately as there's nothing to sort.

* Finding the Range: The implementation identifies the minimum and maximum values in the array. The range is the difference between these values plus one.

* Creating Holes: A vector of vectors (`Vec<Vec<i32>>`) is created to represent the holes. The size of this outer vector is equal to the range of the input data.

* Filling the Holes: The algorithm iterates over each element in the array, placing it into the corresponding hole based on its value.

* Reconstructing the Array: Finally, it iterates over the holes in order, placing each element back into the original array, now sorted.

### [Tree Sort](./tree_sort.rs)

Tree Sort is a sorting algorithm that builds a binary search tree from the elements of the array to be sorted and then performs an in-order traversal to generate a sorted array. The essence of tree sort lies in leveraging the properties of a binary search tree, where elements are inserted in such a way that for any given node, all elements in the left subtree are less than the node and all elements in the right subtree are greater.

* Safety: Rust’s adherence to memory safety is a significant asset in the implementation of tree sort. The algorithm makes use of Rust's ownership and borrowing rules to manage the tree structure without the risk of memory leaks or dangling pointers.
* Tree Construction: Rust’s powerful data handling capabilities facilitate the efficient construction and manipulation of the binary search tree, a crucial aspect of tree sort.
* Performance: While not the most efficient in all cases, tree sort in Rust can be quite performant especially for datasets that are not large, thanks to Rust's optimization capabilities.

![Alt text](image-5.png)

__Properties__
* Not-in-place sorting: Tree sort requires additional memory for the binary search tree, hence it's not an in-place sorting algorithm.
* Time complexity: The average case time complexity of tree sort is O(n log n), but it can degrade to O(n²) in the worst case when the tree becomes unbalanced.

__Sources to read:__
* [Wikipedia](https://en.wikipedia.org/wiki/Tree_sort)
* [Geeksforgeeks](https://www.geeksforgeeks.org/tree-sort/)


85 changes: 85 additions & 0 deletions src/sorting/bitonic_sort.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
pub fn bitonic_sort(up: bool, x: &mut [i32]) {
if x.len() <= 1 {
return;
}

let mid = x.len() / 2;
let (first, second) = x.split_at_mut(mid);
bitonic_sort(true, first);
bitonic_sort(false, second);

bitonic_merge(up, x);
}

pub struct BitonicSort;

fn bitonic_merge(up: bool, x: &mut [i32]) {
if x.len() <= 1 {
return;
}

let mid = x.len() / 2;
for i in 0..mid {
if up == (x[i] > x[mid + i]) {
x.swap(i, mid + i);
}
}

let (first, second) = x.split_at_mut(mid);
bitonic_merge(up, first);
bitonic_merge(up, second);
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_bitonic_sort() {
let mut numbers = vec![10, 30, 11, 20, 4, 330, 21, 110];
bitonic_sort(true, &mut numbers);
assert_eq!(numbers, vec![4, 10, 11, 20, 21, 30, 110, 330]);
}

#[test]
fn test_bitonic_sort_empty() {
let mut numbers = vec![];
bitonic_sort(true, &mut numbers);
assert_eq!(numbers, vec![]);
}

#[test]
fn test_bitonic_sort_one_element() {
let mut numbers = vec![10];
bitonic_sort(true, &mut numbers);
assert_eq!(numbers, vec![10]);
}

#[test]
fn test_bitonic_sort_two_elements() {
let mut numbers = vec![10, 30];
bitonic_sort(true, &mut numbers);
assert_eq!(numbers, vec![10, 30]);
}

#[test]
fn test_error_bitonic_sort() {
let mut numbers = vec![10, 30, 11, 20, 4, 330, 21, 110];
bitonic_sort(true, &mut numbers);
assert_ne!(numbers, vec![10, 4, 11, 20, 21, 30, 110, 330]);
}

#[test]
fn test_bitonic_merge_empty() {
let mut numbers = vec![];
bitonic_merge(true, &mut numbers);
assert_eq!(numbers, vec![]);
}

#[test]
fn test_bitonic_merge_one_element() {
let mut numbers = vec![10];
bitonic_merge(true, &mut numbers);
assert_eq!(numbers, vec![10]);
}
}
61 changes: 61 additions & 0 deletions src/sorting/bogo_bogo_sort.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use rand::seq::SliceRandom;
use rand::thread_rng;

fn is_sorted<T: Ord>(arr: &[T]) -> bool {
arr.windows(2).all(|w| w[0] <= w[1])
}

pub struct BogoBogoSort;

pub fn bogo_bogo_sort<T: Ord + Clone>(arr: &[T]) -> Vec<T> {
if arr.len() <= 1 {
return arr.to_vec();
}

let mut rng = thread_rng();
let mut sorted_subarray = bogo_bogo_sort(&arr[..arr.len() - 1]);

let mut extended_array = sorted_subarray.clone();
extended_array.push(arr[arr.len() - 1].clone());

while !is_sorted(&extended_array)
|| extended_array[arr.len() - 1] < *sorted_subarray.iter().max().unwrap()
{
extended_array.shuffle(&mut rng);
sorted_subarray = bogo_bogo_sort(&extended_array[..arr.len() - 1]);
extended_array = sorted_subarray.clone();
extended_array.push(arr[arr.len() - 1].clone());
}

extended_array
}

#[cfg(test)]
mod tests {
use super::*;
use crate::sorting::BogoSort;

#[test]
fn test_sorted_array() {
let arr = vec![1, 2, 3, 4, 5];
assert!(is_sorted(&bogo_bogo_sort(&arr)));
}

#[test]
fn test_reverse_sorted_array() {
let arr = vec![5, 4, 3, 2, 1];
assert!(is_sorted(&bogo_bogo_sort(&arr)));
}

#[test]
fn test_unsorted_array() {
let arr = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];
assert!(is_sorted(&bogo_bogo_sort(&arr)));
}

#[test]
fn test_empty_array() {
let arr: Vec<i32> = vec![];
assert!(is_sorted(&bogo_bogo_sort(&arr)));
}
}
10 changes: 10 additions & 0 deletions src/sorting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ where
}

mod bingo_sort;
mod bitonic_sort;
mod bogo_bogo_sort;
mod bogo_sort;
mod bubble_sort;
mod bucket_sort;
Expand All @@ -35,18 +37,23 @@ mod insertion_sort;
mod merge_sort;
mod odd_even_sort;
mod pancake_sort;
mod pigeonhole_sort;
mod quick_sort;
mod radix_sort;
mod selection_sort;
mod shell_sort;
mod sleep_sort;
mod stooge_sort;
mod strand_sort;
mod tim_sort;
mod traits;
mod tree_sort;

use std::fmt;

pub use self::bingo_sort::bingo_sort;
pub use self::bitonic_sort::bitonic_sort;
pub use self::bogo_bogo_sort::BogoBogoSort;
pub use self::bogo_sort::BogoSort;
pub use self::bubble_sort::BubbleSort;
pub use self::bucket_sort::BucketSort;
Expand All @@ -61,13 +68,16 @@ pub use self::insertion_sort::InsertionSort;
pub use self::merge_sort::MergeSort;
pub use self::odd_even_sort::OddEvenSort;
pub use self::pancake_sort::PancakeSort;
pub use self::pigeonhole_sort::pigeonhole_sort;
pub use self::quick_sort::QuickSort;
pub use self::radix_sort::RadixSort;
pub use self::selection_sort::SelectionSort;
pub use self::shell_sort::ShellSort;
pub use self::sleep_sort::sleep_sort;
pub use self::stooge_sort::StoogeSort;
pub use self::strand_sort::strand_sort;
pub use self::tim_sort::TimSort;
pub use self::tree_sort::TreeSort;

#[cfg(test)]
mod tests {
Expand Down
Loading