Нормальное распределение Java со средней разницей, отличной от нуля, и стандартным отклонением, отличным от единицы

Мой профессор моделирует приход клиентов в банк. В нем говорится, что клиенты приходят в соответствии с нормальным распределением со средним значением 3,5 и стандартным отклонением 1,3.

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

Код

public class Random {

    private static double second=0;

    static double normal(int stream, double mean, double std){
        double v1=0, v2=0, y1, y2, x1, x2, w=2;

        if (second!=0 ){
            return second;
        }

        while(w>1){
            v1=2*RandomGenerator.rand(stream)-1;
            v2=2*RandomGenerator.rand(stream)-1;
            w=Math.pow(v1,2)+Math.pow(v2,2);
        }

        y1=v1*Math.pow((-2*Math.log(w))/w, 0.5);
        y2=v2*Math.pow((-2*Math.log(w))/w, 0.5);
        x1=mean+y1*std;
        x2=mean+y2*std;
        second=x2;
        return x1;
    }

} 

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

Итак, это мои вопросы.

  1. Что делает second? Это какая-то защита?
  2. Как этот код использует нормальное распределение? Кажется, не могу понять...

person Favolas    schedule 02.05.2012    source источник
comment
Пожалуйста, скажите своему профессору, что он должен научиться правильно называть свои переменные.   -  person chburd    schedule 02.05.2012
comment
@chburd :) Я также думаю, что имя vars должно быть более понятным   -  person Favolas    schedule 02.05.2012
comment
Также сообщите своему профессору, что прибытие обычно моделируется как распределение Пуассона, а не как нормальное распределение.   -  person user207421    schedule 03.05.2012


Ответы (1)


Отвечу на вопросы в обратном порядке. Центральная часть метода представляет собой стандартный метод преобразования двух случайных величин, взятых из равномерного распределения на [0, 1], в две случайные величины, взятых из нормального распределения с заданным средним значением и стандартным отклонением. (Это называется преобразованием Бокса-Мюллера.) Цикл while использует метод тестирования для создания случайной точки (v1, v2) из ​​двумерного равномерного распределения на единичной окружности. Отображение из (v1, v2) в (y1, y2) преобразует равномерное распределение в двумерное нормальное распределение с нулевым средним значением и единичным стандартным отклонением. Затем это сдвигается и масштабируется, чтобы получить (x1, x2) с желаемым средним значением и стандартным отклонением.

Имея в руках метод генерации пар нормально распределенных переменных, остальная часть кода работает следующим образом: если second == 0, это сигнал о том, что необходимо сгенерировать новые значения, поэтому он проваливается через if и генерирует пару значений . Затем он сохраняет одно из значений в second и возвращает другое. При следующем вызове метода он замечает, что в second есть допустимое значение, и немедленно возвращает его, вместо того чтобы генерировать дополнительные значения.

В коде есть серьезная ошибка: second должно быть сброшено до нуля, прежде чем будет возвращено его значение; в противном случае он всегда будет продолжать возвращать второе случайное значение (которое не будет казаться таким случайным после второго вызова). Даже если этот недостаток был исправлен, есть вторая проблема. Каждое другое возвращаемое значение будет иметь нулевую вероятность иметь значение 0. Теоретически это не так уж плохо, потому что вероятность того, что нормально распределенная случайная выборка имеет точно нулевое значение, равна... нулю! Тем не менее, это все еще недостаток.

Я бы переписал код следующим образом:

public class Random {

    private static double second;
    private static boolean secondValid = false;

    static double normal(int stream, double mean, double std) {
        double v1, v2, y1, y2, x1, x2, w;

        if (secondValid) {
            secondValid = false;
            return second;
        }

        do {
            v1 = 2 * RandomGenerator.rand(stream) - 1;
            v2 = 2 * RandomGenerator.rand(stream) - 1;
            w = v1 * v1 + v2 * v2;
        } while (w > 1);

        y1 = v1 * Math.sqrt(-2 * Math.log(w) / w);
        y2 = v2 * Math.sqrt(-2 * Math.log(w) / w);
        x1 = mean + y1 * std;
        x2 = mean + y2 * std;
        second = x2;
        secondValid = true;
        return x1;
    }

}
person Ted Hopp    schedule 02.05.2012
comment
Я бы также удалил все статические ссылки, так как это не является потокобезопасным. - person jontro; 02.05.2012
comment
@jontro - удаление квалификаторов static не поможет безопасности потоков. Это будет просто означать, что можно иметь несколько объектов Random с частными тайниками second. Я думаю, это имеет ограниченную полезность. Параллельный доступ к экземпляру Random по-прежнему будет небезопасным. - person Ted Hopp; 02.05.2012
comment
Да, конечно. Однако, если я использую статический метод, я предполагаю, что он потокобезопасен. Если это изменяемый класс, я бы знал, что нужно либо синхронизировать вызовы, либо создать новый экземпляр, где это имеет смысл. В любом случае, возможно, это выходит за рамки этого вопроса. - person jontro; 02.05.2012
comment
@jontro - я бы не стал предполагать, что статический метод является потокобезопасным просто потому, что он статичен. Так получилось, что Math.random() является потокобезопасным, но это явно задокументировано: Этот метод правильно синхронизирован, чтобы его можно было правильно использовать более чем в одном потоке. - person Ted Hopp; 02.05.2012
comment
@TedHopp Спасибо за ваше объяснение. Только к вещам. Во-первых, я добавил еще один вопрос. Второй. Вы говорите это Затем это смещается и масштабируется, чтобы получить (x1, x2) с желаемым средним значением и стандартным отклонением. Где я могу узнать об этом? Я понимаю, что он делает, но не понимаю, почему. - person Favolas; 02.05.2012
comment
@Favolas — из статьи Википедии Нормальное распределение: N (μ, σ ^ 2) может быть сгенерировано как X = μ + σZ, где Z — стандартная нормаль. Ваш дополнительный вопрос не ясен. Вы просто пытаетесь изменить и масштабировать равномерное распределение? Код, который вы разместили, этого не делает. Было бы лучше разместить отдельный вопрос, так как это другая тема. - person Ted Hopp; 02.05.2012
comment
@TedHopp Спасибо. Убрал вопрос. Хотел знать, как я могу генерировать случайные числа с равномерным распределением при заданном strem, имеющем значение, отличное от 0, и sdt, отличное от 1. Поскольку предоставленный вами код использует единообразное преобразование, мне трудно, это будет просто вопрос повторного использования части кода - person Favolas; 02.05.2012
comment
@Favolas - Сдвиг и масштабирование одного равномерного распределения в другое не так сложно, как переход от равномерного к нормальному, а затем масштабирование. Во-первых, вам не нужно делать это парами (это просто умножить и добавить), поэтому вам не нужно second или какие-либо сложности, необходимые для случая нормального распределения. - person Ted Hopp; 02.05.2012
comment
@Тед. Ok. Таким образом, он просто выполняет do while без v2 удаления y1 и y2 и выполнения x1 = mean + v1 * std;?. Извините за эти вопросы новичка, но, как указано в op, много лет без математики. - person Favolas; 02.05.2012
comment
@Favolas - Нет, цикл while вообще не нужен. Это простая трансформация. Пожалуйста, напишите отдельный вопрос об этом. Равномерное распределение обычно представлено минимумом и максимумом диапазона, а не средним значением и стандартным отклонением. Если вы хотите сдвинуть и масштабировать по среднему и стандартному отклонению, потребуется немного объяснений. Для получения дополнительной информации прочитайте статью Википедии о равномерном распределении. - person Ted Hopp; 02.05.2012