Obscure VBMath random numbers generator behavior - c#

I want to repeat a random number sequence generated by a legacy software using the VBMath.Rnd and VBMath.Randomize functions in VB .NET
Reading on the documentation for those functions on MSDN i found that you are supposed to "reset" the generator calling Rnd with a negative value if you want the same seed to give you the same result sequence each time.
But doing some tests... things didn't work as expected.
The legacy software does something like this at the start of the application on different executions:
float[] rNums = new float[4];
VBMath.Randomize(154341.77394338892);
for (int index = 0; index < 4; index++)
{
rNums[index] = VBMath.Rnd();
}
And my code does something like this:
VBMath.Rnd(-1);
VBMath.Randomize(154341.77394338892);
for (int index = 0; index < 4; index++)
{
Console.WriteLine("rNum[" + index + "] " + rNums[index] + " = " + VBMath.Rnd());
}
The results for this test are:
rNum[0] 0,6918146 = 0,2605162
rNum[1] 0,5121228 = 0,4748411
rNum[2] 0,8309224 = 0,8112976
rNum[3] 0,972851 = 0,8011347
The sequence that i want to reproduce in the second code any number of times is the sequence generated from the hard coded initial state of the generator. That means the sequence you would get if you run the first code alone.
I can not change the first code.
Any idea on why the VBMath.Rnd and VBMath.Randomize functions arent working as expected?
Did i miss something?
ANSWER
Thee problem is that since the legacy code doesn't call Rnd with a negative value, the generator doesn't clear its state and the call to Rnd gets chained to the previous value of the seed (in this case, the hard-coded value).
To solve the problem and be able to repeat the process all over again without all the problems that would imply "reproducing" the initial state, i cloned the generator code and patched it so i could reproduce the same situation every time depending on a parameter.
I know.. its ugly.. but it solves my problem (Btw i also know that there are some rounding errors and that the generated values are not exact.. they differ in like the last digit or something) but i don't need exact precision.
The rounding error probably comes from my choice of language for the cloning of the algorithm. If someone could help out on how to get the exact same result (match the rounding errors) that would be nice.
The patched code follows.
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);
}
}

MSDN says:
To repeat sequences of random numbers, call Rnd with a negative argument immediately before using Randomize with a numeric argument. Using Randomize with the same value for Number does not repeat the previous sequence.
Only one of the code samples you show calls Rnd with a negative argument immediately before using Randomize with a numeric argument.
If code B has a call to Rnd(-1), it should generate the same sequence on all runs. If the sequence generated by a run of code B ( with Rnd(-1) ) repeated that generated by a run of code A ( without Rnd(-1) ), then the different runs of code A would have have to generate the same sequence. This contradicts the information in MSDN.

The second set of code works as expected and will repeatedly give you the same set of 4 numbers.
The first set does not, because it lacks the Rnd(-1) entry. As MSDN says :
Using Randomize with the same value for Number does not repeat the previous sequence
Running the first set 3 times in a row gives this :
rNum[0] 0 = 0.6918146
rNum[1] 0 = 0.5121228
rNum[2] 0 = 0.8309224
rNum[3] 0 = 0.972851
rNum[0] 0 = 0.5982737
rNum[1] 0 = 0.323263
rNum[2] 0 = 0.05594879
rNum[3] 0 = 0.5724301
rNum[0] 0 = 0.5555484
rNum[1] 0 = 0.8296129
rNum[2] 0 = 0.6523779
rNum[3] 0 = 0.6867073
Removing the Rnd(-1) entry from your second set of code gives the same results as the first set. The functions work as expected. Randomize seeds the sequence, but does not restart it - only Rnd(negative number) does that.
Basically, the first set of code is starting the random number generation at a point in the sequence which you have no control over.

You've answered your main question long ago and I'm a bit late to the party. This is my first post so I don't have the reputation to add a comment on your secondary question about rounding errors but here goes:
I recently had call to use Reflector to decompile the implementation of VBMath. I wanted to make a non static version so I could have a multiple threadsafe VB6 compatible Rnd() sequences going at the same time.
I hit the same accuracy errors as you. It took me a while but I found that Reflector (I think) bungled one of the constants:
In the Rnd(float Number) function, change:
return (((float)rndSeed) / 1.677722E+07f);
to
return (((float)rndSeed) / 16777216f);
How Visual Basic Generates Pseudo-Random Numbers for the RND Function
has the correct constant.

