Исключительная ситуация переполнения стека при использовании случайных чисел - PullRequest
1 голос
/ 19 февраля 2012
public int S1x;

public void Execute()
{
    Random random = new Random();
    S1x = random.Next(14, 146);
    if (S1x % 15 != 0)
        Fix(S1x);
}

public int Fix(int SX)
{                
    Random randomG = new Random();
    SX = randomG.Next(14, 146);
    if (SX % 15 != 0)                               
        Fix(SX); // This is the recursion

    return SX;
}

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

Исключение System.StackOverflowException было обработано Необработанное исключение типа «System.StackOverflowException» произошло в mscorlib.dll {Невозможно оценить выражение, поскольку текущий поток находится в состоянии переполнения стека.}

И, да, я знаю, что есть более простой способ сделать это, но мой метод установки значения равным случайному числу между 14 и 146, при этом также гарантируя, что оно кратно 15, все равно должен работать тем не менее.

Я просто очень запутался и в том, почему иногда мне выдается только сообщение об ошибке.

Так что с ним не так? Почему его называют бесконечной рекурсией, хотя в этом нет ничего бесконечного?

Ответы [ 2 ]

18 голосов
/ 19 февраля 2012

Даже если вы решите проблему - то, что вы создаете новый объект Random каждый раз, когда сеяно на основе текущего времени - ваш код все еще не является ни правильным, ни эффективным. Помните, что рекурсивный метод должен иметь следующие характеристики, чтобы быть корректным:

  • Тривиальная задача может быть решена без рекурсии
  • Рекурсивный шаг уменьшает проблему
  • Конечное число рекурсий всегда сводит проблему к тривиальной проблеме

У вас нет ни одного из этих свойств, поэтому рекурсия - неправильное решение .

Если вам нужно случайное число от 14 до 146, а также кратное 15, вам не нужна рекурсия. Единственными такими числами являются 15, 30, 45, 60, 75, 90, 105, 120 и 135. Так что просто скажите:

private int[] array = { 15, 30, 45, 60, 75, 90, 105, 120, 135 };
private Random random = new Random();
...

return array[random.Next(0, array.Length)];

или даже лучше: выберите случайное число от одного до девяти и умножьте его на 15.

return random.Next(1, 10) * 15;
4 голосов
/ 19 февраля 2012

new Random() заполняет себя Environment.TickCount (миллисекундами с момента запуска системы) для генерации псевдослучайных чисел, если вы заполняете его одинаковым номером дважды, первый вызов rand.Next(x, y); будет возвращать одно и то же значение каждый раз.

    public int Fix(int SX)
    {

        Random randomG = new Random();
        SX = randomG.Next(14, 146);
        if (SX % 15 != 0)
        {

            Fix(SX); // This is the recursion
        }           
        return SX;
    }

Поскольку вы создаете новый экземпляр Random каждый раз, когда вы запускаете эту функцию (в течение одной и той же миллисекунды), она генерирует одно и то же число.Так что он будет вызываться тысячи раз, прежде чем генерировать новый номер.Если вы сделаете это:

    Random randomG = new Random();
    public int Fix(int SX)
    {
        SX = randomG.Next(14, 146);
        if (SX % 15 != 0)
        {
            Fix(SX); // This is the recursion
        }           
        return SX;
    }

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

Редактировать: Я забыл упомянуть, почему рекурсия плохая идея для достижения этой цели.На самом деле не используйте приведенный выше код, это все еще плохое решение.Я не буду сейчас исправлять свой ответ, потому что гораздо лучший ответ был опубликован

...