1
\$\begingroup\$

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?!

New contributor
DoubleVoid is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
\$\endgroup\$
5
  • 2
    \$\begingroup\$ If you want something simpler, maybe Donald Knuth’s multiplicative hash? \$\endgroup\$
    – Davislor
    Aug 6 at 17:00
  • \$\begingroup\$ If I understood it correctly the Knuth hash generates a hash based on two integers. So basically I use it to generate a hash out of my 8 integers and then cast the uint to an int for use with Random(int seed) ... if so, I will check it out for sure :) \$\endgroup\$
    – DoubleVoid
    Aug 6 at 17:12
  • \$\begingroup\$ I made a small fiddle: dotnetfiddle.net/hEoVhm . Regarding collisions this could be more error prone I guess? \$\endgroup\$
    – DoubleVoid
    Aug 6 at 17:17
  • \$\begingroup\$ For future reference, unlike Stack Overflow we like to see as much code as possible, you have included the entire class, but the entire program in this case would give us more to review. It would be better if the using statements were also included. \$\endgroup\$
    – pacmaninbw
    Aug 7 at 13:54
  • \$\begingroup\$ I altered the question. I thought the fiddle link would suffice for instant testing :) \$\endgroup\$
    – DoubleVoid
    Aug 7 at 20:49

0

Your Answer

DoubleVoid is a new contributor. Be nice, and check out our Code of Conduct.

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.