ANSWER
The problem is that since the legacy code doesn't call Rnd with a negative value, the generator doesn't clear its state and the call to Rnd gets chained to the previous value of the seed (in this case, the hard-coded value).
To solve the problem and be able to repeat the process all over again without all the problems that would imply "reproducing" the initial state, i cloned the generator code and patched it so i could reproduce the same situation every time depending on a parameter.
I know.. its ugly.. but it solves my problem (Btw i also know that there are some rounding errors and that the generated values are not exact.. they differ in like the last digit or something) but i don't need exact precision.
The rounding error probably comes from my choice of language for the cloning of the algorithm. If someone could help out on how to get the exact same result (match the rounding errors) that would be nice.
The patched code follows.
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);
}
}

Just incase anyone wanted here is my Java version
public class Rnd{
private int Xi;
private static int m = (int) Math.pow(2, 24);
private static int a = 0x43fd43fd;
private static int c = 0xc39ec3;
private static int m_rndSeed = 0x50000;
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 = Float.floatToRawIntBits(number)& 0xffffffffL;
rndSeed = (int) ((num3 + (num3 >> 0x18)) & 0xffffffL);
}
rndSeed = (int) (((rndSeed * a) + c) & 0xffffffL);
}
m_rndSeed = rndSeed;
return (((float) rndSeed) / m);
}
}
Using private static int m = (int) Math.pow(2, 24); instead of 1.677722E+07f fixed my rounding issue.

I would stay away from the VB centric functions, and just use the Random class with a fixed seed (I assume the seed, if set, is not time-sensitive).

Related

Random() times 2^32 - 1 always returns even last digit

