В другом StackExchange был предложен следующий алгоритм (основанный на арифметическом кодировании) как эффективный метод для получения результатов 7-сторонней матрицы, когда все, что дано, - это 6-сторонняя матрица:
int rand7()
{
static double a=0, width=7; // persistent state
while ((int)(a+width) != (int)a)
{
width /= 6;
a += (rand6()-1)*width;
}
int n = (int)a;
a -= n;
a *= 7; width *= 7;
return (n+1);
}
Не будучи истинным математиком, я сделаю все возможное, чтобы объяснить, как работает этот алгоритм:
В начале каждого вызова rand7()
, width
есть отношение 7 s/6 t а a
- неотрицательное значение с тем свойством, что a + width
лежит в интервале [0, 7) после базового случая. При вводе цикла while
width
- максимальное значение, которое можно добавить в a
. Если floor(a + width)
отличается от floor(a)
, то случайный выбор {0, width
* 1/6, width
* 1/3, width
* 1/2, width
* 2/3, width
* 5/6} добавляется к a
, а показатель t
увеличивается на единицу (уменьшая значение width
степенью 6). Заметим, что после итерации свойство, что a + width
лежит в интервале [0, 7), остается инвариантным. Когда width
становится меньше разницы ceil(a) - a
, итерации останавливаются. Цикл добавляет больше энтропии в a
, пока это может фактически повлиять на результат броска кубика, и интуитивно это создает случайное действительное число в диапазоне [0, 7] с использованием базы 6. После выхода из петля, рулон матрицы считается floor(a) + 1
, а a
сводится к его дробной части. В этой точке a + width
лежит в интервале [0, 1). Чтобы подготовиться к следующему вызову и сохранить свойство инварианта, как a
, так и width
масштабируются в 7 раз (для width
это увеличивает показатель s
на 1).
Приведенное выше объясняет работу индуктивного шага. Анализ базового случая оставлен в качестве упражнения для заинтересованного читателя.
Конечно, с точки зрения эффективности использование арифметики с плавающей запятой сразу же появляется в виде перетаскивания производительности (при условии, что производительность rand6()
уже достаточна и сама не может быть улучшена). Поддерживая этот алгоритм арифметического кодирования, каков наилучший подход к удалению использования плавающей запятой?