6

I'm trying to generate a number based on a seed in C#. The only problem is that the seed is too big to be an int32. Is there a way I can use a long as the seed?

And yes, the seed MUST be a long.

user1599078
  • 167
  • 1
  • 3
  • 9
  • Why do you need a long seed for a pseudorandom number? – tomsv Mar 17 '13 at 16:18
  • @J0HN I'm calculating where slimes spawn in minecraft. – user1599078 Mar 17 '13 at 16:18
  • 1
    @user1599078 - Even if you use the Long that you've obtained, how can you guarantee that the psuedorandom number generated matches that of the Java implementation? – keyboardP Mar 17 '13 at 16:19
  • 1
    That's greek for me. Why do you think using long as seed will help you? +100500 for @keyboardP comment(sorry, SO allows only one upvote :)) – J0HN Mar 17 '13 at 16:19
  • @keyboardP I'm not sure what you mean but here is the algorithm:long RandomParameter = seed + (long)(x * x * 0x4c1906) + (long)(x * 0x5ac0db) + (long)(z * z) * 0x4307a7L + (long)(z * 0x5f24f) ^ 0x3ad8025f; Random rnd = new Random(Convert.ToInt32(RandomParameter)); if (rnd.Next(10) == 0) return true; else return false; – user1599078 Mar 17 '13 at 16:20
  • Do you have the original RNG algorithm that Minecraft uses? – Matthew Watson Mar 17 '13 at 16:20
  • @MatthewWatson You mean before I ported it from Java? Random rnd = new Random(seed + (long) (xPosition * xPosition * 0x4c1906) + (long) (xPosition * 0x5ac0db) + (long) (zPosition * zPosition) * 0x4307a7L + (long) (zPosition * 0x5f24f) ^ 0x3ad8025f); return rnd.nextInt(10) == 0; – user1599078 Mar 17 '13 at 16:22
  • Typical chicken-and-egg problem. You now need a really good random number generator to produce a sufficiently random seed. Starting a not-so-good random number generator with a really good seed is a complete waste of effort. Use the System.Security.Cryptography.RNGCryptoServiceProvider class. – Hans Passant Mar 17 '13 at 16:23
  • @user1599078 - The Java version of `Random` will not necessarily be the same as the C# version. Therefore, it's very unlikely you're going to get the same output given the same input. – keyboardP Mar 17 '13 at 16:23
  • @HansPassant Mind linking to an example on how I'd use it? – user1599078 Mar 17 '13 at 16:27
  • @keyboardP I just checked, they have the same first parameter: the seed. – user1599078 Mar 17 '13 at 16:28
  • @user1599078 Unfortunately that doesn't guarantee the output will be the same. The number generated from the `Random` class (either in C# or Java), is not actually truly random in the mathematical sense. They take the seed and from that seed they perform various functions to give you what seems like a random number. The problem you have here is that both will give you a "random" number but that number won't necessarily be the same, even if the seed is the same, because they calculate that number differently. – keyboardP Mar 17 '13 at 16:30
  • @keyboardP That sucks, thanks for clarification though. I found somebody who wrote a Random function that acts like java's in python, I'll port it across now. – user1599078 Mar 17 '13 at 16:32
  • 1
    @user1599078 - Yup, as long as the internal implementation of the `Random` function matches up, you should get the same output. In that case, you don't even need the `Random` class and so you can create your own method that takes a `Long`. – keyboardP Mar 17 '13 at 16:33
  • 1
    You could look at the source for `java.lang.Random` and just convert it to C#; it's probably not too hard. The algorithm is here: http://docs.oracle.com/javase/6/docs/api/java/util/Random.html#next%28int%29 – Matthew Watson Mar 17 '13 at 16:36
  • The [MedallionRandom](https://github.com/madelson/MedallionUtilities/tree/master/MedallionRandom) NuGet package contains a port of Java's random number generator, which accepts a 64-bit seed. – ChaseMedallion Nov 03 '16 at 11:34

3 Answers3

2

Here's a C# version of Java.Util.Random that I ported from the Java Specification.

The best thing to do is to write a Java program to generate a load of numbers and check that this C# version generates the same numbers.

public sealed class JavaRng
{
    public JavaRng(long seed)
    {
        _seed = (seed ^ LARGE_PRIME) & ((1L << 48) - 1);
    }

    public int NextInt(int n)
    {
        if (n <= 0)
            throw new ArgumentOutOfRangeException("n", n, "n must be positive");

        if ((n & -n) == n)  // i.e., n is a power of 2
            return (int)((n * (long)next(31)) >> 31);

        int bits, val;

        do
        {
            bits = next(31);
            val = bits % n;
        } while (bits - val + (n-1) < 0);
        return val;
    }

    private int next(int bits)
    {
        _seed = (_seed*LARGE_PRIME + SMALL_PRIME) & ((1L << 48) - 1);
        return (int) (((uint)_seed) >> (48 - bits));
    }

    private long _seed;

    private const long LARGE_PRIME = 0x5DEECE66DL;
    private const long SMALL_PRIME = 0xBL;
}
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
2

For anyone seeing this question today, .NET 6 and upwards provides Random.NextInt64, which has the following overloads:

  • NextInt64()

    • Returns a non-negative random integer.
  • NextInt64(Int64)

    • Returns a non-negative random integer that is less than the specified maximum.
  • NextInt64(Int64, Int64)

    • Returns a random integer that is within a specified range.
Yuriy Sountsov
  • 179
  • 1
  • 6
  • These methods generate random 64-bit numbers. The question is how do you initialize a `Random` instance using a 64-bit seed, which is unrelated. – Lucas Jan 09 '23 at 16:12
0

I'd go for the answer provided here by @Dyppl: Random number in long range, is this the way?

Put this function where it's accessible to the code that needs to generate the random number:

long LongRandom(long min, long max, Random rand) 
{
    byte[] buf = new byte[8];
    rand.NextBytes(buf);
    long longRand = BitConverter.ToInt64(buf, 0);
    return (Math.Abs(longRand % (max - min)) + min);
}

Then call the function like this:

long r = LongRandom(100000000000000000, 100000000000000050, new Random());
Community
  • 1
  • 1
Kal
  • 25
  • 2
  • 10
    Per comments in link (and testing myself), this method does not work: "Doesn't work properly. LongRandom(long.MinValue, long.MaxValue, new Random()), always returns -9223372036854775808. In any case, Math.Abs() destroys one bit, leaving you with 63 random bits. You can't provide a 64-bit random number if you only have 63 random bits." – Jake Drew Dec 13 '15 at 05:16
  • These method generates a random 64-bit number. The question is how do you initialize a `Random` instance using a 64-bit seed, which is unrelated. – Lucas Jan 09 '23 at 16:13