Data update

This commit is contained in:
Ingy döt Net 2025-06-11 20:16:52 -04:00
parent 72eb4943cb
commit 4d5544505c
2347 changed files with 62432 additions and 16731 deletions

View file

@ -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),
];

View file

@ -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
}

View file

@ -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(())
}