Каково качество реализации класса Random в .NET?
У меня есть два вопроса относительно реализации класса Random
в .NET Framework 4.6 (код доступен здесь):
-
В чем смысл установки аргумента Seed
в 1
в конце конструктора? Кажется, это скопировано из Numerical Recipes в C (2-е изд.), Где это имело какой-то смысл, но в С# оно не имеет.
-
В книге (Численные рецепты в C (2-е изд.)) указано, что в поле inextp
установлено значение 31
, потому что:
Постоянная 31 является специальной; см. Кнут.
Однако в реализации .NET это поле имеет значение 21
. Зачем? Остальная часть кода, похоже, внимательно следит за кодом из книги, за исключением этой детали.
Ответы
Ответ 1
Что касается проблемы intexp
, это ошибка, которая Microsoft подтвердила и отказалась исправлять из-за проблем с обратной совместимостью.
Действительно, вы обнаружили настоящую проблему с реализацией Random. Мы обсудили это в команде и с некоторыми нашими партнерами и пришли к выводу, что, к сожалению, мы не можем решить эту проблему прямо сейчас. Причина в том, что некоторые приложения полагаются на то, что при инициализации с одним и тем же семенем генератор создает одну и ту же псевдослучайную последовательность. Даже если изменение к лучшему, оно сломает приложения, которые сделали это предположение, как только они перешли к "фиксированной" версии.
Ответ 2
В другом контексте:
A назад я полностью проанализировал эту реализацию. Я нашел несколько отличий.
A первый (отлично) - это другое большое значение (MBIG
). Numerical Recipies утверждает, что Кнут дает понять, что любое большое значение должно работать, поэтому это не проблема, и Microsoft разумно предпочла использовать наибольшее значение 32-битного целого числа.
Вторая - та константа, о которой вы говорили. Это большая сделка. Как минимум, это существенно уменьшит период. Сообщалось, что эффекты на самом деле хуже, чем это.
Но затем приходит еще одна особенно неприятная разница. В буквальном смысле это подразумевает смещение вывода (поскольку оно делает это напрямую), а также, вероятно, повлияет на период ГСЧ.
Итак, что это за второй вопрос? Когда .NET впервые появилась, Microsoft не понимала, что RNG, который они закодировали, был включен на обоих концах, и они задокументировали его как эксклюзивный на максимальном конце. Чтобы исправить это, команда безопасности добавила довольно злую строку кода: if (retVal == MBIG) retVal--;
. Это очень к сожалению, поскольку правильное исправление буквально будет всего лишь 4 добавленными символами (плюс пробелы).
Правильное исправление заключалось бы в изменении MBIG
на int.MaxValue-1
, но переключить Sample()
на использование MBIG+1
(т.е. продолжать использовать int.MaxValue
). Это гарантировало бы, что этот образец имеет диапазон [0.0, 1.0), не вводя никакого смещения, и только изменяет значение MBIG
, которое Numerical Recipies говорит, что Knuth сказал, что это прекрасно.