I have encountered this weird behaviour, which is probably best described by a small example:
Random R = new Random();
for (int i = 0; i < 10_000; i++)
{
double d = R.NextDouble() * uint.MaxValue;
}
Now, the last digit of d before the decimal mark is always even, i.e. int r = (int) (d % 10) is always 0, 2, 4, 6, or 8. There are odd digits on either side, though.
I suspected that multiplying with uint.MaxValue (2^32 - 1) could force some rounding error in the last digits, but since double has more than 50 bits of precision, this should beat uint with about 20 bits to spare after the separator. This behaviour also occurs if I explicitly store uint.MaxValue as a double before the multiplication and use that instead.
Can someone shed any light on this?
This is a deficiency in the .Net Random class.
If you inspect the source code you will see the following comment in the implementation of the private method GetSampleForLargeRange():
// The distribution of double value returned by Sample
// is not distributed well enough for a large range.
// If we use Sample for a range [Int32.MinValue..Int32.MaxValue)
// We will end up getting even numbers only.
This is used in the implementation of Next():
public virtual int Next(int minValue, int maxValue) {
if (minValue>maxValue) {
throw new ArgumentOutOfRangeException("minValue",Environment.GetResourceString("Argument_MinMaxValue", "minValue", "maxValue"));
}
Contract.EndContractBlock();
long range = (long)maxValue-minValue;
if( range <= (long)Int32.MaxValue) {
return ((int)(Sample() * range) + minValue);
}
else {
return (int)((long)(GetSampleForLargeRange() * range) + minValue);
}
}
But it is NOT used for the values returned from NextDouble() (which just returns the value returned from Sample().
So the answer is that NextDouble() is not well-distributed.
You can use RNGCryptoServiceProvider to generate better random numbers, but it's a bit of a fiddle to create the double. From this answer:
static void Main()
{
var R = new RNGCryptoServiceProvider();
var bytes = new Byte[8];
for (int i = 0; i < 10_000; i++)
{
R.GetBytes(bytes);
var ul = BitConverter.ToUInt64(bytes, 0) / (1 << 11);
var d = ul / (double)(1UL << 53);
d *= uint.MaxValue;
Console.WriteLine(d);
}
}

How to perform correct calculation on very large number in .net

I am having problem in performing correct calculation in following as result is overflowing so function is not returning correct answer.
public static UInt64 findNwords(UInt64 nletters, UInt64 length)
{
if (nletters == 1) return 1;
UInt64 half1 = nletters / 2;
UInt64 half2 = nletters - half1;
UInt64 loc0 = half1;
UInt64 loc1 = half2;
UInt64 curr = 1;
for (UInt64 i = 1; i < length; i++)
{
curr = (loc0 + loc1);
loc0 = loc1;
loc1 = curr;
}
return (curr * half2) % 100000007;
}
Calling findNwords(1000, 500) should return 6109294. In my case it's returning 19610514. Please help.
Your program is overflowing the 64-bit space at iteration 79. You can easily test this by encapsulating the for-loop with an checked-block:
checked
{
for (UInt64 i = 1; i < length; i++)
{
curr = (loc0 + loc1);
loc0 = loc1;
loc1 = curr;
}
}
This will throw an exception if the one value overflows during an operation. You can't do anything about this, besides changing your algorithm by either using lower values or using a System.Numerics.BigInteger instead of UInt64. The second solution requires you to add the System.Numerics assembly as reference. By doing this the algorithm returns the expected value of 6109294 for your example.

Match a sequence of bits in a number and then convert the match into zeroes?

My assignment is to search through the binary representation of a number and replace a matched pattern of another binary representation of a number. If I get a match, I convert the matching bits from the first integer into zeroes and move on.
For example the number 469 would be 111010101 and I have to match it with 5 (101). Here's the program I've written so far. Doesn't work as expected.
using System;
namespace Conductors
{
class Program
{
static void Main(string[] args)
{
//this is the number I'm searching for a match in
int binaryTicket = 469;
//This is the pattern I'm trying to match (101)
int binaryPerforator = 5;
string binaryTicket01 = Convert.ToString(binaryTicket, 2);
bool match = true;
//in a 32 bit integer, position 29 is the last one I would
//search in, since I'm searching for the next 3
for (int pos = 0; pos < 29; pos++)
{
for (int j = 0; j <= 3; j++)
{
var posInBinaryTicket = pos + j;
var posInPerforator = j;
int bitInBinaryTicket = (binaryTicket & (1 << posInBinaryTicket)) >> posInBinaryTicket;
int bitInPerforator = (binaryPerforator & (1 << posInPerforator)) >> posInPerforator;
if (bitInBinaryTicket != bitInPerforator)
{
match = false;
break;
}
else
{
//what would be the proper bitwise operator here?
bitInBinaryTicket = 0;
}
}
Console.WriteLine(binaryTicket01);
}
}
}
}
Few things:
Use uint for this. Makes things a hell of a lot easier when dealing with binary numbers.
You aren't really setting anything - you're simply storing information, which is why you're printing out the same number so often.
You should loop the x times where x = length of the binary string (not just 29). There's no need for inner loops
static void Main(string[] args)
{
//this is the number I'm searching for a match in
uint binaryTicket = 469;
//This is the pattern I'm trying to match (101)
uint binaryPerforator = 5;
var numBinaryDigits = Math.Ceiling(Math.Log(binaryTicket, 2));
for (var i = 0; i < numBinaryDigits; i++)
{
var perforatorShifted = binaryPerforator << i;
//We need to mask off the result (otherwise we fail for checking 101 -> 111)
//The mask will put 1s in each place the perforator is checking.
var perforDigits = (int)Math.Ceiling(Math.Log(perforatorShifted, 2));
uint mask = (uint)Math.Pow(2, perforDigits) - 1;
Console.WriteLine("Ticket:\t" + GetBinary(binaryTicket));
Console.WriteLine("Perfor:\t" + GetBinary(perforatorShifted));
Console.WriteLine("Mask :\t" + GetBinary(mask));
if ((binaryTicket & mask) == perforatorShifted)
{
Console.WriteLine("Match.");
//Imagine we have the case:
//Ticket:
//111010101
//Perforator:
//000000101
//Is a match. What binary operation can we do to 0-out the final 101?
//We need to AND it with
//111111010
//To get that value, we need to invert the perforatorShifted
//000000101
//XOR
//111111111
//EQUALS
//111111010
//Which would yield:
//111010101
//AND
//111110000
//Equals
//111010000
var flipped = perforatorShifted ^ ((uint)0xFFFFFFFF);
binaryTicket = binaryTicket & flipped;
}
}
string binaryTicket01 = Convert.ToString(binaryTicket, 2);
Console.WriteLine(binaryTicket01);
}
static string GetBinary(uint v)
{
return Convert.ToString(v, 2).PadLeft(32, '0');
}
Please read over the above code - if there's anything you don't understand, leave me a comment and I can run through it with you.

Methods that copies the value of the instance variable to reverse digits & swap digits of a number in C#

I m stuck with the my C# program, just can't figure out how to make it work.
Firstly I am supposed to create an instance variable for a 4digit Number and create getter & setter methods for that instance variable (Done with that).
Secondly I am supposed to create 4 methods:
method for summing up the digits.
method for copying the value of the instance variable & reversing the number.
method for copying the value of the instance variable & swapping first & last digits and 4th or swapping second & third digits.
Lastly there should be a Main method Main method that creates an object, assigns a four-digit number to the instance variable, and tests all methods.
Now I have come up with the following code but all the methods return me 0 except the SumDigits one which is working fine.
I will be grateful if some one can just help out in fixing this code.
public class myClass
{
public int number=0;//instance variable
public int r;
public int sum = 0;
public int rem ;
public int reverse = 0;
public string firstlast;
public string secondthird;
public myClass()//Constructor
{
}
// set & get property
public int MyNumber
{
get
{
return number;
}
set
{
number=value;
}
}
//Setter and getter methods
public void SetNumber(int fourNumber)
{
number = fourNumber;
}
public int GetNumber()
{
return number;
}
//method to calculate and return the sum of the digits of the instance variable
public int SumDigits()
{
for (int i = 0; i < 5; i++)
{
r = number % 10;
number = number / 10;
sum = sum + r;
}
return sum;
}
//method that copies the value of the instance variable, and then returns the value of the copy in a reverse order
public int RevNum()
{
while (number> 0)
{
reverse = reverse * 10 + (number - (number / 10) * 10);
number = number / 10;
}
return reverse;
}
//method that copies the value of the instance variable & swap the first and last digit of the number
public string FirstLastDigit()
{
firstlast = number.ToString();
while (firstlast.Length > 1)
{
{
char[] digits = firstlast.ToCharArray();
char firstDigit = digits[0];
digits[0] = digits[digits.Length - 1];
digits[digits.Length - 1] = firstDigit;
Console.WriteLine(new string(digits));
}
}
return firstlast;
}
//method that copies the value of the instance variable & swaps the second and third digit of the number
public string SecondThirdDigit()
{
secondthird = number.ToString();
while (firstlast.Length > 1)
{
char[] digits = secondthird.ToCharArray();
char firstDigit = digits[1];
digits[1] = digits[digits.Length - 2];
digits[digits.Length - 2] = firstDigit;
Console.WriteLine(new string(digits));
}
return secondthird;
}
//Main method that creates an object, assigns a four-digit number to the instance variable, and tests all methods
public static void Main(string[] args)
{
myClass myNum = new myClass();
myNum.GetNumber();
Console.WriteLine("ENTER THE NUMBER");
myNum.number = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("\nNumber is " + myNum.number);
Console.ReadLine();
myNum.SumDigits();
Console.WriteLine("\nSum is " + myNum.sum);
myNum.RevNum();
Console.WriteLine("\nThe reverse of number " + myNum.reverse);
myNum.FirstLastDigit();
Console.WriteLine("\nThe swap of first and last digit is" + myNum.firstlast);
myNum.SecondThirdDigit();
Console.WriteLine("\nExchange of Second and third digit is" + myNum.secondthird);
Console.ReadLine();
}
}
}
I had a quick look for you and your code was 99% correct. Nice work there. I only had to make a few minor changes to get everything working.
For the SumDigits() and RevNum(), you were using number directly. The problem with that is when you did number = number / 10, you were overwriting the number the user entered with a new one. Eventually number became 0 after Sum() was done... so by the time the other 3 methods executed they were trying to do their actions on the number 0.
To fix this, I assigned number to a temp variable in these two methods.
For the SecondThirdDigit() and FirstLastDigit() methods, your formula was also correct. However, you assigned firstlast to a 4-digit number and then had a while loop that executed while firstlast.length > 1. Because the length was 4 and your loop never changed this, this became a never-ending loop. I'm not sure why you thought you needed a loop here?
Anyway to fix this, I removed the loop and your code now works. Also you were writing the result of this directly to the console instead of assigning it to your firstlast and secondthird variables, so I fixed those as well.
Other than that, everything else is unchanged from your code. Here's an updated version for you that should work properly now:
//method to calculate and return the sum of the digits of the instance variable
public int SumDigits()
{
// Assigned number to temp variable
int tempNumber = number;
for (int i = 0; i < 5; i++)
{
r = tempNumber % 10;
tempNumber = tempNumber / 10;
sum = sum + r;
}
return sum;
}
//method that copies the value of the instance variable, and then returns the value of the copy in a reverse order
public int RevNum()
{
// Assigned number to temp variable
int tempNumber = number;
while (tempNumber > 0)
{
reverse = reverse * 10 + (tempNumber - (tempNumber / 10) * 10);
tempNumber = tempNumber / 10;
}
return reverse;
}
//method that copies the value of the instance variable & swap the first and last digit of the number
public string FirstLastDigit()
{
firstlast = number.ToString();
char[] digits = firstlast.ToCharArray();
char firstDigit = digits[0];
digits[0] = digits[digits.Length - 1];
digits[digits.Length - 1] = firstDigit;
// Assigned result to firstlast instead of console output
firstlast = new string(digits);
return firstlast;
}
//method that copies the value of the instance variable & swaps the second and third digit of the number
public string SecondThirdDigit()
{
secondthird = number.ToString();
char[] digits = secondthird.ToCharArray();
char firstDigit = digits[1];
digits[1] = digits[digits.Length - 2];
digits[digits.Length - 2] = firstDigit;
// Assigned result to secondthird instead of console output
secondthird = new string(digits);
return secondthird;
}

