How to implement a fast RNG? - delphi

I am trying to port an existing random generator based on 128 bit XorShift from C. But I have trouble with generating the seed which is just generating the same number again and again.
static uint64_t s[ 2 ];
static uint64_t __inline next(void) {
uint64_t s1 = s[ 0 ];
const uint64_t s0 = s[ 1 ];
s[ 0 ] = s0;
s1 ^= s1 << 23;
return ( s[ 1 ] = ( s1 ^ s0 ^ ( s1 >> 17 ) ^ ( s0 >> 26 ) ) ) + s0;
}
uint64_t getusertime() {
struct rusage rusage;
getrusage( 0, &rusage );
return rusage.ru_utime.tv_sec * 1000000ULL + ( rusage.ru_utime.tv_usec / 1000 ) * 1000;
}
int main( int argc, char* argv[] ) {
const long long int n = strtoll( argv[1], NULL, 0 );
uint64_t t = 0;
for( int i = 0; i < 2; i++ ) s[ i ] = -1ULL / 3;
const int64_t start = getusertime();
for( long long int i = n; i-- != 0; ) t ^= next();
const int64_t elapsed = getusertime() - start;
const double secs = elapsed / 1E6;
printf( "%f s, %.02f queries/s, %.02f ns/query\n", secs, n / secs, 1E9 * secs / n );
if ( t == 0 ) putchar( 0 );
return 0;
}
program Project1;
var
S: Array [0..1] of UInt64;
function XorShift128: UInt64;
var
s0, s1: UInt64;
begin
s1 := s[0];
s0 := s[1];
s[0] := s0;
s1 := s1 xor (s1 shl 23);
s[1] := (s1 xor s0 xor (s1 shr 17) xor (s0 shr 26));
Result := s[1] + s0;
end;
procedure GenerateSeed;
var
I: Integer;
begin
for I := 0 to High(S) do
S[I] := MaxLongInt div 3;
end;
var
I: UInt64;
begin
GenerateSeed;
I := XorShift128;
end.

The reason you get the same value every time you run the program in the question is that you use the same seed every time. If I am understanding your comments correctly. The other difference between the C and the Pascal is the seed – see below.
However, your code is fine and is an accurate translation of the C code. The output of this C program:
#include <stdio.h>
#include <stdint.h>
static uint64_t s[ 2 ];
static uint64_t __inline next(void) {
uint64_t s1 = s[ 0 ];
const uint64_t s0 = s[ 1 ];
s[ 0 ] = s0;
s1 ^= s1 << 23;
return ( s[ 1 ] = ( s1 ^ s0 ^ ( s1 >> 17 ) ^ ( s0 >> 26 ) ) ) + s0;
}
int main(void)
{
s[ 0 ] = s[ 1 ] = 715827882; // the value of MaxLongInt div 3
printf("%llu\n", next());
printf("%llu\n", next());
printf("%llu\n", next());
return 0;
}
is
6004846026386057
6004846115863870
12676181551404632061
The output of this Delphi program:
program Project1;
{$APPTYPE CONSOLE}
var
S: Array [0..1] of UInt64;
function XorShift128: UInt64;
var
s0, s1: UInt64;
begin
s1 := s[0];
s0 := s[1];
s[0] := s0;
s1 := s1 xor (s1 shl 23);
s[1] := (s1 xor s0 xor (s1 shr 17) xor (s0 shr 26));
Result := s[1] + s0;
end;
procedure GenerateSeed;
var
I: Integer;
begin
for I := 0 to High(S) do
S[I] := MaxLongInt div 3;
end;
begin
GenerateSeed;
Writeln(XorShift128);
Writeln(XorShift128);
Writeln(XorShift128);
end.
is
6004846026386057
6004846115863870
12676181551404632061
I note that the C code in the question uses a different seed from your translation. It seeds the state with -1ULL / 3 and that leads to this output:
46820872945684
46912499612351
13066320939010318272
To match that in the Delphi code you would use high(UInt64) div 3. Do that and you get the output above.
An important note here is that your Delphi code only supplies 64 bits of seed, but your C code supplies 128. I expect that you should supply 128 bits of seed.

