Я хочу повторить последовательность случайных чисел, сгенерированную унаследованным программным обеспечением с использованием функций VBMath.Rnd и VBMath.Randomize в VB .NET
Чтение документации по этим функциям на MSDN Я обнаружил, что вы должны "сбросить" генератор, вызывающий Rnd, с отрицательным значением, если вы хотите, чтобы одно и то же начальное число давало вам одинаковую последовательность результатов каждый время.
Но при выполнении некоторых тестов ... все работало не так, как ожидалось.
Устаревшее программное обеспечение делает что-то вроде этого при запуске приложения в различных исполнениях:
float[] rNums = new float[4];
VBMath.Randomize(154341.77394338892);
for (int index = 0; index < 4; index++)
{
rNums[index] = VBMath.Rnd();
}
И мой код делает что-то вроде этого:
VBMath.Rnd(-1);
VBMath.Randomize(154341.77394338892);
for (int index = 0; index < 4; index++)
{
Console.WriteLine("rNum[" + index + "] " + rNums[index] + " = " + VBMath.Rnd());
}
Результаты этого теста:
rNum[0] 0,6918146 = 0,2605162
rNum[1] 0,5121228 = 0,4748411
rNum[2] 0,8309224 = 0,8112976
rNum[3] 0,972851 = 0,8011347
Последовательность, которую я хочу воспроизвести во втором коде любое количество раз, является последовательностью, сгенерированной из жестко закодированного начального состояния генератора. Это означает последовательность, которую вы получите, если будете запускать только первый код.
Я не могу изменить первый код.
Есть идеи, почему функции VBMath.Rnd и VBMath.Randomize не работают должным образом?
Я что-то пропустил?
ОТВЕТ
Проблема в том, что, поскольку унаследованный код не вызывает Rnd с отрицательным значением, генератор не очищает свое состояние, и вызов Rnd связывается с предыдущим значением начального числа (в данном случае сложный значение).
Чтобы решить проблему и иметь возможность повторять процесс снова и снова без всех проблем, которые подразумевали бы «воспроизведение» исходного состояния, я клонировал код генератора и исправил его, чтобы я мог воспроизводить одну и ту же ситуацию каждый раз в зависимости от параметр.
Я знаю ... это некрасиво ... но это решает мою проблему (кстати, я также знаю, что есть некоторые ошибки округления и что сгенерированные значения не являются точными .. они отличаются как последняя цифра или что-то в этом роде), но я не не нужна точная точность.
Ошибка округления, вероятно, связана с тем, что я выбрал язык для клонирования алгоритма. Если бы кто-то мог помочь с тем, как получить точно такой же результат (сопоставить с ошибками округления), это было бы неплохо.
Патч-код следует.
public sealed class RndGenerator
{
static int m_rndSeed = 0x50000;
// This is the value that the programmer sets the seed at ProjectData object
// initialization
const int CONSTANT_INIT_RNDSEED = 0x50000;
// Methods
private static float GetTimer()
{
DateTime now = DateTime.Now;
return (float)(((((60 * now.Hour) + now.Minute) * 60) + now.Second) + (((double)now.Millisecond) / 1000.0));
}
public static void Randomize()
{
float timer = GetTimer();
int rndSeed = m_rndSeed;
int num = BitConverter.ToInt32(BitConverter.GetBytes(timer), 0);
num = ((num & 0xffff) ^ (num >> 0x10)) << 8;
rndSeed = (rndSeed & -16776961) | num;
m_rndSeed = rndSeed;
}
public static void Randomize(double Number)
{
Randomize(Number, false);
}
public static void Randomize(double Number, bool useHardCodedState)
{
int num;
int rndSeed = 0;
if (useHardCodedState)
rndSeed = CONSTANT_INIT_RNDSEED;
else
rndSeed = m_rndSeed;
if (BitConverter.IsLittleEndian)
{
num = BitConverter.ToInt32(BitConverter.GetBytes(Number), 4);
}
else
{
num = BitConverter.ToInt32(BitConverter.GetBytes(Number), 0);
}
num = ((num & 0xffff) ^ (num >> 0x10)) << 8;
rndSeed = (rndSeed & -16776961) | num;
m_rndSeed = rndSeed;
}
public static float Rnd()
{
return Rnd(1f);
}
public static float Rnd(float Number)
{
int rndSeed = m_rndSeed;
if (Number != 0.0)
{
if (Number < 0.0)
{
long num3 = BitConverter.ToInt32(BitConverter.GetBytes(Number), 0);
num3 &= (long)0xffffffffL;
rndSeed = (int)((num3 + (num3 >> 0x18)) & 0xffffffL);
}
rndSeed = (int)(((rndSeed * 0x43fd43fdL) + 0xc39ec3L) & 0xffffffL);
}
m_rndSeed = rndSeed;
return (((float)rndSeed) / 1.677722E+07f);
}
}