C # Случайные числа не являются "случайными" - PullRequest
6 голосов
/ 31 августа 2011

Я знаю, что класс C # Random не создает "истинно случайные" числа, но у меня возникает проблема с этим кодом:

    public void autoAttack(enemy theEnemy)
    {
        //Gets the random number
        float damage = randomNumber((int)(strength * 1.5), (int)(strength * 2.5));

        //Reduces the damage by the enemy's armor
        damage *= (100 / (100 + theEnemy.armor));

        //Tells the user how much damage they did
        Console.WriteLine("You attack the enemy for {0} damage", (int)damage);

        //Deals the actual damage
        theEnemy.health -= (int)damage;

        //Tells the user how much health the enemy has left
        Console.WriteLine("The enemy has {0} health left", theEnemy.health);
    }

Затем я вызываю функцию здесь (я вызывал ее 5 раз для проверки случайности чисел):

        if (thePlayer.input == "fight")
        {
            Console.WriteLine("you want to fight");
            thePlayer.autoAttack(enemy1);
            thePlayer.autoAttack(enemy1);
            thePlayer.autoAttack(enemy1);
        }

Однако, когда я проверяю вывод, я получаю одно и то же число для каждых 3 вызовов функций. Однако каждый раз, когда я запускаю программу, я получаю другое число (которое повторяется 3 раза), например:

 You attack the enemy for 30 damage.
 The enemy has 70 health left.

 You attack the enemy for 30 damage.
 The enemy has 40 health left.

 You attack the enemy for 30 damage.
 The enemy has 10 health left.

Затем я пересоберу / отладлю / снова запустите программу и получу другое число вместо 30, но оно будет повторяться все 3 раза.

У меня вопрос: как я могу получать разные случайные числа каждый раз, когда вызываю эту функцию? Я просто получаю одно и то же «случайное» число снова и снова.

Вот случайный вызов класса, который я использовал:

    private int randomNumber(int min, int max)
    {
        Random random = new Random();
        return random.Next(min, max);
    }

Ответы [ 5 ]

29 голосов
/ 31 августа 2011

Я предполагаю, что randomNumber создает новый экземпляр Random каждый раз ..., который, в свою очередь, создает новый генератор псевдослучайных чисел на основе текущего времени ...не меняйте так часто, как вы думаете.

Не делайте этого.Используйте один и тот же экземпляр Random несколько раз ... но не"исправляйте" его, создавая статическую переменную Random.Это не будет хорошо работать в долгосрочной перспективе, так как Random не является потокобезопасным.В тестировании все будет выглядеть хорошо, тогда вы получите загадочные данные после того, как вам повезет с параллелизмом: (

К счастью, не так уж сложно заставить что-то работать с использованием локальных потоков, особенно если вына .NET 4. В итоге вы получите новый экземпляр Random для потока.

Я написал статью на эту тему , которая может оказаться вам полезной, включаяэтот код:

using System;
using System.Threading;

public static class RandomProvider
{    
    private static int seed = Environment.TickCount;

    private static ThreadLocal<Random> randomWrapper = new ThreadLocal<Random>
        (() => new Random(Interlocked.Increment(ref seed)));

    public static Random GetThreadRandom()
    {
        return randomWrapper.Value;
    }
}

Если вы измените свой new Random() вызов на RandomProvider.GetThreadRandom(), который, вероятно, сделает все, что вам нужно (опять же, при условии .NET 4). Это не относится к тестируемости, но один шагза один раз ...

7 голосов
/ 31 августа 2011

Вы не показали нам код для randomNumber. Если это выглядит как

private int randomNumber(int m, int n) {
    Random rg = new Random();
    int y = rg.Next();
    int z = // some calculations using m and n
    return z;
}

Хорошо, тогда есть ваша проблема. Если вы продолжаете создавать новые экземпляры Random, возможно, что они иногда будут иметь одно и то же начальное число (начальное число по умолчанию - системные часы с ограниченной точностью; достаточно быстро их создайте, и они получат одинаковое начальное число), а затем полученная последовательность по этому генератору всегда будет одинаковым.

Чтобы это исправить, вам нужно один раз создать экземпляр Random:

private readonly Random rg = new Random();
private int randomNumber(int m, int n) {
    int y = this.rg.Next();
    int z = // some calculations using m and n
    return z;
}

И чтобы прояснить другую точку, даже если вы сделаете это, вывод из Random все еще не является "истинным" случайным. Это всего лишь псевдослучайный.

0 голосов
/ 21 октября 2017

Создание объекта случайным образом вне метода. (Случайный случайный = новый случайный (); должен быть записан перед методом)

Также важно, чтобы вы понимали, что случайное не действительно случайное .

0 голосов
/ 19 августа 2015

если вы генерируете случайные числа в цикле, это, вероятно, не будет случайным. потому что случайные числа в основном создаются внутри текущего системного времени. Поэтому поместите этот код в цикл:

Thread.Sleep(10);

Таким образом, система перейдет в спящий режим на 10 мсек. И вы получите новое свежее случайное число. Это гарантированное решение. Но это также повлияет на производительность системы.

0 голосов
/ 31 августа 2011

Что такое randomNumber?

Как правило, создается генератор псевдослучайных чисел (со связанными со временем вещами или с чем-то случайным, например, между двумя нажатиями клавиш, сетевыми пакетами или чем-то подобным).*

Вы не указываете, какой генератор вы используете, и как его сеют.

...