-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMRGNormal.java
More file actions
147 lines (126 loc) · 5.24 KB
/
Copy pathMRGNormal.java
File metadata and controls
147 lines (126 loc) · 5.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import java.util.HashMap;
import java.util.Map;
/**
* A random number generator using the Multiple Recursive Generator (MRG)
* for uniform distribution, with optional transformation to normal distribution.
*/
public class MRGNormal {
// Constants
private static final long MODULUS = 4294967311L; // 2^32 + 15
private static final long[] COEFFICIENTS = {1664543L, 1013904223L, 1289L, 124897L};
// Seed Set Configuration
private static final Map<Integer, long[]> RANDOM_SEED_SET = new HashMap<>();
static {
RANDOM_SEED_SET.put(1, new long[]{11981, 4001, 1013, 1997});
RANDOM_SEED_SET.put(2, new long[]{11981, 4001, 1997, 1013});
RANDOM_SEED_SET.put(3, new long[]{11981, 1013, 4001, 1997});
RANDOM_SEED_SET.put(4, new long[]{11981, 1013, 1997, 4001});
RANDOM_SEED_SET.put(5, new long[]{11981, 1997, 4001, 1013});
RANDOM_SEED_SET.put(6, new long[]{11981, 1997, 1013, 4001});
RANDOM_SEED_SET.put(7, new long[]{4001, 11981, 1013, 1997});
RANDOM_SEED_SET.put(8, new long[]{4001, 11981, 1997, 1013});
RANDOM_SEED_SET.put(9, new long[]{4001, 1013, 11981, 1997});
RANDOM_SEED_SET.put(10, new long[]{4001, 1013, 1997, 11981});
RANDOM_SEED_SET.put(11, new long[]{4001, 1997, 11981, 1013});
RANDOM_SEED_SET.put(12, new long[]{4001, 1997, 1013, 11981});
RANDOM_SEED_SET.put(13, new long[]{1013, 11981, 4001, 1997});
RANDOM_SEED_SET.put(14, new long[]{1013, 11981, 1997, 4001});
RANDOM_SEED_SET.put(15, new long[]{1013, 4001, 11981, 1997});
RANDOM_SEED_SET.put(16, new long[]{1013, 4001, 1997, 11981});
RANDOM_SEED_SET.put(17, new long[]{1013, 1997, 11981, 4001});
RANDOM_SEED_SET.put(18, new long[]{1013, 1997, 4001, 11981});
RANDOM_SEED_SET.put(19, new long[]{1997, 11981, 4001, 1013});
RANDOM_SEED_SET.put(20, new long[]{1997, 11981, 1013, 4001});
RANDOM_SEED_SET.put(21, new long[]{1997, 4001, 11981, 1013});
RANDOM_SEED_SET.put(22, new long[]{1997, 4001, 1013, 11981});
RANDOM_SEED_SET.put(23, new long[]{1997, 1013, 11981, 4001});
RANDOM_SEED_SET.put(24, new long[]{1997, 1013, 4001, 11981});
}
// Instance Variables
private int seedKey;
private long[] seedList;
private double mean;
private double stdDev;
private double uniformLow;
private double uniformHigh;
private Double cachedNormal = null;
/**
* Constructor with all parameters.
* * @param seedKey Seed key from 1 to 24. If null (passed as 0 or negative), chosen based on time.
* @param mean Mean of the normal distribution.
* @param stdDev Standard deviation of the normal distribution.
* @param uniformLow Lower bound for the uniform distribution.
* @param uniformHigh Upper bound for the uniform distribution.
*/
public MRGNormal(Integer seedKey, double mean, double stdDev, double uniformLow, double uniformHigh) {
int numSeeds = RANDOM_SEED_SET.size();
// Handle auto-seeding if seedKey is null or invalid for manual selection
if (seedKey == null) {
this.seedKey = (int) (System.currentTimeMillis() % numSeeds) + 1;
} else {
if (seedKey < 1 || seedKey > numSeeds) {
throw new IllegalArgumentException("Invalid seed_key. Must be an integer between 1 and " + numSeeds + ".");
}
this.seedKey = seedKey;
}
// Clone the seed array to ensure independence
this.seedList = RANDOM_SEED_SET.get(this.seedKey).clone();
this.mean = mean;
this.stdDev = stdDev;
this.uniformLow = uniformLow;
this.uniformHigh = uniformHigh;
}
// Overloaded constructor for easier usage
public MRGNormal() {
this(null, 0.0, 1.0, 0.0, 1.0);
}
public MRGNormal(Integer seedKey) {
this(seedKey, 0.0, 1.0, 0.0, 1.0);
}
/**
* Internal MRG Step function.
* Updates the seed list and returns the new seed.
*/
private long mrgStep() {
long newSeed = 0;
// sum(a * x for a, x in zip(self.coefficients, self.seed_list))
for (int i = 0; i < COEFFICIENTS.length; i++) {
newSeed += COEFFICIENTS[i] * seedList[i];
}
newSeed %= MODULUS;
// self.seed_list = self.seed_list[1:] + [new_seed]
// Shift elements left and add newSeed at the end
System.arraycopy(seedList, 1, seedList, 0, seedList.length - 1);
seedList[seedList.length - 1] = newSeed;
return newSeed;
}
/**
* Generate a random number uniformly distributed in [uniform_low, uniform_high].
*
* @return A uniform random number.
*/
public double randomUniform() {
double raw = (mrgStep() + 1.0) / (MODULUS + 1.0);
return this.uniformLow + raw * (this.uniformHigh - this.uniformLow);
}
/**
* Generate a random number from a normal distribution with specified mean and std_dev.
* Uses the Box-Muller transform.
*
* @return A normal random number.
*/
public double randomNormal() {
if (this.cachedNormal != null) {
double result = this.cachedNormal;
this.cachedNormal = null;
return result;
}
double u1 = (mrgStep() + 1.0) / (MODULUS + 1.0);
double u2 = (mrgStep() + 1.0) / (MODULUS + 1.0);
// Box-Muller transform
double z1 = Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * Math.PI * u2);
double z2 = Math.sqrt(-2.0 * Math.log(u1)) * Math.sin(2.0 * Math.PI * u2);
this.cachedNormal = z2 * this.stdDev + this.mean;
return z1 * this.stdDev + this.mean;
}
}