Почему этот повторяемый случайный алгоритм не работает?

Мне нужно было получить случайный генератор, который взял бы две координаты в качестве семени и вывел бы случайное число, всегда одинаковое для тех же координат. Итак, вдохновленный реализацией Jon Skeet GetHashCode, вот что я сделал:

public class SimpleRandom2D
{
    public SimpleRandom2D(int _Seed)
    {
        Seed = _Seed;
    }

    public SimpleRandom2D()
    {
        Seed = new Random().Next();
    }

    readonly public int Seed;

    public float Sample(float _X, float _Y)
    {
        int thisSeed;
        unchecked
        {
            thisSeed = (int) 2166136261;
            thisSeed = thisSeed * 16777619 ^ _X.GetHashCode();
            thisSeed = thisSeed * 16777619 ^ _Y.GetHashCode();
            thisSeed = thisSeed * 16777619 ^ Seed;
        }
        return 2f * (float) new Random(thisSeed).NextDouble() - 1f;
    }

}

Я знаю, что создание нового Random очень далек от оптимального, но я хотел, чтобы это было правильно, прежде чем я быстро его освою. Однако оказалось, что это было не совсем правильно: иногда они возвращали те же значения для координат, которые имеют одинаковые x и у которых ± 1. Этот тест последовательно терпит неудачу:

    [Test]
    [Repeat (20)]
    public void Sample_IntegerFloats_WithSameXNeighbourY_NotSame()
    {
        var random = new Random();
        var simpleRandom2d = new SimpleRandom2D();
        float y = random.Next(); // .Next() returns an integer, which is implicitly converted to a float — hence IntegerFloats in the test title
        float x = random.Next();
        float value1 = simpleRandom2d.Sample(x, y);
        float value2 = simpleRandom2d.Sample(x, y + 1);
        Assert.That(value1, Is.Not.EqualTo(value2));
    }

Напротив, этот тест не прерывается:

    [Test]
    [Repeat (20)]
    public void Sample_IntegerFloats_WithSameX_NotSame()
    {
        var random = new Random();
        var simpleRandom2d = new SimpleRandom2D();
        float x = random.Next();
        float value1 = simpleRandom2d.Sample(x, random.Next());
        float value2 = simpleRandom2d.Sample(x, random.Next());
        Assert.That(value1, Is.Not.EqualTo(value2));
    }

Почему это происходит и как я могу его исправить?

Ответы

Ответ 1

Оказывается, моя ошибка не имела никакого отношения к случайности. Я сгенерировал случайный целочисленный float с методом .Next() метода Random. Тем не менее, я забыл, что в то время как .Next() приведет к любому целому числу от 0 до 2 147 483 647, тип float имел ограниченную точность, поэтому, когда я сделал + 1 для получения "соседа", мой поплавок проводился большую часть времени, поэтому большой, что на самом деле это не имело значения. Другими словами, когда вы это делаете:

float x = new Random().Next();
y = x + 1;

x обычно все равно равен y.