Ваш код выглядит правильным и непредвзятым. Тем не менее, вы можете захотеть немного его изменить, если вы после исполнения, и в зависимости от скорости случайного источника, который вы используете. Идея состоит в том, чтобы замаскировать еще несколько битов, чтобы случайное значение r
было меньше, чем 2*bound
. Если длина в битах границы составляет x
(длина , исключая знаковый бит), то вы создаете буфер размером n = ((x+8)/8)
байтов и маскируете верхний (n*8-x)
биты. В C # это должно выглядеть так:
var x = BitLength(bound);
var n = ((x + 8) / 8);
var buffer = new Byte[n];
var mask = 0xFF >> (8 * n - x);
while (true) {
source.GetBytes(buffer);
buffer[n - 1] &= mask;
var r = new BigInteger(buffer);
if (r < bound)
return r;
}
С этим типом кода вам, возможно, придется запрашивать больше случайных байтов из источника, но вы избегаете модульного сокращения (оператор %
). Правильный PRNG должен быть намного быстрее, чем деление на большие целые числа, так что это должен быть лучший компромисс - но это действительно зависит от производительности случайного источника, и, поскольку это вопрос производительности, он не может быть полностью ответил, не пытаясь. Скорее всего, как часть общей реализации SRP, в любом случае это не будет иметь каких-либо заметных различий.
Я использовал выше функцию BitLength()
, которая, по-видимому, не существует в C # (класс Java BigInteger
имеет метод bitLength()
, но, очевидно, Microsoft забыла включить его в свою целочисленную реализацию, которая является позор, потому что кажется, что реализация действительно включает частное поле с именем _bits
, которое поддерживает это значение). Длина в битах может быть эффективно вычислена из представления в байтах значения bound
. Таким образом, код станет примерно таким:
var buffer = bound.ToByteArray();
var n = buffer.length;
var msb = buffer[n - 1];
var mask = 0;
while (mask < msb)
mask = (mask << 1) + 1;
while (true) {
source.GetBytes(buffer);
buffer[n - 1] &= mask;
var r = new BigInteger(buffer);
if (r < bound)
return r;
}
Я использую тот факт, что длина в байтах кодировки bound
, возвращаемая ToByteArray()
, - это именно то значение n
, которое мне нужно.