The second parameter to BitConverter.ToInt32
is an offset into the byte array. It should be 0 for you. There is also no reason to get 1000 byttes if you're using only 4 of them.
A working version in C#:
public static int RandomUniform(int count)
{
if(count <= 0)
throw new ArgumentOutOfRangeException("count");
var rng=new RNGCryptoServiceProvider();
var bytes=new byte[8];
rng.GetBytes(bytes, 0);
return BitConverter.ToUInt64() % (uint)count;
}
Label4.Text = 1000 + RandomUniform(9000);
In VB.net this will look similar to
Public Shared Function RandomUniform(count As Integer) As Integer
If count <= 0 Then
Throw New ArgumentOutOfRangeException("count")
End If
Dim rng = New RNGCryptoServiceProvider()
Dim bytes = New Byte(8) {}
rng.GetBytes(bytes, 0)
Return BitConverter.ToUInt64() Mod CUInt(count)
End Function
This code has two weaknesses:
- It's quite slow. The per-call overhead of
RNGCryptoServiceProvider.GetBytes
is large.
For better performance use a buffer of several kilobytes and only call GetBytes
when it's used up.
It's not perfectly uniform, but the bias should be small enough for most applications.
Even when using a value for count
that exhibits maximal bias you will need over ten billion samples to detect it, whereas for System.Random
a handful are enough. i.e the bias is about 8 billion times smaller than for System.Random
.
If either weakness is unacceptable for you, you can look into my random number generator project which offers fast, secure and unbiased numbers. But this comes at the cost of adding an external library instead of simply writing a few lines of code.