Related

Is Delphi's Skewness correct

In Delphi one can calculate Skewness using System.Math's function MomentSkewKurtosis().
var m1, m2, m3, m4, skew, k: Extended;
System.Math.MomentSkewKurtosis([1.1,
3.345,
12.234,
11.945,
14.235,
16.876,
20.213,
11.001,
7.098,
21.234], m1, m2, m3, m4, skew, k);
This will prints skew equal to -0.200371489809269.
Minitab prints the value -0.24
SigmaXL prints the value -0.23611
The reason is that Delphi does not not perform adjustment.
Here is my implementation which calculates adjustment:
function Skewness(const X: array of Extended; const Adjusted: Boolean): Extended;
begin
var AMean := Mean(X);
var xi_minus_mean_power_3 := 0.0;
var xi_minus_mean_power_2 := 0.0;
for var i := Low(X) to High(X) do
begin
xi_minus_mean_power_3 := xi_minus_mean_power_3 + IntPower((X[i] - AMean), 3);
xi_minus_mean_power_2 := xi_minus_mean_power_2 + IntPower((X[i] - AMean), 2);
end;
// URL : https://www.gnu.org/software/octave/doc/v4.0.1/Descriptive-Statistics.html
{ mean ((x - mean (x)).^3)
skewness (X) = ------------------------.
std (x).^3
}
var N := Length(X);
Result := xi_minus_mean_power_3 / N /
IntPower(Sqrt(1 / N * xi_minus_mean_power_2), 3);
// URL : https://www.gnu.org/software/octave/doc/v4.0.1/Descriptive-Statistics.html
{ sqrt (N*(N-1)) mean ((x - mean (x)).^3)
skewness (X, 0) = -------------- * ------------------------.
(N - 2) std (x).^3
}
if Adjusted then
Result := Result * Sqrt(N * Pred(N)) / (N - 2);
end;
The helper routine IntPower is as follows:
function IntPower(const X: Extended; const N: Integer): Extended;
/// <remarks>
/// Calculate any float to non-negative integer power. Developed by Rory Daulton and used with permission. Last modified December 1998.
/// </remarks>
function IntPow(const Base: Extended; const Exponent: LongWord): Extended;
{ Heart of Rory Daulton's IntPower: assumes valid parameters &
non-negative exponent }
{$IFDEF WIN32}
asm
fld1 // Result := 1
cmp eax, 0 // eax := Exponent
jz ##3
fld Base
jmp ##2
##1: fmul ST, ST // X := Base * Base
##2: shr eax,1
jnc ##1
fmul ST(1),ST // Result := Result * X
jnz ##1
fstp st // pop X from FPU stack
##3:
fwait
end;
{$ENDIF}
{$IFDEF WIN64}
begin
Result := Power(Base, Exponent);
end;
{$ENDIF}
begin
if N = 0 then
Result := 1
else if (X = 0) then
begin
if N < 0 then
raise EMathError.Create('Zero cannot be raised to a negative power.')
else
Result := 0
end
else if (X = 1) then
Result := 1
else if X = -1 then
begin
if Odd (N) then
Result := -1
else
Result := 1
end
else if N > 0 then
Result := IntPow (X, N)
else
begin
var P: LongWord;
if N <> Low (LongInt) then
P := Abs(N)
else
P := LongWord(High(LongInt)) + 1;
try
Result := IntPow(X, P);
except
on EMathError do
begin
Result := IntPow(1 / X, P); // try again with another method, perhaps less precise
Exit;
end;
end;
Result := 1 / Result;
end;
end;
With that function the adjusted skewness becomes the accurate -0.237611357234441 matching Matlab and Minitab.
The only explanation I found is:
https://octave.org/doc/v4.0.1/Descriptive-Statistics.html
"The adjusted skewness coefficient is obtained by replacing the sample
second and third central moments by their bias-corrected versions."
Same goes with Kurtosis:
function Kurtosis(const X: array of Extended; const Adjusted: Boolean): Extended;
begin
var AMean := Mean(X);
var xi_minus_mean_power_4 := 0.0;
var xi_minus_mean_power_2 := 0.0;
for var i := Low(X) to High(X) do
begin
xi_minus_mean_power_4 := xi_minus_mean_power_4 + IntPower((X[i] - AMean), 4);
xi_minus_mean_power_2 := xi_minus_mean_power_2 + IntPower((X[i] - AMean), 2);
end;
{ mean ((x - mean (x)).^4)
k1 = ------------------------
std (x).^4
}
var N := Length(X);
Result := xi_minus_mean_power_4 / N /
IntPower(1 / N * xi_minus_mean_power_2, 2);
{ N - 1
k0 = 3 + -------------- * ((N + 1) * k1 - 3 * (N - 1))
(N - 2)(N - 3)
}
if Adjusted then
// Mathlab, Minitab and SigmaXL do not add 3 (which is the kurtosis for normal distribution
Result := {3 + }(N - 1) / ((N - 2) * (N - 3)) * ((N + 1) * Result - 3 * (N - 1));
end;
What is the reason for such adjustments and why Delphi decided not to implement it?

