Results 1 to 40 of 79

Thread: Random BigInteger

Threaded View

  1. #11
    PowerPoster boops boops's Avatar
    Join Date
    Nov 2008
    Location
    Holland/France
    Posts
    3,201

    Re: Random BigInteger

    I couldn't resist having a go at this myself, and I think I have a solution. It works as far as I can see for any any minimum and maximum, including those stated by the OP, and (so it seems) including negatives. I didn't plan for the negatives, but they just seem to work.

    My method is similar to those so far, building an array of random bytes of the same length as BigInteger.ToByteArray. What I do that is different is to zeroise any bits in the most significant byte of the random output which correspond to leading zeros in the most significant byte of the test maximum. That way the random value has the same bit length (not just byte length) as the maximum, greatly reducing the probability that the value will exceed the maximum. This does not affect the randomness of the remaining bits. There is no need to worry about a zero sign byte at the most signficant end since it simply remains unaltered; and there cannot be any leading zeros in the next most significant byte.

    As a result, only one pass of the loop is often needed, depending on the chosen maximum and minimum. The OP's limits seem to need only one pass, and the same is true for a range from Long.MinValue (inclusive) to Long.MaxValue (exclusive). Sometimes the average number of loops per biginteger is slightly more than one, showing that the loop is actually needed. In one test, with a range from 0 to &H899999, an average of 1.86 loops were needed. The performance seems satisfactory: with the OP's minimum and maximum, I get an average of 0.12 milliseconds per generated biginteger on a fairly humdrum twincore. Smaller limits are obviously much faster.

    Here's the code of the function:
    vb.net Code:
    1. Private Function RandomBigInteger(minimum As BigInteger, maximum As BigInteger) As BigInteger
    2.         Dim biDiff As BigInteger = maximum - minimum
    3.         If biDiff < 0 Then Throw New Exception("Maximum must be more than minimum!")
    4.  
    5.         Dim biResult As BigInteger
    6.         Dim diffBytes() As Byte = biDiff.ToByteArray
    7.         Dim count As Integer = diffBytes.Count
    8.         Dim resultBytes(count - 1) As Byte
    9.  
    10.         Do
    11.             'Get a random byte array of the same length as the diffBytes:
    12.             _random.NextBytes(resultBytes)
    13.  
    14.             'zeroise bits in the most significant byte which are leading zeros in diffBytes:
    15.             For i As Integer = 7 To 0 Step -1
    16.                 Dim bitmask = 1 << i
    17.                 If (diffBytes.Last And bitmask) = bitmask Then
    18.                                         'If the tested bit is 1, there are no more leading zeros:
    19.                     Exit For
    20.                 Else
    21.                                         'Zeroize the tested byte:
    22.                     resultBytes(count - 1) = CByte(resultBytes.Last And (Not bitmask))
    23.  
    24.                 End If
    25.             Next
    26.  
    27.             biResult = New BigInteger(resultBytes)
    28.             _loopCount += 1
    29.  
    30.         Loop Until biResult < biDiff
    31.  
    32.         Return biResult + minimum
    33.     End Function

    Here's a test sub I have been using.
    vb.net Code:
    1. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    2.         _loopCount = 0
    3.         Dim minimum As BigInteger
    4.         Dim maximum As BigInteger
    5.         Dim errors As Integer
    6.         Dim positives As Integer
    7.         Dim negatives As Integer
    8.         Dim total As BigInteger
    9.         Dim average As BigInteger
    10.  
    11.         'OP's test limits:
    12.         Dim a As BigInteger = 2 ^ 31 - 1
    13.         minimum = BigInteger.Pow(a, 33)
    14.         Dim b As Integer = CInt(2 ^ 15)
    15.         maximum = BigInteger.Pow(2, b)
    16.        
    17.         'alternative test limits:
    18.         minimum = Long.MinValue
    19.         maximum = Long.MaxValue
    20.         'minimum = 0
    21.         'maximum = &H899999
    22.  
    23.         Dim iterations As Integer = 999999
    24.  
    25.         Dim sw As Stopwatch = Stopwatch.StartNew
    26.         For i As Integer = 0 To iterations - 1
    27.             Dim bi As BigInteger = RandomBigInteger(minimum, maximum)
    28.             'Console.WriteLine(bi.ToString) '(Comment this line out when performance testing.)
    29.             If bi < minimum OrElse bi >= maximum Then
    30.                 Console.WriteLine("Error: " & bi.ToString)
    31.                 errors += 1
    32.             Else
    33.                 total += bi
    34.             End If
    35.             If bi >= 0 Then positives += 1 Else negatives += 1
    36.  
    37.         Next
    38.         Console.WriteLine("Loop count per biginteger : " & (_loopCount / iterations).ToString())
    39.         Console.WriteLine((1000 * sw.ElapsedTicks / Stopwatch.Frequency / iterations).ToString("N3") _
    40.            & " average milliseconds per random biginteger")
    41.         Console.WriteLine("Out-of-range errors : " & errors)
    42.         Console.WriteLine("Positives : " & positives)
    43.         Console.WriteLine("Negatives : " & negatives)
    44.         average = BigInteger.Divide(total, iterations - errors)
    45.         Console.WriteLine("Average: " & average.ToString)
    46.         Console.WriteLine("Average as % of maximum: " & (CDec(average) / CDec(maximum)).ToString("P2"))
    47.     End Sub

    BB

    EDIT: I added some more feedback to the test sub. In particular, there are now some simple tests for skewing. With a symmetrical range (e.g. Long.MinValue to Long.MaxValue) the number of positive results should be close to the number of negatives. With a minimum=0 and a large number of iterations, the average output should be close to 50% of the maximum; this test is limited to Decimal ranges because I haven't thought out how to do a fractional divide of bigintegers.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width