MathgeomGLS
302 строки · 8.7 Кб
1{---------------------------------------------------------------------------}
2{ }
3{ File: Velthuis.RandomNumbers.pas }
4{ Function: Simple random number generators. }
5{ Language: Delphi version XE3 or later }
6{ Author: Rudy Velthuis }
7{ Copyright: (c) 2016 Rudy Velthuis }
8{ }
9{ License: Redistribution and use in source and binary forms, with or }
10{ without modification, are permitted provided that the }
11{ following conditions are met: }
12{ }
13{ * Redistributions of source code must retain the above }
14{ copyright notice, this list of conditions and the following }
15{ disclaimer. }
16{ * Redistributions in binary form must reproduce the above }
17{ copyright notice, this list of conditions and the following }
18{ disclaimer in the documentation and/or other materials }
19{ provided with the distribution. }
20{ }
21{ Disclaimer: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" }
22{ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT }
23{ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND }
24{ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO }
25{ EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE }
26{ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, }
27{ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, }
28{ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, }
29{ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED }
30{ AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT }
31{ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) }
32{ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF }
33{ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. }
34{ }
35{---------------------------------------------------------------------------}
36
37unit Velthuis.RandomNumbers;
38
39// TODO: streamline this.
40// TODO: better random number algorithms
41
42interface
43
44type
45IRandom = interface
46function NextInteger: Integer; overload;
47function NextInteger(MaxValue: Integer): Integer; overload;
48function NextInteger(MinValue, MaxValue: Integer): Integer; overload;
49function NextDouble: Double;
50function NextInt64: Int64;
51procedure NextBytes(var Bytes: array of Byte);
52procedure SetSeed(Seed: Int64);
53function GetSeed: Int64;
54property Seed: Int64 read GetSeed write SetSeed;
55end;
56
57/// <summary>Base for 32 bit random number generators implementing IRandom</summary>
58TRandomBase = class(TInterfacedObject, IRandom)
59protected
60// Abstract. Must be overridden.
61function Next(Bits: Integer): UInt32; virtual; abstract;
62procedure SetSeed(ASeed: Int64); virtual; abstract;
63function GetSeed: Int64; virtual; abstract;
64public
65// Generates exception.
66constructor Create;
67
68// default implementations.
69function NextInteger: Integer; overload;
70function NextInteger(MaxValue: Integer): Integer; overload;
71function NextInteger(MinValue, MaxValue: Integer): Integer; overload;
72procedure NextBytes(var Bytes: array of Byte);
73function NextDouble: Double;
74function NextInt64: Int64;
75end;
76
77/// <summary>Base for 64 bit random number generators implementing IRandom</summary>
78TRandomBase64 = class(TRandomBase, IRandom)
79protected
80function Next64(Bits: Integer): UInt64; virtual; abstract;
81function Next(Bits: Integer): UInt32; override;
82public
83procedure NextBytes(var Bytes: array of Byte);
84function NextDouble: Double;
85function NextInt64: Int64;
86end;
87
88TRandom = class(TRandomBase, IRandom)
89private
90FSeed: Int64; // Only 48 bits are used.
91protected
92function Next(Bits: Integer): UInt32; override;
93procedure SetSeed(ASeed: Int64); override;
94function GetSeed: Int64; override;
95public
96constructor Create(Seed: Int64 = 0);
97end;
98
99TDelphiRandom = class(TRandomBase, IRandom)
100protected
101function Next(Bits: Integer): UInt32; override;
102procedure SetSeed(ASeed: Int64); override;
103function GetSeed: Int64; override;
104public
105constructor Create; overload;
106constructor Create(Seed: Int64); overload;
107end;
108
109implementation
110
111uses
112System.SysUtils, Velthuis.Numerics;
113
114{ TRandom }
115
116const
117CMultiplier = Int64(6364136223846793005);
118CIncrement = Int64(1442695040888963407);
119CSeedSize = 64 div 8;
120
121constructor TRandom.Create(Seed: Int64);
122begin
123FSeed := Seed;
124end;
125
126function TRandom.Next(Bits: Integer): UInt32;
127begin
128{$IFOPT Q+}
129{$DEFINE HasRangeChecks}
130{$ENDIF}
131FSeed := (FSeed * CMultiplier + CIncrement);
132Result := UInt32(FSeed shr (64 - Bits)); // Use the highest bits; Lower bits have lower period.
133{$IFDEF HasRangeChecks}
134{$RANGECHECKS ON}
135{$ENDIF}
136end;
137
138function TRandom.GetSeed: Int64;
139begin
140Result := FSeed;
141end;
142
143procedure TRandom.SetSeed(ASeed: Int64);
144begin
145FSeed := ASeed;
146end;
147
148{ TDelphiRandom }
149
150constructor TDelphiRandom.Create(Seed: Int64);
151begin
152System.RandSeed := Integer(Seed);
153end;
154
155constructor TDelphiRandom.Create;
156begin
157Randomize;
158end;
159
160function TDelphiRandom.GetSeed: Int64;
161begin
162Result := System.RandSeed;
163end;
164
165function TDelphiRandom.Next(Bits: Integer): UInt32;
166begin
167Result := UInt32(System.RandSeed) shr (32 - Bits);
168System.Random;
169end;
170
171procedure TDelphiRandom.SetSeed(ASeed: Int64);
172begin
173System.RandSeed := Integer(ASeed);
174end;
175
176{ TRandomBase }
177
178constructor TRandomBase.Create;
179begin
180raise EArgumentException.Create('Seed needs initialization');
181end;
182
183procedure TRandomBase.NextBytes(var Bytes: array of Byte);
184var
185Head, Tail: Integer;
186N, Rnd, I: Integer;
187begin
188Head := Length(Bytes) div SizeOf(Int32);
189Tail := Length(Bytes) mod SizeOf(Int32);
190N := 0;
191for I := 1 to Head do
192begin
193Rnd := Next(32);
194Bytes[N] := Byte(Rnd);
195Bytes[N + 1] := Byte(Rnd shr 8);
196Bytes[N + 2] := Byte(Rnd shr 16);
197Bytes[N + 3] := Byte(Rnd shr 24);
198Inc(N, 4);
199end;
200Rnd := Next(32);
201for I := 1 to Tail do
202begin
203Bytes[N] := Byte(Rnd);
204Rnd := Rnd shr 8;
205Inc(N);
206end;
207end;
208
209function TRandomBase.NextDouble: Double;
210const
211Divisor = UInt64(1) shl 53;
212begin
213Result := (UInt64(Next(26) shl 27) + Next(27)) / Divisor;
214end;
215
216function TRandomBase.NextInteger: Integer;
217begin
218Result := Next(32);
219end;
220
221function TRandomBase.NextInt64: Int64;
222begin
223Result := Int64(Next(32)) shl 32 + Next(32);
224end;
225
226function TRandomBase.NextInteger(MinValue, MaxValue: Integer): Integer;
227begin
228if MinValue < 0 then
229raise EArgumentException.Create('MinValue must be positive or 0');
230Result := MinValue + NextInteger(MaxValue - MinValue);
231end;
232
233function TRandomBase.NextInteger(MaxValue: Integer): Integer;
234var
235Bits: Integer;
236begin
237if MaxValue = 0 then
238raise EArgumentException.Create('MaxValue not be 0');
239
240if IsPowerOfTwo(MaxValue) then
241begin
242Bits := Next(31);
243Exit((Int64(MaxValue) * Bits) shr 31);
244end;
245
246repeat
247Bits := Next(31);
248Result := Bits mod MaxValue;
249until (Bits - Result + (MaxValue - 1) >= 0);
250end;
251
252{ TRandomBase64 }
253
254function TRandomBase64.Next(Bits: Integer): UInt32;
255begin
256Result := Next64(Bits + 32) shr 32;
257end;
258
259procedure TRandomBase64.NextBytes(var Bytes: array of Byte);
260var
261Head, Tail: Integer;
262N, I: Integer;
263Rnd: UInt64;
264begin
265Head := Length(Bytes) div SizeOf(UInt64);
266Tail := Length(Bytes) mod SizeOf(UInt64);
267N := 0;
268for I := 1 to Head do
269begin
270Rnd := Next64(64);
271Bytes[N] := Byte(Rnd);
272Bytes[N + 1] := Byte(Rnd shr 8);
273Bytes[N + 2] := Byte(Rnd shr 16);
274Bytes[N + 3] := Byte(Rnd shr 24);
275Bytes[N + 4] := Byte(Rnd shr 32);
276Bytes[N + 5] := Byte(Rnd shr 40);
277Bytes[N + 6] := Byte(Rnd shr 48);
278Bytes[N + 7] := Byte(Rnd shr 56);
279Inc(N, 8);
280end;
281Rnd := Next64(64);
282for I := 1 to Tail do
283begin
284Bytes[N] := Byte(Rnd);
285Rnd := Rnd shr 8;
286Inc(N);
287end;
288end;
289
290function TRandomBase64.NextDouble: Double;
291const
292Divisor = UInt64(1) shl 53;
293begin
294Result := Next64(53) / Divisor;
295end;
296
297function TRandomBase64.NextInt64: Int64;
298begin
299Result := Int64(Next64(64));
300end;
301
302end.
303
304
305
306
307