Code for Chi-square distribution function in Delphi

I have been looking for usable and full code for chi-square distribution in Delphi. There are some codes via net, but usually they don't work or have missing parts, do not compile etc.. There are also some libraries, but I'm interested about some code that I just can simply implement.
I've found something almost working. Some german parts have been fixed, it compiles and it gives p-values for most of the data:
function LnGamma (x : Real) : Real;
const
a0 = 0.083333333096;
a1 = -0.002777655457;
a2 = 0.000777830670;
c = 0.918938533205;
var
r : Real;
begin
r := (a0 + (a1 + a2 / sqr(x)) / sqr(x)) / x;
LnGamma := (x - 0.5) * ln(x) - x + c + r;
end;
function LnFak (x : Real) : Real;
var
z : Real;
begin
z := x+1;
LnFak := LnGamma(z);
end;
function Reihe (chi : Real; f : Real) : Real;
const MaxError = 0.0001;
var
Bruch,
Summe,
Summand : Real;
k, i : longint;
begin
Summe := 1;
k := 1;
repeat
Bruch := 1;
for i := 1 to k do
Bruch := Bruch * (f + 2 * i);
Summand := power(chi, 2 * k) / Bruch;
Summe := Summe + Summand;
k := succ(k);
until (Summand < MaxError);
Reihe := Summe;
end;
function IntegralChi (chisqr : Real; f : longint) : Real;
var
s : Real;
begin
S := power((0.5 * chisqr), f/2) * Reihe(sqrt(chisqr), f)
* exp((-chisqr/2) - LnGamma((f + 2) / 2));
IntegralChi := 1 - s;
end;
It works quite good for relatively big results.
For example:
For Chi = 1.142132 and df = 1 I'm getting p about 0.285202, which is perfect. Same as SPSS result or other programs.
But for example Chi = 138.609137 and df = 4 I should recieive something about 0.000000, but I'm getting floating point overflow error in Reiche function. Summe and Summand are very big then.
I admit that understanding distribution function is not my strong point, so maybe someone will tell me what I did wrong?
Thank you very much for the information
You should debug your program and find that there is an overflow
in your loop for k=149. For k=148 the value of Bruch is 3.3976725289e+304. The next computation of Bruch overflows. A fix is to code
for i := 1 to k do
Bruch := Bruch / (f + 2 * i);
Summand := power(chi, 2 * k) * Bruch;
With this change you get the value IntegralChi(138.609137,4) = 1.76835197E-7 after 156th iteration.
Note that your computation (even for this simple algorithm) is sub-optimal
because you compute the Bruch value over and over again. Just update it once
per loop:
function Reihe (chi : Real; f : Real) : Real;
const MaxError = 0.0001;
var
Bruch,
Summe,
Summand : Real;
k : longint;
begin
Summe := 1;
k := 1;
Bruch := 1;
repeat
Bruch := Bruch / (f + 2 * k);
Summand := power(chi, 2 * k) * Bruch;
Summe := Summe + Summand;
k := succ(k);
until (Summand < MaxError);
Reihe := Summe;
end;
Similar consideration should be applied to compute power(chi, 2*k) and then combine this with the improved evaluation of Bruch.
Edit: As a response to your comment, here the improved version based on the property of the power function, that is power(chi, 2*(k+1)) = power(chi, 2*k)*sqr(chi)
function Reihe (chi : Real; f : Real) : Real;
const MaxError = 0.0001;
var
chi2,
Summe,
Summand : Real;
k : longint;
begin
Summe := 1;
k := 1;
Summand := 1;
chi2 := sqr(chi);
repeat
Summand := Summand * chi2 / (f + 2 * k);
Summe := Summe + Summand;
k := succ(k);
until (Summand < MaxError);
Reihe := Summe;
end;

