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

feat: add quick sort #242

Merged
merged 5 commits into from
Jan 23, 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
3 changes: 3 additions & 0 deletions Scarb.lock
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ dependencies = [
[[package]]
name = "alexandria_sorting"
version = "0.1.0"
dependencies = [
"alexandria_data_structures",
]

[[package]]
name = "alexandria_storage"
Expand Down
10 changes: 10 additions & 0 deletions src/sorting/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Sorting

## [Quick Sort](./src/quick_sort.cairo)

The Quick Sort algorithm is an efficient sorting algorithm used to sort a list of elements. It operates by selecting a 'pivot' element from the array and partitioning the other elements into two sub-arrays, according to whether they are less than or greater than the pivot. The sub-arrays are then recursively sorted.

The purpose of the Quick Sort algorithm is to provide an efficient and practical sorting method. It has an average time complexity of O(n log n), which makes it more efficient for larger lists compared to algorithms like Bubble Sort. However, its worst-case scenario can still reach O(n^2), although this situation is rare, especially if the pivot is chosen wisely.

The time complexity of the Quick Sort algorithm on average is O(n log n), but in the worst-case scenario, it can go up to O(n^2).
The space complexity of the algorithm is O(log n) due to the stack space required for recursion.
The Quick Sort algorithm is considered one of the more efficient sorting algorithms, especially for larger lists. Its efficiency and low space complexity make it a popular choice for many sorting tasks, despite the potential for a high time complexity in the worst-case scenario.


## [Bubble Sort](./src/bubble_sort.cairo)

Expand Down
3 changes: 3 additions & 0 deletions src/sorting/Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ homepage = "https://github.com/keep-starknet-strange/alexandria/tree/src/sorting

[tool]
fmt.workspace = true

[dependencies]
alexandria_data_structures = { path = "../data_structures" }
28 changes: 28 additions & 0 deletions src/sorting/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
mod bubble_sort;
mod merge_sort;
mod quick_sort;

#[cfg(test)]
mod tests;
use alexandria_data_structures::vec::{Felt252Vec, VecTrait};

// Check if two arrays are equal.
/// * `a` - The first array.
Expand All @@ -25,3 +27,29 @@ fn is_equal(mut a: Span<u32>, mut b: Span<u32>) -> bool {
};
}
}


/// * `a` - The first Felt252Vec.
/// * `b` - The second array.
/// # Returns
/// * `bool` - True if the arrays are equal, false otherwise.
fn is_equal_vec(mut a: Felt252Vec<u32>, mut b: Span<u32>) -> bool {
if a.len() != b.len() {
return false;
}

let mut compare_id = 0;
let mut is_equal = true;
loop {
if compare_id == b.len() {
break;
}

if a[compare_id] != *b[compare_id] {
is_equal = false;
}

compare_id += 1;
};
is_equal
}
67 changes: 67 additions & 0 deletions src/sorting/src/quick_sort.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//! Quick sort algorithm
use alexandria_data_structures::vec::{Felt252Vec, VecTrait};
Soptq marked this conversation as resolved.
Show resolved Hide resolved

// Quick sort
/// # Arguments
/// * `Felt252Vec<T>` - Array to sort
/// # Returns
/// * `Felt252Vec<T>` - Sorted array
fn quick_sort<T, +Copy<T>, +Drop<T>, +PartialOrd<T>, +PartialEq<T>, +Felt252DictValue<T>>(
mut array: Felt252Vec<T>
) -> Felt252Vec<T> {
let array_size = array.len();
if array_size <= 1 {
return array;
}
quick_sort_range(ref array, 0, array_size - 1);

return array;
}


