Click here to Skip to main content
15,889,456 members
Articles / Security / Encryption
Tip/Trick

Getting a strong random digit using RNGCryptoServiceProvider

Rate me:
Please Sign up or sign in to vote.
4.00/5 (3 votes)
15 May 2013CPOL1 min read 28.5K   5   12
Generating a random number between 0 and 9.

Introduction

I tried to write code that would return an integer of value between 0 and 9 without losing the randomness strength provided in the class RandomNumberGenerator (RNGCryptoServiceProvider) in the System.Security.Cryptography namespace. The hard part was that the byte array is filled with random bytes valued between 0 and 255.

Basic idea and method

If we use only integers 0, 1, 2, 3 and we randomly pick one three times from this collection, the sum of the three draws wil always have a value between 0 and 9.

If a byte has a value between 0 and 255 we also can say that this byte is an even or odd number. The even and odd numbers are equally represented in a set of (0-255).

Therefore if we take two bytes the possibilities of the outcome for a byte pair (b1 and b2) will always be:

  1. b(1)odd b(0)even = 0
  2. b(1)odd b(1)odd = 1
  3. b(0)even b(0)even = 2
  4. b(1)even b(2)odd = 3

If we populate a byte array with random 6 bytes (3 pairs) and compare 3x two bytes, we will have 3x3 possible values between 0 and 3.

For example: Array X= {a,b,c,d,e,f}

  • Bytes: [b1,b2] - [b3,b4] - [b5,b6]
  • Valued: [11][34] -[55][47] - [55][66]
  • Are: odd/even -odd/odd -odd/even
  • Resulting in: 3 + 3 + 2 = 8

(After each round I reverse the odd/even values for better distribution. This was also done in the Hot Bits Radiation project. After testing the Bell curve was better distributed.)

Using the code

There is a class file RandomDigits and a Console application.

The class file:

C#
//M.C. Koutzarov Class file RandomDigits [0-9]

using System;
using System.Security.Cryptography;
using System.Text;

namespace ps.security{
    public class RandomDigits
    {     
        ///<summary>
        /// Returns One random Digit of a value between 0 and 9.
        ///</summary>   
        public static int getDigit(){
            int[] Result = { 0, 0, 0 }; // this stores sum1 , sum2 and sum3 
            int R = 0; // Will hold the resulting integer Sum of Result{3}
            int b1 = 0; // byte one
            int b2 = 0; // byte two 
            int offset = 0;
            // need an offset to loop 3 times. Each time i get 2 bytes to compare.
          
            //get 6 random bytes
            RandomNumberGenerator rng = new RNGCryptoServiceProvider();
            
            byte[] b = new byte[6];
             
            rng.GetNonZeroBytes(b);


            for (int i =0; i <=2; i++)  // Loop 3 time  i=2 i=1 and i=0
            { 
                //get the offset 
                if (i == 2) {
                    offset = 4; // 5th and 6th byte 
                }else if(i == 1) {
                    offset = 2; // 3th and 4th byte 
                }else if (i == 0){
                    offset = 0; // 1th and 2th byte
                };

                //(b1,2 MOD 2) b1 and b2 will be odd number is 1 and even if 0
                b1 = ((int)b[offset] % 2);
                b2 = ((int)b[offset+1] % 2);


                if ((i == 0))
                {
                    if ((b1 == 1) && (b2 == 1))
                    {
                        Result[i] = 0;              //odd-odd   = 0
                    }
                    else if ((b1 == 0) && (b2 == 1))
                    {
                        Result[i] = 1;              //even-odd  = 1
                    }
                    else if ((b1 == 0) && (b2 == 0))
                    {
                        Result[i] = 2;              //even-even = 2  
                    }
                    else if ((b1 == 1) && (b2 == 0))
                    {
                        Result[i] = 3;
                    };           //odd-even  = 3
                }
                else if (i==1) {
                    // on second round flip the values 
                    if ((b1 == 1) && (b2 == 1))
                    {
                        Result[i] = 3;              //odd-odd   = 3
                    }
                    else if ((b1 == 0) && (b2 == 1))
                    {
                        Result[i] = 2;              //even-odd  = 2
                    }
                    else if ((b1 == 0) && (b2 == 0))
                    {
                        Result[i] = 1;              //even-even = 1  
                    }
                    else if ((b1 == 1) && (b2 == 0))
                    {
                        Result[i] = 0;          //odd-even  = 0
                    };

                }
                else if (i == 2) {

                    // on 3 round  flip the values once more
                    if ((b1 == 1) && (b2 == 1))
                    {
                        Result[i] = 1;              //odd-odd   = 1
                    }
                    else if ((b1 == 0) && (b2 == 1))
                    {
                        Result[i] = 3;              //even-odd  = 3
                    }
                    else if ((b1 == 0) && (b2 == 0))
                    {
                        Result[i] = 0;              //even-even = 0  
                    }
                    else if ((b1 == 1) && (b2 == 0))
                    {
                        Result[i] = 2;          //odd-even  = 2
                    };
                }

                b1 = 0;
                b2 = 0;

            };// end 3 for loops

            //Sum the results of round 1,2 and 3 together
            //this will make an Int between 0 - 9 
            R =(int)(Result[0] + Result[1] + Result[2]);

            //Return a Int valued between 0 - 9
            return R;
        }      
    }
}

