I want to generate a random seed based on a string value (which is ~32 characters long).
I don't want to use string.GetHashCode()
since I may later will need a working implementation in another language (C++, Python, JavaScript) (Yes I will need to get rid of Random()
too then and all other .NET classes).
I also want the whole input string to be relevant for the seed and not just a fraction of it.
This is the solution I came up with:
Fiddle: https://dotnetfiddle.net/G9Qr7r
I basically generate 8 Random
instances with a Int32 seed based on a part of the SHA256 array. Everytime I generate a random number I switch to the next Random
Instance.
code:
using System;
using System.Security.Cryptography;
using System.Text;
public class Program
{
public static void Main()
{
Sha256Random rnd = new("Well, hello there");
for (int i = 0; i < 100; i++)
{
Console.WriteLine(rnd.Next(0,100));
}
}
}
public class Sha256Random
{
private int[] seeds = default!;
private Random[] rnd = new Random[8];
private int rndIdx = 0;
public Sha256Random(string seed)
{
GenerateSeeds(seed);
}
public int Next()
{
if(rndIdx>= 8)
rndIdx=0;
return rnd[rndIdx++].Next();
}
public int Next(int min, int max)
{
if (rndIdx >= 8)
rndIdx = 0;
return rnd[rndIdx++].Next(min, max);
}
public int Next(int max)
{
if (rndIdx >= 8)
rndIdx = 0;
return rnd[rndIdx++].Next(max);
}
private void GenerateSeeds(string value)
{
using var hash = SHA256.Create();
byte[] bytes = hash.ComputeHash(Encoding.ASCII.GetBytes(value));
seeds = new int[bytes.Length/4];
for (int i = 0; i < seeds.Length; i++)
{
int idx = i*4;
seeds[i] = bytes[idx] | bytes[idx+1] << 8 | bytes[idx+2] << 16 | bytes[idx+3] << 24;
}
rnd = new Random[seeds.Length];
for (int i = 0; i < seeds.Length; i++)
{
rnd[i] = new Random(seeds[i]);
}
}
}
I feel like it's overengineered?!
1 Answer 1
In general, I would prefer the simplest code that satisfies all of the criteria. In this case, that means having a single Random
instance, initialized with String.GetHashCode()
. I understand that GetHashCode
isn't easily portable to other platforms, but neither is Random
. If making it easily portable to other platforms becomes a requirement, you can replace them at that time. You might end up using SHA256 for that, and then that's one thing you wouldn't need to change, but it's also possible you would choose something else and need to change anyway.
As for the existing implementation, the seeds are only needed temporarily to initialize the Random
instances and therefore should not be part of the object state. You have the value 8
hard-coded in several places, I would probably replace that with rnd.Length
.
Getting further into matters of taste, I would prefer:
public int Next(int min, int max)
{
rndIdx = (rndIdx + 1) % rnd.Length;
return rnd[rndIdx].Next(min, max);
}
-
1\$\begingroup\$ Thanks :) I think you forgot the paranthesises, should be
rndIdx = (rndIdx + 1) % rnd.Length;
\$\endgroup\$DoubleVoid– DoubleVoid2022年09月07日 16:01:18 +00:00Commented Sep 7, 2022 at 16:01 -
\$\begingroup\$ You're right. Updated. \$\endgroup\$Pierre Menard– Pierre Menard2022年09月07日 16:05:26 +00:00Commented Sep 7, 2022 at 16:05
Random(int seed)
... if so, I will check it out for sure :) \$\endgroup\$