В другом 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() уже достаточна и сама не может быть улучшена). Поддерживая этот алгоритм арифметического кодирования, каков наилучший подход к удалению использования плавающей запятой?