using System;
using System.Collections.Generic;
using System.Threading;
using System.Diagnostics;
using System.Runtime.CompilerServices;
// 64 bit fnv-1a hash
// : http://w...content-available-to-author-only...e.com/chongo/tech/comp/fnv/
public class FnvHash64
{
private ulong _hash = 14695981039346656037ul;
public ulong Value
{
get { return _hash; }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AppendByte(byte b)
{
_hash = _hash ^ b;
_hash = _hash * 1099511628211ul;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AppendShort(ushort x)
{
AppendByte((byte)(x & 0xff));
AppendByte((byte)((x >> 8) & 0xff));
}
public void AppendInt(uint x)
{
AppendByte((byte)(x & 0xff));
AppendByte((byte)((x >> 8) & 0xff));
AppendByte((byte)((x >> 16) & 0xff));
AppendByte((byte)((x >> 24) & 0xff));
}
public void AppendLong(ulong x)
{
AppendByte((byte)(x & 0xff));
AppendByte((byte)((x >> 8) & 0xff));
AppendByte((byte)((x >> 16) & 0xff));
AppendByte((byte)((x >> 24) & 0xff));
AppendByte((byte)((x >> 32) & 0xff));
AppendByte((byte)((x >> 40) & 0xff));
AppendByte((byte)((x >> 48) & 0xff));
AppendByte((byte)((x >> 56) & 0xff));
}
public void AppendData(byte [] data)
{
for (int i = 0; i < data.Length; i++)
AppendByte(data[i]);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AppendChar(char ch)
{
AppendShort(ch);
}
public void AppendStr(string str)
{
for (int i = 0; i < str.Length; i++)
AppendShort(str[i]);
}
}
public class Random64: Random
{
private UInt64 state;
public Random64(): this((ulong)Environment.TickCount)
{
}
public Random64(UInt64 seed)
{
const UInt64 DefaultSeed = 12030203114834284017ul;
if (seed != DefaultSeed)
seed = seed ^ DefaultSeed;
state = seed;
for (int i = 0; i < 16; i++)
Next64();
}
// XorShift implementation
// : http://w...content-available-to-author-only...x.com/tutorials/random_numbers/xorshift.shtml
// (Numerical Recipes, 3rd ed, p. 346)
// - performace is likely limited by the virtual call,
// unless inlining optimizations work well
public virtual UInt64 Next64()
{
state ^= state << 21;
state ^= state >> 35;
state ^= state << 4;
return state - 1;
}
protected override double Sample()
{
const double inv2r64 = 5.4210108624275216e-20;
//const double largestSub1 = 0.99999999999999989;
//Console.WriteLine("sub1: {0:f30}, <1: {1}", largestSub1, largestSub1 < 1.0);
//double maxSample = (double)ulong.MaxValue * inv2r64;
//Console.WriteLine("maxS: {0:f30}, <1: {1}", maxSample, maxSample < 1.0);
UInt64 val64 = Next64();
double sampledVal = (double)val64 * inv2r64;
// >=1.0 is not needed and contains a branch, although a never taken one,
// and efficient compilation has 2 evaluated instructions
// (cmp x,1.0; jge ~never;; return; ~never: mv x,L; return)
// : maxSample < 1 => product with any smaller val64 should be < 1 too,
// else ordering problems (could break Sort assumptions)
//if (sampledVal >= 1.0)
// sampledVal = largestSub1;
return sampledVal;
}
public override int Next()
{
UInt64 val64 = Next64();
int val = (int)(val64 & 0x7fffffff);
return val;
}
private const int HaltLimit = 1000000;
public override int Next(int maxValue)
{
if (maxValue < 0)
throw new Exception("Invalid parameter");
else if (maxValue < 2)
return 0;
UInt64 maxValue64 = (UInt64)maxValue;
UInt64 limitValue = UInt64.MaxValue - UInt64.MaxValue % maxValue64;
UInt64 val64 = Next64();
int haltCount = HaltLimit;
while (val64 >= limitValue && --haltCount >= 0)
val64 = Next64();
UInt64 val = val64 % maxValue64;
return (int)val;
}
public override int Next(int minValue, int maxValue)
{
if (minValue > maxValue)
throw new Exception("Invalid parameter");
else if ((long)maxValue - (long)minValue < 2)
return minValue;
UInt64 valueIntLen = (ulong)((long)maxValue - (long)minValue); // at most uint.MaxValue
UInt64 limitValue = UInt64.MaxValue - UInt64.MaxValue % valueIntLen;
UInt64 val64 = Next64();
int haltCount = HaltLimit;
while (val64 >= limitValue && --haltCount >= 0)
val64 = Next64();
UInt64 randDiff = val64 % valueIntLen;
long val = (long)minValue + (long)randDiff;
return (int)val;
}
public override void NextBytes(byte[] buffer)
{
int len = buffer.Length;
int lenX8 = len - len % 8; // len & 0x7ffffff8 = len & (MaxInt ^ 0x7)
for (int i = 0; i < lenX8; i += 8)
{
UInt64 val = Next64();
buffer[i + 0] = (byte)(val & 0xff);
buffer[i + 1] = (byte)((val >> 8) & 0xff);
buffer[i + 2] = (byte)((val >> 16) & 0xff);
buffer[i + 3] = (byte)((val >> 24) & 0xff);
buffer[i + 4] = (byte)((val >> 32) & 0xff);
buffer[i + 5] = (byte)((val >> 40) & 0xff);
buffer[i + 6] = (byte)((val >> 48) & 0xff);
buffer[i + 7] = (byte)((val >> 56) & 0xff);
}
if (lenX8 < len)
{
UInt64 val = Next64();
for (int i = lenX8, j = 0; i < len; i++, j++)
{
buffer[i] = (byte)((val >> (8*j)) & 0xff);
}
}
}
public override double NextDouble()
{
// [performance note] calling virtual Sample which calls virtual Next64
return Sample();
}
// Quality random seed
// : not crypto qyality but better than the constructor default, which is just simple and fast
public static UInt64 HashSeed
{
get
{
Process process = Process.GetCurrentProcess();
FnvHash64 hash = new FnvHash64();
hash.AppendStr(Environment.MachineName); // network unique
hash.AppendInt((uint)process.Id); // system unique
hash.AppendInt((uint)Thread.CurrentThread.ManagedThreadId); // process or system unique
hash.AppendLong((ulong)process.StartTime.Ticks); // constant for process, for seed used like process Id but more random bits
hash.AppendLong((ulong)process.TotalProcessorTime.Ticks);
hash.AppendInt((uint)Environment.TickCount);
hash.AppendLong((ulong)DateTime.Now.Ticks); // brings most randomness for seed (easy to predict, but not crypto anyway)
return hash.Value; // maybe 30-40 bits of randomness
}
}
}
public class Test
{
public static void RandomPermute<T>(IList<T> items, Random rnd)
{
int icount = items.Count;
// [NOTE] Random.Next(maxValue) implementation is biased, can be overriden
//
// [TODO] mathematically prove equivalence with selecting a random available position in a new list
// or with any other clear randomness
// : [draft notes]: all elements are fully randomized, once randomized it remains randomized, potential further moves are just as random,
// nothing useful added / nothing lost at randomness by further fully random moves,
// once fully random (any position could move to any) without RNG bias then no implicit algorithm bias is relevant
// (i.e. could apply any algorithm based on indices and the values remain randomly distributed)
// [TODO] at which swap count the random 2 elements swap becomes equivalent to this?
// - [draft notes]: must be related with the probability for any element not to move at all with an end result that is
// equivalent with a fully randomized list
// (in the same position we have elements that did not move at all and elements that moved back,
// the most probable count being same as in fully randomized and equally probable for all elements)
// - the count may be infinite because always a small chance that one element was not touched and the touched ones
// have the exact fully random distribution, like always a small bias that some elements remain outside the
// full randomization, like a bias for more elements on original position
// - how much is the bias, does it matter at 1000*count ?
for (int i = 0; i < icount; i++)
{
int dsti = rnd.Next(icount);
T itemVal = items[i];
items[i] = items[dsti];
items[dsti] = itemVal;
}
}
public static void PermuteReverseRand1of5<T>(IList<T> items, Random rnd)
{
int icount = items.Count;
if (icount < 2)
return;
// reverse list
for (int i = 0; i < icount/2; i++)
{
T aux = items[i];
items[i] = items[icount - i - 1];
items[icount - i - 1] = aux;
}
// n/10 random swaps -> ~n/5 swapped elements (1 - 1/e^(1/5) ~= 0.18 * n)
// : prob. not to be swapped: ((n - 1)/n)^(n/5) -> 1/e^(1/5) ,
// https://w...content-available-to-author-only...a.com/input/?i=%28%28n+-+1%29%2Fn%29%5E%28n%2F5%29
int swapCount = icount / 10;
for (int i = 0; i < swapCount; i++)
{
int srcI = rnd.Next(icount);
int dstI = rnd.Next(icount);
T aux = items[srcI];
items[srcI] = items[dstI];
items[dstI] = aux;
}
}
public static double EstimatePi(int sampleCount, Random rnd)
{
// Sampling the area of a cicle quadrant fit inside a square
// A4 = PI * r^2 / 4, r = 1 => A4 = PI / 4 => PI = 4 * A4, A4 = InR / All
int inCircleCount = 0;
for (int i = 0; i < sampleCount; i++)
{
double x = rnd.NextDouble(), y = rnd.NextDouble();
double d2 = x * x + y * y;
if (d2 <= 1)
inCircleCount += 1;
}
double pi = 4.0 * (double)inCircleCount / (double)sampleCount;
return pi;
}
public static void Main()
{
Random rnd = new Random64(Random64.HashSeed);
List<int> lsA = new List<int>() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
RandomPermute(lsA, rnd);
lsA.ForEach(a => Console.Write("{0}, ", a)); Console.WriteLine(";");
double pi = EstimatePi(100000, rnd);
Console.WriteLine("pi ~= {0:F9}, err: {1:F9} (PI = {2:F9})", pi,
Math.Abs(Math.PI - pi), Math.PI);
}
}