Генератор случайных чисел PRNG Park-Miller-Carta всегда возвращает 2.33E-10 - PullRequest
2 голосов
/ 15 апреля 2009

Я пытаюсь реализовать реализацию генератора случайных чисел Park-Miller-Carta PRNG на Java.

Ниже приведена реализация функции Random в ActionScript 3 отсюда .

return (_currentSeed = (_currentSeed * 16807) % 2147483647) / 0x7FFFFFFF
                                                          + 0.000000000233;

Мне не очень повезло, что это работает на Java:

int seed = 20; //for example.

public double random() {
    seed = (seed * 16807) % 2147483647;
    return seed / 0x7FFFFFFF + 0.000000000233;
}

Это всегда возвращает 2.33E-10. Есть идеи, что я делаю не так в Java? (Код AS3 возвращает 0.0001565276181885122, затем 0.6307557630963248 для первых двух ответов с начальным числом 20).

Ответы [ 4 ]

6 голосов
/ 15 апреля 2009
seed / 0x7FFFFFFF

- целочисленная операция, поскольку оба аргумента являются целыми числами. Целочисленное деление всегда округляет «истинный» результат вниз. В этом случае истинный результат находится между 0 и 1, поэтому операция всегда возвращает 0.

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

return (double)seed / 0x7FFFFFFF + 0.000000000233;
2 голосов
/ 15 апреля 2009

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

Но я думаю, что исправление этого конкретного кода является чем-то особенным. Зачем беспокоиться об этом в первую очередь? Он использует по сути тот же класс алгоритма, что и java.lang.Random!

Если вам нужен быстрый генератор, рассмотрите генератор XORShift . Если вам нужен генератор хорошего качества, у вас есть SecureRandom из коробки (хотя он намного медленнее), рассмотрите алгоритм Numeric Recipes (довольно быстрый, комбинированный генератор), который вы можете реализовать в Java как следующим образом:

public class HighQualityRandom extends Random {

  private Lock l = new ReentrantLock();
  private long u;
  private long v = 4101842887655102017L;
  private long w = 1;

  public HighQualityRandom() {
    this(System.nanoTime());
  }
  public HighQualityRandom(long seed) {
    l.lock();
    u = seed ^ v;
    nextLong();
    v = u;
    nextLong();
    w = v;
    nextLong();
    l.unlock();
  }

  @Override
  public long nextLong() {
    l.lock();
    try {
      u = u * 2862933555777941757L + 7046029254386353087L;
      v ^= v >>> 17;
      v ^= v << 31;
      v ^= v >>> 8;
      w = 4294957665L * (w & 0xffffffff) + (w >>> 32);
      long x = u ^ (u << 21);
      x ^= x >>> 35;
      x ^= x << 4;
      return (x + v) ^ w;
    } finally {
      l.unlock();
    }
  }

  protected int next(int bits) {
    return (int) (nextLong() >>> (64-bits));
  }

}

Это скопировано из некоторого кода, где я хотел, чтобы он был параллельным; вы можете в принципе избавиться от блокировки или просто использовать обычную синхронизацию.

Если вы абсолютно настаиваете на использовании Park-Miller-Carta, я бы, по крайней мере, обернул его в подкласс Random и позволил java.util.Random позаботиться о преобразовании целых чисел в двойные и т. Д. - в конце концов, это то, что расширяемо библиотеки на объектно-ориентированном языке предназначены для ...

1 голос
/ 15 апреля 2009

Заменить:

return seed / 0x7FFFFFFF+0.000000000233;

с:

return (double)seed / 0x7FFFFFFF+0.000000000233;
0 голосов
/ 15 апреля 2009

Приоритет оператора.

(seed / 0x7FFFFFFF)+0.000000000233;

это то, что у вас есть на самом деле. Это то, что вы имели в виду?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...