fn quick_sort_range<T, +Copy<T>, +Drop<T>, +PartialOrd<T>, +PartialEq<T>, +Felt252DictValue<T>>(
ref array: Felt252Vec<T>, left: usize, right: usize
) {
if left >= right {
return;
}

let mut l = left;
let mut r = right;

loop {
if l >= r {
break;
}

loop {
if (l >= r) || (array[r] < array[left]) {
break;
}
r -= 1;
};

loop {
if (l >= r) || (array[l] > array[left]) {
break;
}
l += 1;
};

if left != right {
let tmp = array[l];
array.set(l, array[r]);
array.set(r, tmp);
}
};

let tmp = array[left];
array.set(left, array[l]);
array.set(l, tmp);

if l > 1 {
quick_sort_range(ref array, left, l - 1);
}

quick_sort_range(ref array, r + 1, right);
}
1 change: 1 addition & 0 deletions src/sorting/src/tests.cairo
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
mod bubble_sort_test;
mod merge_sort_test;
mod quick_sort_test;
105 changes: 105 additions & 0 deletions src/sorting/src/tests/quick_sort_test.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use alexandria_data_structures::vec::{Felt252Vec, VecTrait};
use alexandria_sorting::{is_equal, is_equal_vec, quick_sort};
use core::option::OptionTrait;


#[test]
#[available_gas(20000000000000)]
fn quicksort_test() {
let mut data = VecTrait::<Felt252Vec, u32>::new();
data.push(4_u32);
data.push(2_u32);
data.push(1_u32);
data.push(3_u32);
data.push(5_u32);
data.push(0_u32);
let mut correct = array![0_u32, 1_u32, 2_u32, 3_u32, 4_u32, 5_u32];

let mut sorted = quick_sort::quick_sort(data);

assert(is_equal_vec(sorted, correct.span()), 'invalid result');
}


#[test]
#[available_gas(2000000)]
fn quicksort_test_empty() {
let mut data = VecTrait::<Felt252Vec, u32>::new();
let mut correct = array![];

let mut sorted = quick_sort::quick_sort(data);

assert(is_equal_vec(sorted, correct.span()), 'invalid result');
}

#[test]
#[available_gas(2000000)]
fn quicksort_test_one_element() {
let mut data = VecTrait::<Felt252Vec, u32>::new();
data.push(2_u32);
let mut correct = array![2_u32];

let mut sorted = quick_sort::quick_sort(data);

assert(is_equal_vec(sorted, correct.span()), 'invalid result');
}

#[test]
#[available_gas(2000000)]
fn quicksort_test_pre_sorted() {
let mut data = VecTrait::<Felt252Vec, u32>::new();
data.push(1_u32);
data.push(2_u32);
data.push(3_u32);
data.push(4_u32);
let mut correct = array![1_u32, 2_u32, 3_u32, 4_u32];

let mut sorted = quick_sort::quick_sort(data);

assert(is_equal_vec(sorted, correct.span()), 'invalid result');
}

#[test]
#[available_gas(2000000)]
fn quicksort_test_pre_sorted_decreasing() {
let mut data = VecTrait::<Felt252Vec, u32>::new();
data.push(4_u32);
data.push(3_u32);
data.push(2_u32);
data.push(1_u32);
let mut correct = array![1_u32, 2_u32, 3_u32, 4_u32];

let mut sorted = quick_sort::quick_sort(data);

assert(is_equal_vec(sorted, correct.span()), 'invalid result');
}

#[test]
#[available_gas(2000000)]
fn quicksort_test_pre_sorted_2_same_values() {
let mut data = VecTrait::<Felt252Vec, u32>::new();
data.push(1_u32);
data.push(2_u32);
data.push(2_u32);
data.push(4_u32);
let mut correct = array![1_u32, 2_u32, 2_u32, 4_u32];

let mut sorted = quick_sort::quick_sort(data);

assert(is_equal_vec(sorted, correct.span()), 'invalid result');
}

#[test]
#[available_gas(2000000)]
fn quicksort_test_2_same_values() {
let mut data = VecTrait::<Felt252Vec, u32>::new();
data.push(1_u32);
data.push(2_u32);
data.push(4_u32);
data.push(2_u32);
let mut correct = array![1_u32, 2_u32, 2_u32, 4_u32];

let mut sorted = quick_sort::quick_sort(data);

assert(is_equal_vec(sorted, correct.span()), 'invalid result');
}