Data update
This commit is contained in:
parent
72eb4943cb
commit
4d5544505c
2347 changed files with 62432 additions and 16731 deletions
|
|
@ -0,0 +1,121 @@
|
|||
fn bubble_sort<T: Ord>(arr: &mut [T]) {
|
||||
let mut n = arr.len();
|
||||
let mut swapped = true;
|
||||
|
||||
while swapped {
|
||||
swapped = false;
|
||||
|
||||
for i in 1..n {
|
||||
if arr[i - 1] > arr[i] {
|
||||
arr.swap(i - 1, i);
|
||||
swapped = true;
|
||||
}
|
||||
}
|
||||
|
||||
n -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn insertion_sort<T: Ord>(arr: &mut [T]) {
|
||||
for i in 1..arr.len() {
|
||||
let mut j = i;
|
||||
while j > 0 && arr[j] < arr[j - 1] {
|
||||
arr.swap(j, j - 1);
|
||||
j -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn quick_sort<T: Ord + Copy>(arr: &mut [T]) {
|
||||
if arr.len() >= 2 {
|
||||
let pivot = partition(arr);
|
||||
quick_sort(&mut arr[..pivot]);
|
||||
quick_sort(&mut arr[pivot..]);
|
||||
}
|
||||
}
|
||||
|
||||
/// In order to sort 100,000 integers without overflowing the stack, we use Hoare's partition scheme
|
||||
/// which moves iterators from both ends of the slice towards the center until they cross.
|
||||
/// This is especially effective for slicing sequences that are nearly/fully sorted.
|
||||
fn partition<T: Ord + Copy>(arr: &mut [T]) -> usize {
|
||||
let pivot = arr[arr.len() / 2];
|
||||
|
||||
let mut i = 0;
|
||||
let mut j = arr.len() - 1;
|
||||
|
||||
loop {
|
||||
while arr[i] < pivot {
|
||||
i += 1;
|
||||
}
|
||||
while arr[j] > pivot {
|
||||
j -= 1;
|
||||
}
|
||||
|
||||
if i >= j {
|
||||
return i;
|
||||
}
|
||||
|
||||
arr.swap(i, j);
|
||||
i += 1;
|
||||
j -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// We are hard-coding the type for this example, since radix sorting on signed integers is a _bit_
|
||||
/// more complicated than needed here.
|
||||
fn radix_sort(arr: &mut [u32]) {
|
||||
let mut buf = vec![0; arr.len()];
|
||||
// Reduce the amount of iterations by finding the max value bit
|
||||
let max_bits = u32::BITS - arr.iter().max().unwrap().leading_zeros();
|
||||
|
||||
for bit in 0..max_bits {
|
||||
// Manual implementation of partition with only one buffer array
|
||||
let mut a = 0;
|
||||
let mut b = 0;
|
||||
|
||||
for i in 0..arr.len() {
|
||||
if arr[i] >> bit & 1 == 0 {
|
||||
arr[a] = arr[i];
|
||||
a += 1;
|
||||
} else {
|
||||
buf[b] = arr[i];
|
||||
b += 1;
|
||||
}
|
||||
}
|
||||
|
||||
arr[a..].copy_from_slice(&buf[..b]);
|
||||
}
|
||||
}
|
||||
|
||||
fn shell_sort<T: Ord + Copy>(arr: &mut [T]) {
|
||||
// Marcin Ciura's gap sequence
|
||||
for gap in [701, 301, 132, 57, 23, 10, 4, 1] {
|
||||
for i in gap..arr.len() {
|
||||
let temp = arr[i];
|
||||
let mut j = i;
|
||||
while j >= gap && arr[j - gap] > temp {
|
||||
arr[j] = arr[j - gap];
|
||||
j -= gap;
|
||||
}
|
||||
arr[j] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rust_stable_sort<T: Ord>(arr: &mut [T]) {
|
||||
arr.sort();
|
||||
}
|
||||
|
||||
fn rust_unstable_sort<T: Ord>(arr: &mut [T]) {
|
||||
arr.sort_unstable();
|
||||
}
|
||||
|
||||
const ALGOS: [(&'static str, fn(&mut [u32])); 7] = [
|
||||
("Bubble", bubble_sort),
|
||||
("Insert", insertion_sort),
|
||||
("Quick", quick_sort),
|
||||
("Radix", radix_sort),
|
||||
("Shell", shell_sort),
|
||||
("Drift", rust_stable_sort),
|
||||
("IPN", rust_unstable_sort),
|
||||
];
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
fn ones(len: u32) -> Vec<u32> {
|
||||
vec![1; len as usize]
|
||||
}
|
||||
|
||||
fn sequence(len: u32) -> Vec<u32> {
|
||||
(1..=len).collect()
|
||||
}
|
||||
|
||||
fn random(mut rng: &mut rand::rngs::ThreadRng, len: u32) -> Vec<u32> {
|
||||
use rand::seq::SliceRandom;
|
||||
|
||||
let mut seq = sequence(len);
|
||||
seq.shuffle(&mut rng);
|
||||
|
||||
seq
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
use core::error::Error;
|
||||
use core::time::Duration;
|
||||
use std::collections::BTreeMap;
|
||||
use std::io::{Write, stdout};
|
||||
use std::time::Instant;
|
||||
|
||||
fn run_time(f: fn(&mut [u32]), arr: &mut [u32]) -> Duration {
|
||||
let start_time = Instant::now();
|
||||
f(arr);
|
||||
start_time.elapsed()
|
||||
}
|
||||
|
||||
const LENGTHS: [u32; 30] = [
|
||||
1, 2, 3, 4, 6, 10, 14, 21, 31, 46, 68, 100, 146, 215, 316, 464, 681, 1000, 1467, 2154, 3162,
|
||||
4641, 6812, 10000, 14677, 21544, 31622, 46415, 68129, 100000,
|
||||
];
|
||||
|
||||
const IDC: [usize; 6] = [0, 5, 11, 17, 23, 29];
|
||||
|
||||
fn main() {
|
||||
let mut rng = rand::rng();
|
||||
let mut metrics: [_; 3] =
|
||||
core::array::from_fn(|_| BTreeMap::<&'static str, [Duration; 30]>::new());
|
||||
|
||||
for (i, len) in LENGTHS.iter().enumerate() {
|
||||
print!("{len} ");
|
||||
stdout().flush().unwrap();
|
||||
|
||||
let one = ones(*len);
|
||||
let seq = sequence(*len);
|
||||
let ran = random(&mut rng, *len);
|
||||
|
||||
for (algo_name, algo) in ALGOS {
|
||||
let (mut a, mut b, mut c) = (one.clone(), seq.clone(), ran.clone());
|
||||
|
||||
metrics[0].entry(algo_name).or_default()[i] = run_time(algo, &mut a);
|
||||
metrics[1].entry(algo_name).or_default()[i] = run_time(algo, &mut b);
|
||||
metrics[2].entry(algo_name).or_default()[i] = run_time(algo, &mut c);
|
||||
|
||||
debug_assert!(a.is_sorted());
|
||||
debug_assert!(b.is_sorted());
|
||||
debug_assert!(c.is_sorted());
|
||||
}
|
||||
}
|
||||
println!("...Done");
|
||||
|
||||
println!(
|
||||
"\n{:^20} {:>12?}",
|
||||
"Data sizes",
|
||||
[1, 10, 100, 1_000, 10_000, 100_000]
|
||||
);
|
||||
println!("\n{:-^20}", "All Ones");
|
||||
for (algo_name, run_times) in &mut metrics[0] {
|
||||
let powers_of_ten = run_times.get_disjoint_mut(IDC).unwrap();
|
||||
println!("{algo_name:^20} {powers_of_ten:>12?}");
|
||||
}
|
||||
println!("\n{:-^20}", "Ascending");
|
||||
for (algo_name, run_times) in &mut metrics[1] {
|
||||
let powers_of_ten = run_times.get_disjoint_mut(IDC).unwrap();
|
||||
println!("{algo_name:^20} {powers_of_ten:>12?}");
|
||||
}
|
||||
println!("\n{:-^20}", "Random");
|
||||
for (algo_name, run_times) in &mut metrics[2] {
|
||||
let powers_of_ten = run_times.get_disjoint_mut(IDC).unwrap();
|
||||
println!("{algo_name:^20} {powers_of_ten:>12?}");
|
||||
}
|
||||
|
||||
let _ = plot("one", &metrics[0]);
|
||||
let _ = plot("sequenced", &metrics[1]);
|
||||
let _ = plot("random", &metrics[2]);
|
||||
}
|
||||
|
||||
fn plot(data_type: &str, map: &BTreeMap<&str, [Duration; 30]>) -> Result<(), Box<dyn Error>> {
|
||||
use plotters::prelude::*;
|
||||
|
||||
let file_name = format!("{data_type}.png");
|
||||
let caption = format!("Sorting algorithms on {data_type} data");
|
||||
|
||||
let image = BitMapBackend::new(&file_name, (800, 600)).into_drawing_area();
|
||||
image.fill(&WHITE)?;
|
||||
|
||||
let root = image.margin(10, 10, 10, 10);
|
||||
let mut chart = ChartBuilder::on(&root)
|
||||
.caption(caption, ("sans-serif", 24).into_font())
|
||||
.x_label_area_size(20)
|
||||
.y_label_area_size(60)
|
||||
.build_cartesian_2d((0..100_000).log_scale(), (0..10_000_000_000).log_scale())?;
|
||||
|
||||
chart
|
||||
.configure_mesh()
|
||||
.x_desc("Data sizes")
|
||||
.y_desc("Sort time")
|
||||
.x_labels(10)
|
||||
.y_labels(10)
|
||||
.y_label_formatter(&|x| format!("{x:e} ns"))
|
||||
.draw()?;
|
||||
|
||||
for ((&algo_name, durations), color) in map
|
||||
.iter()
|
||||
.zip([MAGENTA, RED, GREEN, BLUE, YELLOW, CYAN, BLACK])
|
||||
{
|
||||
let coordinates = durations
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, duration)| (LENGTHS[i], duration.as_nanos() as i128));
|
||||
chart
|
||||
.draw_series(LineSeries::new(coordinates, color))?
|
||||
.label(algo_name)
|
||||
.legend(move |(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], color));
|
||||
}
|
||||
|
||||
chart
|
||||
.configure_series_labels()
|
||||
.background_style(WHITE.mix(0.8))
|
||||
.border_style(BLACK)
|
||||
.draw()?;
|
||||
|
||||
image.present()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue