2

I tried to write simple program, that simulates lottery and I got some behavior I cannot understand, nor fix:

To keep it simple I excluded code that is irrelevant to the problem

PROGRAM: On click it should Get six different numbers (between 1 and 49) entered by user, get six different random numbers (between 1 and 49) compare them and repeat getting random numbers and comparing with entered until there are three matches.

  1. What is relevant, I am calling function GetResults() on button click and passing to it two parameters (method definition below). I simplified it to button click to show you. There are some conditions and function calls there, but they are working and problem exists even without them so that's why image example on the bottom may be a little different.

    private void btnCheck_Click(object sender, EventArgs e)
    {
        lotto.GetResults(3, ref listRndNumLabels);
    
        lblMatches.Text = lotto.CurrentMatches.ToString();
        lblTryCounter.Text = lotto.NumberOfTries.ToString();
        lblBalance.Text = lotto.Balance.ToString() + " zł";
        lblThreesAmmount.Text = lotto.ThreesAmmount.ToString();
        lblFoursAmmount.Text = lotto.FoursAmmount.ToString();
        lblFivesAmmount.Text = lotto.FivesAmmount.ToString();
        lblSixesAmmount.Text = lotto.SixesAmmount.ToString();
    }
    
  2. The method GetResults() takes 3 as number of desired matches and List of Labels which is updated at the end

    public void GetResults(int Choice, ref List<Label> listLblRndNum)
    {
        _currentMatches = 0;
        int desiredNumberOfMatches = Choice;
    
        // while we got other ammount of matches than three, go again
        while (_currentMatches != desiredNumberOfMatches)
        {
            _numberOfTries++;
    
            // get list of mutually exclusive 6 numbers betweeen 1 and 49
            var tempList = GetRndNums();
    
            // insert random numbers to list
            _listLotteryNumbers.Clear();
            for (int i = 0; i < 6; i++)
            {
                _listLotteryNumbers.Insert(i, tempList[i]);
            }
    
            _balance -= _ticketCost;
            _currentMatches = 0;
    
            // get number of matches
            for (int i = 0; i < 6; i++)
            {
                foreach (int num in _listLotteryNumbers)
                {
                    if (_listSelectedNumbers[i] == num)
                    {
                        _currentMatches++;
                    }
                }
            }
    
            //FrmLotto.lbResults.Items.Add(_numberOfTries.ToString() + " - _currentMatches: " + _currentMatches.ToString());
    
            //FrmLotto.lbResults.Items.Add(_numberOfTries.ToString() + " - tempList { " + tempList[0] + " " + tempList[1] + " " + tempList[2] + " " + tempList[3] + " " + tempList[4] + " " + tempList[5] + " }");
            //FrmLotto.lbResults.Items.Add(_numberOfTries.ToString() + " - _listLotteryNumbers { " + _listLotteryNumbers[0] + " " + _listLotteryNumbers[1] + " " + _listLotteryNumbers[2] + " " + _listLotteryNumbers[3] + " " + _listLotteryNumbers[4] + " " + _listLotteryNumbers[5] + " }");
            //FrmLotto.lbResults.Items.Add(_numberOfTries.ToString() + " - _listSelectedNumbers { " + _listSelectedNumbers[0] + " " + _listSelectedNumbers[1] + " " + _listSelectedNumbers[2] + " " + _listSelectedNumbers[3] + " " + _listSelectedNumbers[4] + " " + _listSelectedNumbers[5] + " }");
    
            // update stats
            if (_currentMatches == 3)
            {
                _threesAmmount++;
                _balance += 10;
            }
            else if (_currentMatches == 4)
            {
                _foursAmmount++;
                _balance += 100;
            }
            else if (_currentMatches == 5)
            {
                _fivesAmmount++;
                _balance += 3500;
            }
            else if (_currentMatches == 6)
            {
                _sixesAmmount++;
                _balance += 1000000;
            }
    
            //FrmLotto.lbResults.Items.Add(_numberOfTries.ToString() + " - Threes Ammount right after updating: " + _threesAmmount);
            //FrmLotto.lbResults.Items.Add("");
    
            // this gets out of the loop if user has chosen from ddl to run once, it is irrelevant here
            if (desiredNumberOfMatches == -1)
                break;
        }
    
        // finally update Labels with the desired result
        for (int i = 0; i < 6; i++)
        {
            listLblRndNum[i].Text = _listLotteryNumbers[i].ToString();
        }
    }
    
  3. And this is the function which gets random numbers:

    public List<int> GetRndNums()
    {
        List<int> listRndNums = new List<int>();
        Random rndNum = new Random();
    
        for (int i = 0; i < 6; i++)
        {
            int myNum = 0;
            do
                myNum = rndNum.Next(1, 49);
            while (listRndNums.Contains(myNum));
    
            listRndNums.Add(myNum);
        }
        listRndNums.Sort();
    
        return listRndNums;
    }
    

So program works as expected if loop run once, or if there is delay after each loop, or if I put breakpoint in the loop.

