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> { // 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> { // 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!"); }