Random number in long range, is this the way?

Viewed 82964

Can somebody verify this method. I need a long type number inside a range of two longs. I use the .NET Random.Next(min, max) function which return int's. Is my reasoning correct if I simply divide the long by 2, generate the random number and finally multiply it by 2 again? Or am I too enthusiastic... I understand that my random resolution will decrease but are there any other mistakes which will lead to no such a random number.

long min = st.MinimumTime.Ticks;    //long is Signed 64-bit integer
long max = st.MaximumTime.Ticks;
int minInt = (int) (min / 2);      //int is Signed 64-bit integer
int maxInt = (int) (max / 2);      //int is Signed 64-bit integer

Random random = new Random();
int randomInt = random.Next(minInt, maxInt);
long randomLong = (randomInt * 2);
17 Answers

This will get you a secure random long:

using (RNGCryptoServiceProvider rg = new RNGCryptoServiceProvider()) 
{ 
    byte[] rno = new byte[9];    
    rg.GetBytes(rno);    
    long randomvalue = BitConverter.ToInt64(rno, 0); 
}

I wrote some Test Methods and check my own method and many of the answers from this and the same questions. Generation of redundant values is a big problem. I found @BlueRaja - Danny Pflughoeft answer at this address Is good enough and did not generate redundant values at least for first 10,000,000s. This is a Test Method:

[TestMethod]
    public void TestRand64WithExtensions()
    {
        Int64 rnum = 0;
        HashSet<Int64> hs = new HashSet<long>();
        Random randAgent = new Random((int)DateTime.Now.Ticks);

        for (int i = 0; i < 10000000; i++)
        {
            rnum = randAgent.NextLong(100000000000000, 999999999999999);
            //Test returned value is greater than zero
            Assert.AreNotEqual(0, rnum);
            //Test Length of returned value
            Assert.AreEqual(15, rnum.ToString().Length);
            //Test redundancy
            if (!hs.Contains(rnum)) { hs.Add(rnum); }
            else
            {
                //log redundant value and current length we received
                Console.Write(rnum + " | " + hs.Count.ToString());
                Assert.Fail();
            }
        }
    }

I didn't want to post this as an answer but I can't stuff this in the comment section and I didn't want to add as an edit to answer without author consent. So pardon me as this is not an independent answer and maybe just a prove to one of the answers.

I wrote a benchmarking C# console app that tests 5 different methods for generating unsigned 64-bit integers. Some of those methods are mentioned above. Method #5 appeared to consistently be the quickest. I claim to be no coding genius, but if this helps you, you're welcome to it. If you have better ideas, please submit. - Dave (sbda26@gmail.com)

enter code here

  static private Random _clsRandom = new Random();
  private const int _ciIterations = 100000;

  static void Main(string[] args)
  {
      RunMethod(Method1);
      RunMethod(Method2);
      RunMethod(Method3);
      RunMethod(Method4);
      RunMethod(Method5);

      Console.ReadLine();
  }

  static void RunMethod(Func<ulong> MethodX)
  {
      ulong ulResult;
      DateTime dtStart;
      TimeSpan ts;

      Console.WriteLine("--------------------------------------------");
      Console.WriteLine(MethodX.Method.Name);
      dtStart = DateTime.Now;
      for (int x = 1; x <= _ciIterations; x++)
          ulResult = MethodX.Invoke();
      ts = DateTime.Now - dtStart;

      Console.WriteLine(string.Format("Elapsed time: {0} milliseconds", ts.TotalMilliseconds));
  }

  static ulong Method1()
  {
      int x1 = _clsRandom.Next(int.MinValue, int.MaxValue);
      int x2 = _clsRandom.Next(int.MinValue, int.MaxValue);
      ulong y;

      // lines must be separated or result won't go past 2^32
      y = (uint)x1;
      y = y << 32;
      y = y | (uint)x2;

      return y;
  }

  static ulong Method2()
  {
      ulong ulResult = 0;

      for(int iPower = 0; iPower < 64; iPower++)
      {
          double dRandom = _clsRandom.NextDouble();
          if(dRandom > 0.5)
          {
              double dValue = Math.Pow(2, iPower);
              ulong ulValue = Convert.ToUInt64(dValue);
              ulResult = ulResult | ulValue;
          }
      }

      return ulResult;
  }

  static ulong Method3()  // only difference between #3 and #2 is that this one (#3) uses .Next() instead of .NextDouble()
  {
      ulong ulResult = 0;

      for (int iPower = 0; iPower < 64; iPower++)
          if (_clsRandom.Next(0, 1) == 1)
              ulResult = ulResult | Convert.ToUInt64(Math.Pow(2, iPower));

      return ulResult;
  }

static ulong Method4()
{
    byte[] arr_bt = new byte[8];
    ulong ulResult;

    _clsRandom.NextBytes(arr_bt);
    ulResult = BitConverter.ToUInt64(arr_bt, 0);
    return ulResult;
}

// Next method courtesy of https://stackoverflow.com/questions/14708778/how-to-convert-unsigned-integer-to-signed-integer-without-overflowexception/39107847
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit)]
struct EvilUnion
{
    [System.Runtime.InteropServices.FieldOffset(0)] public int Int32;
    [System.Runtime.InteropServices.FieldOffset(0)] public uint UInt32;
}
static ulong Method5()
{
    var evil = new EvilUnion();
    ulong ulResult = 0;

    evil.Int32 = _clsRandom.Next(int.MinValue, int.MaxValue);
    ulResult = evil.UInt32;
    ulResult = ulResult << 32;
    evil.Int32 = _clsRandom.Next(int.MinValue, int.MaxValue);
    ulResult = ulResult | evil.UInt32;

    return ulResult;
}

}

I'll add my solution for generating random unsigned long integer (random ulong) below max value.

    public static ulong GetRandomUlong(ulong maxValue)
    {
        Random rnd = new Random();
        
        //This algorithm works with inclusive upper bound, but random generators traditionally have exclusive upper bound, so we adjust.
        //Zero is allowed, function will return zero, as well as for 1. Same behavior as System.Random.Next().
        if (maxValue > 0) maxValue--;   
                    
        byte[] maxValueBytes = BitConverter.GetBytes(maxValue); 
        byte[] result = new byte[8];
                    
        int i;
        for(i = 7; i >= 0; i--)
        {
                //senior bytes are either zero (then Random will write in zero without our help), or equal or below that of maxValue
                result[i] = (byte)rnd.Next( maxValueBytes[i] + 1 );         
                
                //If, going high bytes to low bytes, we got ourselves a byte, that is lower than that of MaxValue, then lower bytes may be of any value.
                if ((uint)result[i] < maxValueBytes[i])  break;
        }
                        
        for(i--; i >= 0; i--) // I like this row
        {
                result[i] = (byte)rnd.Next(256);
        }
                
        return BitConverter.ToUInt64(result, 0);
    }

What's wrong with generating a double to be intended as a factor to be used to calculate the actual long value starting from the max value a long can be?!

long result = (long)Math.Round( random.NextDouble() * maxLongValue );
  • NextDouble generates a random number between [0.0, 0.99999999999999978] (msdn doc)

  • You multiply this random number by your maxLongValue.

  • You Math.Round that result so you can get the chance to get maxLongValue anyway (eg: simulate you got 1.0 from the NextDouble).

  • You cast back to long.
Related