Here is the console testing code:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ps.security;
using System.Diagnostics; 

//M.C. Koutzarov Testing Randomness for digits [0-9]

    class Program
    {
        static void Main(string[] args){
             Program.DrawNumbersPerDigitTest();
             Console.ReadKey();
             Console.WriteLine("");
             Console.WriteLine("Test 2");
             Program.DrawDigitTest(1000000); 
        }
   
        // Draws a the digits 0-9 
        // each digit(from 0-9) is being drawn untill it appears
        // At the end it shows the Total draws needed for all didigts to appear.
        // and the avg number of draws per digit.
        public static int[] DrawNumbersPerDigitTest()
        {
            Console.WriteLine("Random digits  0-9 generator test.");
            double frequency = System.Diagnostics.Stopwatch.Frequency;
            double nanosecPerTick = (1000 * 1000 * 1000) / frequency;
            int totalItter = 0;
            int[] Totaldistri = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
            for (int AllDigits = 9; AllDigits >= 0; AllDigits--)
            {
                Stopwatch timer = new Stopwatch();
                int avg = 0;
                int itters = 0;
                int[] distri = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
                timer.Start();
                for (int i = 1000000; i >= 1; i--)
                {
                    int n = ps.security.RandomDigits.getDigit();
                    avg = (avg + n);
                    distri[n]++;
                    itters++;
                    if (n == AllDigits)
                    {
                        break;
                    }
                };
                timer.Stop();
                
                double durNanosec = timer.ElapsedTicks * nanosecPerTick;
                double durSecs = timer.ElapsedTicks * 1000 / frequency;
                
                Console.WriteLine("");
                Console.WriteLine("|----------------------------------|");
                Console.WriteLine("Digit nr:\t" + AllDigits);
                Console.WriteLine("");
                Console.WriteLine("|  int\t|  freq\t percentage\t   |");
                Console.WriteLine("|----------------------------------|");
                for (int i = 9; i >= 0; i--)
                {
                    double p =Math.Round( ((double)distri[i])/((double)itters / 100),3);
                    Console.WriteLine("|  " + i + "\t|  " + 
                       +distri[i] + "\t " + p + "%\t");
                }
                Console.WriteLine("|----------------------------------|");
                Console.WriteLine("");
                Console.WriteLine("  Sum     : >\t" + avg.ToString());
                Console.WriteLine("  Average : >\t" + 
                        ((double)avg / (double)itters).ToString());
                Console.WriteLine("  Count   : >\t" + (itters).ToString());
                Console.WriteLine("");
                Console.WriteLine("  nSecs   :\t" + Math.Round(durNanosec, 3));
                Console.WriteLine("  mSecs   :\t" + Math.Round(durSecs, 9));
                totalItter += (itters);
                Totaldistri[AllDigits] = itters;
            };
            Console.WriteLine("");
            Console.WriteLine("|----------------------------------|");
            Console.WriteLine("Average + total draws per Digit:");
            Console.WriteLine("|----------------------------------|");
            Console.WriteLine("");
            Console.WriteLine("  total draws\t: >\t" + totalItter.ToString());
            Console.WriteLine("  avg\t\t: >\t" + 
                    Math.Round((double)totalItter / 10, 3));
            Console.WriteLine("|  int\t|  freq\t ");
            Console.WriteLine("|----------------------------------|");
            for (int i = 9; i >= 0; i--)
            {
                Console.WriteLine("|  " + i + "\t|  " + 
                          +Totaldistri[i] + "\t \t");
            }
            return Totaldistri;
     }
     // Draws a the digits 0-9 
     // each digit(from 0-9) is being drawn untill it appears
     // At the end it shows the Total draws needed for all didigts to appear.
     // and the avg number of draws per digit.
     public static void DrawDigitTest(int nrOfDigits)
     {
         Console.WriteLine("Random digits  0-9 generator test.");
         double frequency = System.Diagnostics.Stopwatch.Frequency;
         double nanosecPerTick = (1000 * 1000 * 1000) / frequency;
        
         Stopwatch timer = new Stopwatch();
         int avg = 0;
         int itters = 0;
         int[] distri = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
         timer.Start();
         for (int i = nrOfDigits; i >= 1; i--)
         {
             int n = ps.security.RandomDigits.getDigit();
             avg = (avg + n);
             distri[n]++;
             itters++;
         };
         timer.Stop();
         double durNanosec = timer.ElapsedTicks * nanosecPerTick;
         double durSecs = timer.ElapsedTicks * 1000 / frequency;
        
         Console.WriteLine("");
         Console.WriteLine("| int\t|  freq\t percentage\t   |");
         Console.WriteLine("|----------------------------------|");
         for (int i = 9; i >= 0; i--)
         {
             double p = Math.Round(((double)distri[i]) / ((double)itters / 100), 1);
             Console.WriteLine("| " + i + "\t| " + 
               +distri[i] + "\t\t" + p + "%.");
         }
         Console.WriteLine("|----------------------------------|");
         Console.WriteLine("");
         Console.WriteLine("  #Digits : >\t\t" + (itters).ToString());
         Console.WriteLine("  Sum     : >\t\t" + avg.ToString());
         Console.WriteLine("  Average : >\t\t" + 
               ((double)avg / (double)itters).ToString());
         
         Console.WriteLine("");
         Console.WriteLine("  nSecs   : >\t" + Math.Round(durNanosec, 3));
         Console.WriteLine("  mSecs   : >\t" + Math.Round(durSecs, 9));
     }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer
Hong Kong Hong Kong
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
AnswerYou've demonstrated the Central Limit Theorem... Pin
Matt T Heffron14-May-13 14:02
professionalMatt T Heffron14-May-13 14:02 
GeneralRe: You've demonstrated the Central Limit Theorem... Pin
Marc Koutzarov14-May-13 16:34
professionalMarc Koutzarov14-May-13 16:34 
GeneralRe: You've demonstrated the Central Limit Theorem... Pin
Marc Koutzarov15-May-13 4:55
professionalMarc Koutzarov15-May-13 4:55 
GeneralRe: You've demonstrated the Central Limit Theorem... Pin
Matt T Heffron15-May-13 9:25
professionalMatt T Heffron15-May-13 9:25 
AnswerRe: You've demonstrated the Central Limit Theorem... Pin
Marc Koutzarov15-May-13 11:06
professionalMarc Koutzarov15-May-13 11:06 
GeneralRe: You've demonstrated the Central Limit Theorem... Pin
Matt T Heffron15-May-13 12:50
professionalMatt T Heffron15-May-13 12:50 
GeneralRe: You've demonstrated the Central Limit Theorem... Pin
Marc Koutzarov15-May-13 13:52
professionalMarc Koutzarov15-May-13 13:52 
GeneralRe: You've demonstrated the Central Limit Theorem... Pin
Marc Koutzarov15-May-13 13:49
professionalMarc Koutzarov15-May-13 13:49 
SuggestionStreamlined version of the code Pin
Fred Flams14-May-13 3:27
Fred Flams14-May-13 3:27 
GeneralRe: Streamlined version of the code Pin
Marc Koutzarov14-May-13 4:09
professionalMarc Koutzarov14-May-13 4:09 
GeneralMy vote of 4 Pin
Fred Flams14-May-13 3:05
Fred Flams14-May-13 3:05 
SuggestionSource code downloadable via GitHub. Pin
Marc Koutzarov13-May-13 4:43
professionalMarc Koutzarov13-May-13 4:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.