rand_simple/distributions/chi.rs
1use crate::create_state;
2use crate::standard_distributions::{standard_gamma, xorshift160_0_or_greater_and_less_than_1};
3
4/// Chi Distribution
5///
6/// This struct represents the Chi distribution, which is a special case of the gamma distribution
7/// with a shape parameter equal to half the degrees of freedom. It is useful in various statistical
8/// applications, including hypothesis testing and confidence interval estimation.
9///
10/// # Example
11///
12/// ```
13/// let mut chi = rand_simple::Chi::new([1192_u32, 765_u32, 1543_u32, 2003_u32]);
14///
15/// // Verify the initial state
16/// assert_eq!(format!("{chi}"), "χ(Degree of Freedom parameter) = χ(1)");
17///
18/// // Generate a random number from the Chi distribution with 1 degree of freedom
19/// println!("Initial setting: Random number from Chi distribution with 1 degree of freedom -> {}", chi.sample());
20///
21/// // Change the degrees of freedom
22/// let degree_of_freedom: u64 = 2_u64;
23/// let result: Result<u64, &str> = chi.try_set_params(degree_of_freedom);
24///
25/// // Verify the new state
26/// assert_eq!(format!("{chi}"), "χ(Degree of Freedom parameter) = χ(2)");
27///
28/// // Generate a random number from the Chi distribution with the updated degrees of freedom
29/// println!("Updated setting: Random number from Chi distribution with {} degrees of freedom -> {}", degree_of_freedom, chi.sample());
30/// ```
31///
32/// # Fields
33///
34/// * `xyzuv_u_gamma` - State variable for gamma distribution random number generation
35/// * `xyzuv_n_0_gamma` - State variable for gamma distribution random number generation
36/// * `xyzuv_n_1_gamma` - State variable for gamma distribution random number generation
37/// * `xyzuv_uniform` - State variable for uniform distribution random number generation
38/// * `degree_of_freedom` - Degrees of freedom, must be a positive integer
39///
40pub struct Chi {
41 xyzuv_u_gamma: [u32; 5], // State variable for gamma distribution random number generation
42 xyzuv_n_0_gamma: [u32; 5], // State variable for gamma distribution random number generation
43 xyzuv_n_1_gamma: [u32; 5], // State variable for gamma distribution random number generation
44
45 xyzuv_uniform: [u32; 5], // State variable for uniform distribution random number generation
46
47 degree_of_freedom: u64, // Degrees of freedom, must be a positive integer
48}
49
50impl Chi {
51 /// Constructor for the `Chi` struct
52 /// # Arguments
53 ///
54 /// * `seeds` - An array of 4 unsigned 32-bit integers used as seeds for the random number generators.
55 /// The seeds are adjusted to ensure they are unique and suitable for the random number generation.
56 pub fn new(seeds: [u32; 4_usize]) -> Self {
57 // Adjust the seeds to ensure they are suitable for the random number generation
58 let adjusted_seeds = crate::adjust_seeds!(seeds);
59
60 // Create a new instance of the Chi struct with the adjusted seeds
61 Self {
62 // Initialize the state variables for gamma distribution random number generation
63 xyzuv_u_gamma: create_state(adjusted_seeds[0]), // Seed for gamma distribution random number generator 1
64 xyzuv_n_0_gamma: create_state(adjusted_seeds[1]), // Seed for gamma distribution random number generator 2
65 xyzuv_n_1_gamma: create_state(adjusted_seeds[2]), // Seed for gamma distribution random number generator 3
66
67 // Initialize the state variable for uniform distribution random number generation
68 xyzuv_uniform: create_state(adjusted_seeds[3]), // Seed for uniform distribution random number generator
69
70 // Set the initial degrees of freedom
71 degree_of_freedom: 1_u64, // Default degrees of freedom
72 }
73 }
74
75 /// Generates a random number following the Chi distribution.
76 pub fn sample(&mut self) -> f64 {
77 // Step 1: Generate a random number Y following the Chi-squared distribution (χ^2(x))
78 let y = if self.degree_of_freedom > 1_u64 {
79 // If degrees of freedom > 1, generate a gamma distribution random number with the degrees of freedom as the shape parameter
80 standard_gamma(
81 &mut self.xyzuv_u_gamma,
82 &mut self.xyzuv_n_0_gamma,
83 &mut self.xyzuv_n_1_gamma,
84 &(self.degree_of_freedom as f64),
85 ) * 2_f64
86 } else {
87 // If degrees of freedom == 1, generate a random number using a specific method
88 let y = standard_gamma(
89 &mut self.xyzuv_u_gamma,
90 &mut self.xyzuv_n_0_gamma,
91 &mut self.xyzuv_n_1_gamma,
92 &(3_f64 / 2_f64),
93 ) * 2_f64;
94 let u = xorshift160_0_or_greater_and_less_than_1(&mut self.xyzuv_uniform); // Generate a uniform random number in the interval (0, 1)
95 u.powi(2) * y * 2_f64
96 };
97 // Step 2: Calculate X = √Y
98 y.sqrt() // Return the square root of Y as the final random number
99 }
100
101 /// Updates the parameters of the random variable.
102 /// # Parameters
103 ///
104 /// * `degree_of_freedom` - The degrees of freedom `r` (should be a natural number).
105 pub fn try_set_params(&mut self, degree_of_freedom: u64) -> Result<u64, &str> {
106 // Step 1: Validate the degrees of freedom
107 if degree_of_freedom < 1_u64 {
108 // If the degrees of freedom is less than 1, return an error message
109 Err("自由度は自然数である必要があります。確率変数のパラメータは前回の設定を維持します。")
110 } else {
111 // Step 2: Update the internal state with the new degrees of freedom
112 self.degree_of_freedom = degree_of_freedom;
113 // Return the updated degrees of freedom
114 Ok(degree_of_freedom)
115 }
116 }
117}
118
119impl core::fmt::Display for Chi {
120 /// Formatter for displaying in macros like println!
121 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
122 // Writes the formatted string to the formatter
123 write!(
124 f,
125 "χ(Degree of Freedom parameter) = χ({})",
126 self.degree_of_freedom
127 )?;
128 Ok(())
129 }
130}