rand_simple/distributions/t.rs
1use crate::create_state;
2use crate::standard_distributions::{
3 standard_cauchy, standard_exponential, standard_gamma, standard_normal,
4};
5
6/// t-distribution (Student's t-distribution)
7/// # Usage Example
8/// ```
9/// // Create a new TDistribution instance with the provided seeds.
10/// let mut t = rand_simple::TDistribution::new([1192u32, 765u32, 1543u32, 2003u32, 1867u32]);
11/// // Check if the default degree of freedom is correctly set to 1.
12/// assert_eq!(format!("{t}"), "T(Degree of Freedom parameter) = T(1)");
13/// // Generate and print a random number based on the t-distribution with 1 degree of freedom.
14/// println!("Generates a random number with degree of freedom 1 -> {}", t.sample());
15///
16/// // If you need to modify the parameters of the t-distribution (degree of freedom).
17/// let degree_of_freedom: u64 = 3_u64;
18/// // Set the new degree of freedom and check if the operation was successful.
19/// let result: Result<u64, &str> = t.try_set_params(degree_of_freedom);
20/// // Verify that the degree of freedom has been correctly updated to 3.
21/// assert_eq!(format!("{t}"), "T(Degree of Freedom parameter) = T(3)");
22/// // Generate and print a random number with the updated degree of freedom (3).
23/// println!("Generates a random number with degree of freedom {} -> {}", degree_of_freedom, t.sample());
24/// ```
25///
26/// This example demonstrates how to initialize a t-distribution instance, change the degree of freedom,
27/// and generate random numbers following the t-distribution.
28pub struct TDistribution {
29 xyzuv_n_0: [u32; 5], // 状態変数
30 xyzuv_n_1: [u32; 5], // 状態変数
31
32 xyzuv_u_gamma: [u32; 5], // 状態変数
33 xyzuv_n_0_gamma: [u32; 5], // 状態変数
34 xyzuv_n_1_gamma: [u32; 5], // 状態変数
35
36 degree_of_freedom: u64, // 自由度 r ∈ N
37}
38
39impl TDistribution {
40 /// Constructor for the `TDistribution` struct.
41 /// This method initializes a new instance of the t-distribution with the provided random seeds.
42 ///
43 /// # Arguments
44 /// * `seeds` - An array of 5 unsigned 32-bit integers (u32) that serve as seeds for generating the random numbers.
45 ///
46 /// The constructor processes the seeds through an adjustment function (`adjust_seeds!`)
47 /// and assigns them to internal state variables used for generating random numbers
48 /// that follow a t-distribution.
49 ///
50 /// The degree of freedom (DoF) is initialized to 1, but can be changed later via other methods.
51 ///
52 /// # Returns
53 /// A new instance of the `TDistribution` struct.
54 pub fn new(seeds: [u32; 5_usize]) -> Self {
55 let adjusted_seeds = crate::adjust_seeds!(seeds);
56
57 Self {
58 xyzuv_n_0: create_state(adjusted_seeds[0]),
59 xyzuv_n_1: create_state(adjusted_seeds[1]),
60
61 xyzuv_u_gamma: create_state(adjusted_seeds[2]),
62 xyzuv_n_0_gamma: create_state(adjusted_seeds[3]),
63 xyzuv_n_1_gamma: create_state(adjusted_seeds[4]),
64
65 degree_of_freedom: 1_u64,
66 }
67 }
68
69 /// Generates a random sample from the t-distribution based on the current degree of freedom (DoF).
70 ///
71 /// # Algorithm Description:
72 /// The function generates a random number following a t-distribution using different algorithms
73 /// depending on the value of the degree of freedom (DoF):
74 ///
75 /// - For `DoF = 1`: It generates a sample from the standard Cauchy distribution.
76 /// - For `DoF = 2`: It follows Algorithm 3.84, which uses a combination of normal and exponential
77 /// distributions to generate the sample.
78 /// - For `DoF > 2`: It follows Algorithm 3.85, using a combination of normal and gamma distributions
79 /// to generate the sample.
80 ///
81 /// # Returns
82 /// * `f64` - A floating-point value representing the generated random sample.
83 ///
84 pub fn sample(&mut self) -> f64 {
85 match self.degree_of_freedom as usize {
86 1_usize => standard_cauchy(&mut self.xyzuv_u_gamma),
87 2_usize => {
88 // アルゴリズム 3.84 r: = 2
89 // step 1
90 let mut z = 0_f64;
91 let mut w = 0_f64;
92 while w == 0_f64 {
93 z = standard_normal(&mut self.xyzuv_n_0, &mut self.xyzuv_n_1);
94 w = standard_exponential(&mut self.xyzuv_u_gamma);
95 }
96 // step 2
97 z / w.sqrt()
98 }
99 _ => {
100 // アルゴリズム 3.85: r > 2
101 // step 1
102 let z = standard_normal(&mut self.xyzuv_n_0, &mut self.xyzuv_n_1);
103 let w = standard_gamma(
104 &mut self.xyzuv_u_gamma,
105 &mut self.xyzuv_n_0_gamma,
106 &mut self.xyzuv_n_1_gamma,
107 &(self.degree_of_freedom as f64 / 2_f64),
108 );
109 // step 2
110 (self.degree_of_freedom as f64).sqrt() * z / w.sqrt()
111 }
112 }
113 }
114
115 /// Updates the degree of freedom parameter for the random variable.
116 ///
117 /// # Parameters:
118 /// * `degree_of_freedom` - The new degree of freedom to set, must be a natural number (r ≥ 1).
119 ///
120 /// # Returns:
121 /// * `Ok(u64)` - If the input `degree_of_freedom` is valid, returns the updated value.
122 /// * `Err(&str)` - If the input `degree_of_freedom` is invalid (r < 1), returns an error message.
123 ///
124 /// # Description:
125 /// This method is responsible for setting the degree of freedom (DoF) for the random variable.
126 /// The DoF must be a positive integer (natural number) to be valid. If the input value is less
127 /// than 1, the function will return an error message and maintain the previous DoF setting.
128 /// Otherwise, it updates the degree of freedom with the new valid value and returns it.
129 ///
130 /// # Example:
131 /// ```
132 /// let mut dist = rand_simple::TDistribution::new([1192u32, 765u32, 1543u32, 2003u32, 1867u32]);
133 /// let result = dist.try_set_params(3_u64);
134 /// assert_eq!(result.unwrap(), 3_u64); // Successfully updates the degree of freedom.
135 /// ```
136 pub fn try_set_params(&mut self, degree_of_freedom: u64) -> Result<u64, &str> {
137 // Check if the provided degree_of_freedom is a valid natural number (r ≥ 1).
138 if degree_of_freedom < 1_u64 {
139 // Return an error if the input is invalid, without changing the current parameter.
140 Err("The degree of freedom must be a natural number. The parameter remains unchanged.")
141 } else {
142 // Update the degree of freedom with the valid input and return the updated value.
143 self.degree_of_freedom = degree_of_freedom;
144 Ok(degree_of_freedom)
145 }
146 }
147}
148
149impl std::fmt::Display for TDistribution {
150 /// Formatter implementation for the TDistribution struct.
151 /// This allows the `println!` macro or other formatting macros
152 /// to display the t-distribution in a human-readable format.
153 ///
154 /// # Arguments
155 /// * `f` - A mutable reference to the formatter used by the `println!` macro.
156 ///
157 /// The implementation writes a formatted string representing the current state of the
158 /// `TDistribution` instance, specifically displaying the degree of freedom.
159 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
160 // Use the `write!` macro to format the output string, specifying the degree of freedom.
161 // This will output something like: "T(Degree of Freedom parameter) = T(3)"
162 write!(
163 f,
164 "T(Degree of Freedom parameter) = T({})",
165 self.degree_of_freedom
166 )?;
167 // Return `Ok(())` to indicate that the formatting was successful.
168 Ok(())
169 }
170}