How to pass an integer constant as generic parameter

I need to pass an integer constant as a generic parameter. For example, here is a code for working with a non-standard floating-point numbers (float16) (the code is taken from the githab and slightly modified):
const
ES = 5; // exponent size (bits)
MS = 10; // mantissa size (bits)
// plus sign (1 bit)
ESMS = ES + MS;
function FloatXXToSingle(const _in: cardinal): Single;
var
t1, t2, t3: cardinal;
begin
t1 := _in and (Cardinal(1 shl ESMS) - 1); // Non-sign bits
t2 := _in and (1 shl ESMS); // Sign bit
t3 := _in and ((1 shl ES - 1) shl MS); // Exponent
t1 := t1 shl (23 - MS); // Align mantissa on MSB
t2 := t2 shl (31 - ESMS); // Shift sign bit into position
t1 := t1 + cardinal((127-(1 shl (ES-1)-1)) shl 23); // Adjust bias
if t3 = 0 then // Denormals-as-zero
t1 := 0;
t1 := t1 or t2; // Re-insert sign bit
pCardinal(#Result)^ := t1;
end;
I want to create a generic on its basis, so that I can set the number of bits for the exponent and mantissa myself.
Now I'm doing something like this:
type
TFloat<TES, TMS> = class
const ES = sizeof(TES);
const MS = sizeof(TMS);
const ESMS = ES + MS;
class function ToSingle(const _in: cardinal): Single;
end;
class function TFloat<TES, TMS>.ToSingle(const _in: cardinal): Single;
var
t1, t2, t3: cardinal;
begin
t1 := _in and (Cardinal(1 shl ESMS) - 1); // Non-sign bits
t2 := _in and (1 shl ESMS); // Sign bit
t3 := _in and ((1 shl ES - 1) shl MS); // Exponent
t1 := t1 shl (23 - MS); // Align mantissa on MSB
t2 := t2 shl (31 - ESMS); // Shift sign bit into position
t1 := t1 + cardinal((127-(1 shl (ES-1)-1)) shl 23); // Adjust bias
if t3 = 0 then // Denormals-as-zero
t1 := 0;
t1 := t1 or t2; // Re-insert sign bit
pCardinal(#Result)^ := t1;
end;
But to use this, I have to write something like this:
type
TMyFloat = TFloat<packed record a:Integer; b:Byte; end, packed record a: Extended; end>;
begin
writeln(TMyFloat.ToSingle($1234));
end.
But this method is not at all elegant. Perhaps there is a way to directly pass two numbers to the generic: the size of the exponent and the mantissa?

Delphi - Lenze Standard addressing - Code number conversion

From Lenze manual
Code number (C1, C2)
Standard addressing
The meaning of the code numbers and the assigned parameters can be obtained from
the code table (see chapter 8.2). When transmitting data, the code number are
coded as follows:
The following calculation determines the two ASCII digits from the code number
(value range: 0..6229) (value range: 48dec 127dec):
C1 = INTEGER((REMAINDER(code number/790))/10) + 48dec
C2 = REMAINDER(REMAINDER(code number/790)/10) +
INTEGER(code number/790) x 10 + 48dec
Procedure for calculating C1 and C2 from codenumber.
procedure pCodeNumberToC1C2(CodeNumber: Word; var C1, C2: Byte);
begin
C1 := Byte((CodeNumber mod 790) div 10) + 48;
C2 := ((CodeNumber mod 790) mod 10) + 48 + 10 * Byte(CodeNumber div 790);
end;
But, how to calculate it the other way without the aweful:
function fC1C2ToCodeNumber(iC1, iC2: Byte): Word;
var
C1, C2: Byte;
i: Integer;
Begin
Result := 0;
For i := 0 to 6229 Do Begin
pCodeNumberToC1C2(i, C1, C2);
if (C1 = iC1) and (C2 = iC2) Then Result := i;
End;
Result := cn;
End;
Let's
N = p * 790 + q
then
c1 = 48 + q div 10
c2 = 48 + q mod 10 + 10 * p
so
p = (c2-48) div 10
q = (c2-48) mod 10 + (c1-48) * 10
test:
var
c1, c2: Byte;
n, p, q, t: Word;
begin
for t := 0 to 6229 do begin
n := t;
pCodeNumberToC1C2(n, c1, c2);
p := (c2-48) div 10;
q := (c2-48) mod 10 + (c1-48) * 10;
n := 790*p+q;
if n <> t then
Memo1.Lines.Add('Failed at ' + IntToStr(t))
end;
Final:
function C1C2ToCodeNumber(C1, C2: Byte): Word;
begin
Result := ((C2 - 48) div 10) * 790 + ((C2 - 48) mod 10 + (C1 - 48) * 10);
end;
As an alternative to arithmetic you could consider a lookup table. At the cost of memory, this gives you better performance. The code looks like this:
const
CodeNumberTable: array [48..126, 48..127] of Word = (
.... code removed because of Sack Overflow post size limitation
);
const
MinC1 = low(CodeNumberTable);
MinC2 = high(CodeNumberTable);
MaxC1 = low(CodeNumberTable[MinC1]);
MaxC2 = high(CodeNumberTable[MinC1]);
type
EInvalidParameters = class(Exception);
function fC1C2ToCodeNumber(iC1, iC2: Byte): Word;
begin
if not InRange(iC1, MinC1, MaxC1) then
raise EInvalidParameters.CreateFmt(
'iC1 (%d) must be in the range %d to %d',
[iC1, MinC1, MaxC1]
);
if not InRange(iC2, MinC2, MaxC2) then
raise EInvalidParameters.CreateFmt(
'iC2 (%d) must be in the range %d to %d',
[iC2, MinC2, MaxC2]
);
Result := CodeNumberTable[iC1, iC2];
if Result=high(Word) then
raise EInvalidParameters.CreateFmt(
'CodeNumber not defined for iC1=%d, ic2=%d',
[iC1, iC2]
);
end;
I can supply the table via paste bin if you are interested.

CRC-CCITT (0xFFFF) function?

Can someone help me with Delphi implementation of CRC-CCITT (0xFFFF)?
Already get the Java version, but confusing on how to port it to Delphi
public static int CRC16CCITT(byte[] bytes) {
int crc = 0xFFFF; // initial value
int polynomial = 0x1021; // 0001 0000 0010 0001 (0, 5, 12)
for (byte b : bytes) {
for (int i = 0; i < 8; i++) {
boolean bit = ((b >> (7-i) & 1) == 1);
boolean c15 = ((crc >> 15 & 1) == 1);
crc <<= 1;
if (c15 ^ bit) crc ^= polynomial;
}
}
crc &= 0xffff;
//System.out.println("CRC16-CCITT = " + Integer.toHexString(crc));
return crc;
}
and for PHP implementation
<?php
function crc16($data)
{
$crc = 0xFFFF;
for ($i = 0; $i < strlen($data); $i++)
{
$x = (($crc >> 8) ^ ord($data[$i])) & 0xFF;
$x ^= $x >> 4;
$crc = (($crc << 8) ^ ($x << 12) ^ ($x << 5) ^ $x) & 0xFFFF;
}
return $crc;
}
0xFFFF translates to $FFFF
& translates to and
^ translates to xor
<< translates to shl
>> translates to shr
x ^= y translates to x := x xor y, similar for &=, <<=, etc.
These operators generally have higher precedence in Delphi so they usually need to have their arguments parenthesized.
I'm quite sure that there are plenty of other implementations of CRC16 etc. for Delphi, see e.g. Improve speed on Crc16 calculation
function CRC16CCITT(bytes: TBytes): Word;
const
polynomial = $1021; // 0001 0000 0010 0001 (0, 5, 12)
var
crc: Word;
I, J: Integer;
b: Byte;
bit, c15: Boolean;
begin
crc := $FFFF; // initial value
for I := 0 to High(bytes) do
begin
b := bytes[I];
for J := 0 to 7 do
begin
bit := (((b shr (7-J)) and 1) = 1);
c15 := (((crc shr 15) and 1) = 1);
crc := crc shl 1;
if ((c15 xor bit) <> 0) then crc := crc xor polynomial;
end;
end;
Result := crc and $ffff;
end;
You can find one in Delphi Encryption Compendium (DEC) component.
5 Checksums (CRC32, CRC16-CCITT, CRC16-Standard ...)
http://blog.digivendo.com/2008/11/delphi-encryption-compendium-dec-52-for-d2009-released/
i found some code that works:
function crc16(Buffer:String;Polynom,Initial:Cardinal):Cardinal;
var
i,j: Integer;
begin
Result:=Initial;
for i:=1 to Length(Buffer) do begin
Result:=Result xor (ord(buffer[i]) shl 8);
for j:=0 to 7 do begin
if (Result and $8000)<>0 then Result:=(Result shl 1) xor Polynom
else Result:=Result shl 1;
end;
end;
Result:=Result and $ffff;
end;
source : http://www.miscel.dk/MiscEl/CRCcalculations.html
unit CRC16CCITT;
interface
function ComputeCRC16CCITT(crc: word; const data: PByte; len:integer) : word;
implementation
const
crc16_table: array [0..$FF] of word = (0,4489,8978,12955,17956,22445,25910,29887,35912,40385,44890,48851,51820,56293,59774,
63735,4225,264,13203,8730,22181,18220,30135,25662,40137,36160,49115,44626,56045,52068,63999,
59510,8450,12427,528,5017,26406,30383,17460,21949,44362,48323,36440,40913,60270,64231,51324,
55797,12675,8202,4753,792,30631,26158,21685,17724,48587,44098,40665,36688,64495,60006,55549,
51572,16900,21389,24854,28831,1056,5545,10034,14011,52812,57285,60766,64727,34920,39393,43898,
47859,21125,17164,29079,24606,5281,1320,14259,9786,57037,53060,64991,60502,39145,35168,48123,
43634,25350,29327,16404,20893,9506,13483,1584,6073,61262,65223,52316,56789,43370,47331,35448,
39921,29575,25102,20629,16668,13731,9258,5809,1848,65487,60998,56541,52564,47595,43106,39673,
35696,33800,38273,42778,46739,49708,54181,57662,61623,2112,6601,11090,15067,20068,24557,28022,
31999,38025,34048,47003,42514,53933,49956,61887,57398,6337,2376,15315,10842,24293,20332,32247,
27774,42250,46211,34328,38801,58158,62119,49212,53685,10562,14539,2640,7129,28518,32495,19572,
24061,46475,41986,38553,34576,62383,57894,53437,49460,14787,10314,6865,2904,32743,28270,23797,
19836,50700,55173,58654,62615,32808,37281,41786,45747,19012,23501,26966,30943,3168,7657,12146,
16123,54925,50948,62879,58390,37033,33056,46011,41522,23237,19276,31191,26718,7393,3432,16371,
11898,59150,63111,50204,54677,41258,45219,33336,37809,27462,31439,18516,23005,11618,15595,3696,
8185,63375,58886,54429,50452,45483,40994,37561,33584,31687,27214,22741,18780,15843,11370,7921,
3960);
function ComputeCRC16CCITT(crc: word; const data: PByte; len:integer) : word;
var
i : integer;
begin
for i := 0 to len-1 do
crc := (crc shr 8) xor crc16_table[(crc xor data[i]) and $ff];
result := crc;
end;
end.

Resources