itoa conversion in C#

It was an interview question asked to me - write itoa conversion without using any builtin functions.
The following is the algorithm I am using. But ('0' + n % 10); is throwing an error:
cannot convert string to int
private static string itoa(int n)
{
string result = string.Empty;
char c;
bool sign = n > 0 ? true : false;
while (true)
{
result = result + ('0' + n % 10); //'0'
n = n / 10;
if(n <= 0)
{
break;
}
}
if(sign)
{
result = result + '-';
}
return strReverse(result);
}
I'm unclear why you'd want to do this; just call ToString on your integer. You can specify whatever formatting you need with the various overloads.
As #minitech commented, we usually just use ToString() to do that in C#. If you really want to write the algorithm on your own, the following is an implementation:
public static partial class TestClass {
public static String itoa(int n, int radix) {
if(0==n)
return "0";
var index=10;
var buffer=new char[1+index];
var xlat="0123456789abcdefghijklmnopqrstuvwxyz";
for(int r=Math.Abs(n), q; r>0; r=q) {
q=Math.DivRem(r, radix, out r);
buffer[index-=1]=xlat[r];
}
if(n<0) {
buffer[index-=1]='-';
}
return new String(buffer, index, buffer.Length-index);
}
public static void TestMethod() {
Console.WriteLine("{0}", itoa(-0x12345678, 16));
}
}
It works only for int. The range int is -2147483648 to 2147483647, the length in the string representation would be max to 11.
For the signature of itoa in C is char * itoa(int n, char * buffer, int radix);, but we don't need to pass the buffer in C#, we can allocate it locally.
The approach that add '0' to the remainder may not work when the radix is greater than 10; if I recall correctly, itoa in C supports up to 36 based numbers, as this implementation is.
('0' + n % 10) results in an int value, so you should cast it back to char. There are also several other issues with your code, like adding - sign on the wrong side, working with negative values, etc.
My version:
static string itoa(int n)
{
char[] result = new char[11]; // 11 = "-2147483648".Length
int index = result.Length;
bool sign = n < 0;
do
{
int digit = n % 10;
if(sign)
{
digit = -digit;
}
result[--index] = (char)('0' + digit);
n /= 10;
}
while(n != 0);
if(sign)
{
result[--index] = '-';
}
return new string(result, index, result.Length - index);
}

Categories