2026-01-21 10:56:16 -05:00

143 lines
4.2 KiB
Rust

use statrs::statistics::{Data, Distribution};
use solutions::{RandomGenerator, Pcg32};
use plotters::prelude::*;
fn plot_histogram(
samples: &[f64],
filename: &str,
title: &str,
bins: usize,
) -> Result<(), Box<dyn std::error::Error>> {
// Create histogram bins
let mut counts = vec![0u32; bins];
for &sample in samples {
let bin = ((sample * bins as f64).floor() as usize).min(bins - 1);
counts[bin] += 1;
}
let max_count = *counts.iter().max().unwrap() as f64;
// Set up the drawing area
let root = BitMapBackend::new(filename, (800, 600)).into_drawing_area();
root.fill(&WHITE)?;
let mut chart = ChartBuilder::on(&root)
.caption(title, ("sans-serif", 30))
.margin(10)
.x_label_area_size(40)
.y_label_area_size(50)
.build_cartesian_2d(0.0..1.0, 0.0..(max_count * 1.1))?;
chart
.configure_mesh()
.x_desc("Value")
.y_desc("Count")
.draw()?;
// Draw histogram bars
chart.draw_series(
counts.iter().enumerate().map(|(i, &count)| {
let x0 = i as f64 / bins as f64;
let x1 = (i + 1) as f64 / bins as f64;
Rectangle::new([(x0, 0.0), (x1, count as f64)], BLUE.mix(0.6).filled())
}),
)?;
root.present()?;
println!("Histogram saved to: {}", filename);
Ok(())
}
fn plot_3d_scatter(
samples: &[f64],
filename: &str,
title: &str,
max_points: usize,
) -> Result<(), Box<dyn std::error::Error>> {
// Create triplets from consecutive samples
let triplets: Vec<(f64, f64, f64)> = samples
.windows(3)
.step_by(1)
.take(max_points)
.map(|w| (w[0], w[1], w[2]))
.collect();
// Set up the drawing area
let root = BitMapBackend::new(filename, (1024, 768)).into_drawing_area();
root.fill(&WHITE)?;
let mut chart = ChartBuilder::on(&root)
.caption(title, ("sans-serif", 30))
.margin(10)
.build_cartesian_3d(0.0..1.0, 0.0..1.0, 0.0..1.0)?;
// Rotate the view
chart.with_projection(|mut pb| {
pb.pitch = 0.8;
pb.yaw = 0.5;
pb.scale = 0.9;
pb.into_matrix()
});
chart.configure_axes().draw()?;
// Draw the points
chart.draw_series(
triplets
.iter()
.map(|&(x, y, z)| Circle::new((x, y, z), 2, BLUE.filled())),
)?;
root.present()?;
println!("Plot saved to: {}", filename);
Ok(())
}
fn main() {
println!("PROBLEM 3: PCG32 (Permuted Congruential Generator)");
println!("===================================================");
println!("PCG32 is a modern PRNG with excellent statistical properties.");
println!("It uses a 64-bit LCG internally with output permutation (XSH-RR).\n");
let seed = 42; // Classic seed choice!
let mut pcg = Pcg32::new(seed);
println!("Seed: {}\n", seed);
// Generate first few samples to show output
println!("First 5 random numbers:");
for i in 0..5 {
println!(" Random Number {}: {:.10}", i, pcg.next_uniform());
}
// Generate large number of samples for statistics
let n = 100_000;
println!("\nGenerating {} samples for analysis...", n);
let samples = pcg.generate_samples(n);
let data = Data::new(samples.clone());
// Calculate statistics
let mean = data.mean().unwrap();
let std_dev = data.std_dev().unwrap();
println!("\nStatistics:");
println!(" Mean: {:.6} (expected: 0.5)", mean);
println!(" Std Dev: {:.6} (expected: {:.6})", std_dev, (1.0/12.0_f64).sqrt());
// Generate histogram
println!("\nGenerating histogram...");
plot_histogram(&samples, "pcg32_histogram.png", "PCG32 - Histogram", 50)
.expect("Failed to create histogram");
// Generate 3D scatter plot
println!("Generating 3D scatter plot...");
let plot_samples = pcg.generate_samples(30000);
plot_3d_scatter(&plot_samples, "pcg32_3d.png", "PCG32 - 3D Scatter", 10000)
.expect("Failed to create 3D scatter plot");
println!("\nDone! Check the generated PNG files.");
println!("\nNote: PCG32 should show excellent uniformity and no visible");
println!("correlation patterns in the 3D plot - much better than basic LCGs!");
}