Я помню, как читал о методе эффективного использования случайных битов в статье на веб-сайте, ориентированном на математику, но я больше не могу найти правильные ключевые слова в Google, чтобы найти его, и его нет в истории моего браузера.
Суть поставленной задачи заключалась в том, чтобы взять последовательность случайных чисел в области [domainStart
, domainEnd
) и эффективно использовать биты последовательности случайных чисел для равномерного проецирования в диапазон [rangeStart
, rangeEnd
). И домен, и диапазон — целые числа (вернее, long
s, а не Z). Какой для этого алгоритм?
Что касается реализации, у меня есть функция с такой сигнатурой:
long doRead(InputStream in, long rangeStart, long rangeEnd);
in
основан на CSPRNG (с питанием от аппаратного ГСЧ, обусловленным SecureRandom), который я должен использовать; возвращаемое значение должно быть между rangeStart
и rangeEnd
, но очевидная реализация этого расточительна:
long doRead(InputStream in, long rangeStart, long rangeEnd) {
long retVal = 0;
long range = rangeEnd - rangeStart;
// Fill until we get to range
for (int i = 0; (1 << (8 * i)) < range; i++) {
int in = 0;
do {
in = in.read();
// but be sure we don't exceed range
} while(retVal + (in << (8 * i)) >= range);
retVal += in << (8 * i);
}
return retVal + rangeStart;
}
Я считаю, что это фактически та же идея, что и (rand() * (max - min)) + min
, только мы отбрасываем биты, которые подталкивают нас к max
. Вместо использования оператора по модулю, который может привести к неправильному смещению результатов в сторону меньших значений, мы отбрасываем эти биты и пытаемся снова. Поскольку попадание в CSPRNG может привести к повторному заполнению (что может заблокировать InputStream), я хотел бы избежать потери случайных битов. Генри указывает, что этот код смещается от 0 и 257; Бантар демонстрирует это на примере.
Первое редактирование: Генри напомнил мне, что суммирование вызывает центральную предельную теорему. Я исправил приведенный выше код, чтобы обойти эту проблему.
Второе редактирование: механическая улитка предложила мне посмотреть источник Random.nextInt(). Почитав его какое-то время, я понял, что эта проблема похожа на проблему преобразования базы. Смотрите ответ ниже.
java.util.Random.nextInt
. - person Mechanical snail   schedule 22.09.2013