OTHERWISE there is an unexpected behavior, loop run more than once for the same data (for the same lists), I don't understand why.

look at the images:

  1. Program run once, I clicked button five times, to show you that results are fine:

(btw = Sprawdź = Check, raz = once, do pierwszej trójki = until 3 matches)

http://ultraimg.com/jHQY

  1. And when I select until 3 matches (or click button from the code example above) I am receiving wrong result, loop runs multiple times for the same values.

http://ultraimg.com/jHQn

Would be really grateful for help, I am learning, I know that many parts of code could be improved, many parts are for temp debugging purposes only. But this behavior, I simply don't get it.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
rvnlord
  • 3,487
  • 3
  • 23
  • 32
  • 2
    http://stackoverflow.com/questions/767999/random-number-generator-only-generating-one-random-number?lq=1 – Steve Jun 27 '14 at 17:07
  • Just an FYI - passing your list as a ref parameter is redundant, lists are instances which are passed by reference by default – James Jun 27 '14 at 17:07

1 Answers1

0

In order to fix this problem you should try making

Random rndNum = new Random();

a static variable.

See this : http://msdn.microsoft.com/en-us/library/system.random.aspx

Pseudo-random numbers are chosen with equal probability from a finite set of numbers. The chosen numbers are not completely random because a definite mathematical algorithm is used to select them, but they are sufficiently random for practical purposes. The current implementation of the Random class is based on Donald E. Knuth's subtractive random number generator algorithm. For more information, see D. E. Knuth. "The Art of Computer Programming, volume 2: Seminumerical Algorithms". Addison-Wesley, Reading, MA, second edition, 1981.

The random number generation starts from a seed value. If the same seed is used repeatedly, the same series of numbers is generated. One way to produce different sequences is to make the seed value time-dependent, thereby producing a different series with each new instance of Random. By default, the parameterless constructor of the Random class uses the system clock to generate its seed value, while its parameterized constructor can take an Int32 value based on the number of ticks in the current time. However, because the clock has finite resolution, using the parameterless constructor to create different Random objects in close succession creates random number generators that produce identical sequences of random numbers. The following example illustrates that two Random objects that are instantiated in close succession generate an identical series of random numbers.

byte[] bytes1 = new byte[100];
byte[] bytes2 = new byte[100];
Random rnd1 = new Random();
Random rnd2 = new Random();

rnd1.NextBytes(bytes1);
rnd2.NextBytes(bytes2);

Console.WriteLine("First Series:");
for (int ctr = bytes1.GetLowerBound(0); 
     ctr <= bytes1.GetUpperBound(0); 
     ctr++) { 
   Console.Write("{0, 5}", bytes1[ctr]);
   if ((ctr + 1) % 10 == 0) Console.WriteLine();
} 
Console.WriteLine();
Console.WriteLine("Second Series:");        
for (int ctr = bytes2.GetLowerBound(0);
     ctr <= bytes2.GetUpperBound(0);
     ctr++) {
   Console.Write("{0, 5}", bytes2[ctr]);
   if ((ctr + 1) % 10 == 0) Console.WriteLine();
}   
// The example displays the following output to the console: 
//       First Series: 
//          97  129  149   54   22  208  120  105   68  177 
//         113  214   30  172   74  218  116  230   89   18 
//          12  112  130  105  116  180  190  200  187  120 
//           7  198  233  158   58   51   50  170   98   23 
//          21    1  113   74  146  245   34  255   96   24 
//         232  255   23    9  167  240  255   44  194   98 
//          18  175  173  204  169  171  236  127  114   23 
//         167  202  132   65  253   11  254   56  214  127 
//         145  191  104  163  143    7  174  224  247   73 
//          52    6  231  255    5  101   83  165  160  231 
//        
//       Second Series: 
//          97  129  149   54   22  208  120  105   68  177 
//         113  214   30  172   74  218  116  230   89   18 
//          12  112  130  105  116  180  190  200  187  120 
//           7  198  233  158   58   51   50  170   98   23 
//          21    1  113   74  146  245   34  255   96   24 
//         232  255   23    9  167  240  255   44  194   98 
//          18  175  173  204  169  171  236  127  114   23 
//         167  202  132   65  253   11  254   56  214  127 
//         145  191  104  163  143    7  174  224  247   73 
//          52    6  231  255    5  101   83  165  160  231   

This problem can be avoided by creating a single Random object rather than multiple ones. To improve performance, create one Random object to generate many random numbers over time, instead of repeatedly creating a new Random objects to generate one random number. To generate a cryptographically secure random number suitable for creating a random password, for example, use a class derived from System.Security.Cryptography.RandomNumberGenerator such as System.Security.Cryptography.RNGCryptoServiceProvider.

Aelphaeis
  • 2,593
  • 3
  • 24
  • 42
  • Thank you so much! I tried million things, and didn't think it might be so simple. Thank you sir that you enlightened me. – rvnlord Jun 27 '14 at 17:31