Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Random Number without repeat

Discussion in 'Scripting' started by behdadsoft, Sep 28, 2017.

  1. behdadsoft

    behdadsoft

    Joined:
    Aug 25, 2012
    Posts:
    150
    Hi.

    I want create a random number between (1-5) without repeating. so i wrote this code but it give me repeat number. actually i don't know how can compare a value in array list. how can i fix this problem?

    Code (CSharp):
    1.     int Rand;
    2.     int[] LastRand;
    3.     int Max = 6;
    4.  
    5.     void Start ()
    6.     {
    7.         LastRand = new int[Max];
    8.  
    9.         for (int i = 1; i < Max; i++)
    10.         {
    11.             Rand = Random.Range(1,6);
    12.  
    13.             for (int j = 1; j < i; j++)
    14.             {
    15.                 while (Rand == LastRand[j])
    16.                 {
    17.                     Rand = Random.Range(1, 6);
    18.                 }
    19.             }
    20.  
    21.             LastRand[i] = Rand;
    22.             print(Rand);
    23.         }
    24.     }
    Thanks.
     
  2. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,150
    Using a list, you could generate a random number, check if the list contains the number, and if not, add it. If so, generate another random number. However, you could probably use the array contains as well. https://msdn.microsoft.com/en-us/library/bb384015.aspx

    There are also several examples on this forum of how to generate a random list of numbers without repeats. Including one that describes several options on how to do this, I just don't have links to the post atm.
     
    behdadsoft and Rob21894 like this.
  3. Chocolade

    Chocolade

    Joined:
    Jun 19, 2013
    Posts:
    916
    Use this:

    Code (csharp):
    1.  
    2. public static List<int> GenerateRandom(int count, int min, int max)
    3.         {
    4.             if (max <= min || count < 0 ||
    5.                     (count > max - min && max - min > 0))
    6.             {
    7.                 throw new ArgumentOutOfRangeException("Range " + min + " to " + max +
    8.                         " (" + ((Int64)max - (Int64)min) + " values), or count " + count + " is illegal");
    9.             }
    10.  
    11.             HashSet<int> candidates = new HashSet<int>();
    12.  
    13.             for (int top = max - count; top < max; top++)
    14.             {
    15.                 if (!candidates.Add(random.Next(min, top + 1)))
    16.                 {
    17.                     candidates.Add(top);
    18.                 }
    19.             }
    20.  
    21.             List<int> result = candidates.ToList();
    22.  
    23.             for (int i = result.Count - 1; i > 0; i--)
    24.             {
    25.                 int k = random.Next(i + 1);
    26.                 int tmp = result[k];
    27.                 result[k] = result[i];
    28.                 result[i] = tmp;
    29.             }
    30.             return result;
    31.         }
    32.  
    Use it in the start for example:

    Code (csharp):
    1.  
    2. GenerateRandom(5, 1, 6);
    3.  
     
    youngleox and behdadsoft like this.
  4. behdadsoft

    behdadsoft

    Joined:
    Aug 25, 2012
    Posts:
    150
    Thanks, I wrote it. it's very easy.

    Code (CSharp):
    1.     int Rand;
    2.     int Lenght = 6;
    3.     List<int> list = new List<int>();
    4.  
    5.     void Start ()
    6.     {
    7.         list = new List<int>(new int[Lenght]);
    8.  
    9.         for (int j = 1; j < Lenght; j++)
    10.         {
    11.             Rand = Random.Range(1,6);
    12.  
    13.             while (list.Contains(Rand))
    14.             {
    15.                 Rand = Random.Range(1,6);
    16.             }
    17.  
    18.             list[j] = Rand;
    19.             print(list[j]);
    20.         }
    21.    
    22.     }
     
    Last edited: Sep 30, 2017
    Deleted User, Nithinsvs and kian94 like this.
  5. behdadsoft

    behdadsoft

    Joined:
    Aug 25, 2012
    Posts:
    150

    Thanks for code.:)
     
  6. fire7side

    fire7side

    Joined:
    Oct 15, 2012
    Posts:
    1,819
    Code (csharp):
    1.  
    2. List<int> list = new List<int> (){1,2,3,4,5};
    3. int randNumber = 4;
    4. bool exists = list.Contains(randNumber);
    5.  
    Oops, too late!
     
    behdadsoft likes this.
  7. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,131
    in your sczenario, a typical approach is to fill the list with all potential outcomes and remove the drawn items from the list.
    Pseudo code

    pos=Random.Range(0, list.length)
    random = list[pos]
    List.deleteAtIndex(pos)
     
  8. MaxGuernseyIII

    MaxGuernseyIII

    Joined:
    Aug 23, 2015
    Posts:
    315
    @sngdan
    1. Create a mutable list with all the options.
    2. Use the RNG to pick an index into that list.
    3. Delete the corresponding item from the list.
    4. Return or use the selected random number.
    Alternatively, you can just create a list or queue with all the items in their pre-randomized state, then pull from one end of the collection. That has no real impact, though. It's just about where you want your design and when you want to execute the random-number-generation calls.
     
    behdadsoft likes this.
  9. behdadsoft

    behdadsoft

    Joined:
    Aug 25, 2012
    Posts:
    150
    I wrote this code and work very well. but when I change start value form 1 to 0 in for loop, it don't work correctly. because list.Contains(0) is exist. how can fix this problem?

    Code (CSharp):
    1.     int Rand;
    2.     int Lenght = 6;
    3.     List<int> list = new List<int>();
    4.     void Start ()
    5.     {
    6.         list = new List<int>(new int[Lenght]);
    7.         for (int j = 1; j < Lenght; j++)
    8.         {
    9.             Rand = Random.Range(1,6);
    10.             if (list.Contains(Rand))
    11.             {
    12.                 Rand = Random.Range(1,6);
    13.             }
    14.             list[j] = Rand;
    15.             print(list[j]);
    16.         }
    17.  
    18.     }
     
  10. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,150
    Code (CSharp):
    1.  
    2.     int Lenght = 6;
    3.     List<int> list = new List<int>();
    4.     void GenerateRandom()
    5.     {
    6.         for (int j = 0; j < Lenght; j++)
    7.         {
    8.             int Rand = Random.Range(0,6);
    9.             while(list.Contains(Rand))
    10.             {
    11.                 Rand = Random.Range(0,6);
    12.             }
    13.             list.add(Rand);
    14.             print(list[j]);
    15.         }
    16. }
    17.  
    What do you mean by 0 is already added?
     
  11. eXonius

    eXonius

    Joined:
    Feb 2, 2016
    Posts:
    207
    Your code has a bug. You must use while instead of if, like Brathnann does above.
     
  12. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,131
    this is what i meant in my post above...

    Code (CSharp):
    1.     void Start ()
    2.     {
    3.         int startNumber = 1;
    4.         int endNumber = 10;
    5.         List<int> numberPot = new List<int>();
    6.         for (int i = startNumber; i < endNumber + 1; i++)
    7.         {
    8.             numberPot.Add(i);
    9.         }
    10.        
    11.         while (numberPot.Count > 0)
    12.         {
    13.             int index = Random.Range (0, numberPot.Count);
    14.             int randomNumber = numberPot[index];
    15.             numberPot.RemoveAt(index);
    16.             Debug.Log(randomNumber);
    17.         }
    18.     }
     
    PoTTyD likes this.
  13. behdadsoft

    behdadsoft

    Joined:
    Aug 25, 2012
    Posts:
    150
    Thanks, my mistake is relate to this line list = new List<int>(new int[Lenght]);
    because it set all index values to 0 and make don't work correctly.
     
  14. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,131
    The problem with the approach to re-roll the random number in case it has come up before is that you can get stuck in the while loop for a long time. If you only have 6 values it does not matter much though.
     
  15. behdadsoft

    behdadsoft

    Joined:
    Aug 25, 2012
    Posts:
    150
    now, I wrote this code for my game and when Rand value is great than 0 unity give me an error. for example when Rand value is great than 0, my RandBonusList length is less than Rand value. anyone know how can fix it? I don't want it generate regular number (for example: 0, 1, 2).

    Code (CSharp):
    1.             List<int> RandBonusList = new List<int>();
    2.          
    3.             void Start()
    4.             {
    5.                 Rand = Random.Range(0, 3);
    6.  
    7.                 while (RandBonusList.Contains(Rand))
    8.                 {
    9.                     Rand = Random.Range(0, 3);
    10.                 }
    11.  
    12.                 RandBonusList[Rand] = Rand;
    13.  
    14.                 print(RandBonusList[Rand]);
    15.             }
     
    Last edited: Sep 30, 2017
  16. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    You cannot add to a list's index that's not initialized (I think that's the proper wording).
    You've gotta fill the list first. Fill it with something you don't want. Originally , you said you wanted 1-5, so you could fill it with zero or any number not 1-5. if you wanted 0, fill it with 10 or -1 or whatever.
    Then, when you have 'x number' of spots filled, you can work on it like the code you posted more properly. :)

    Code (csharp):
    1. List<int> RandBonusList = new List<int> { 10,10,10,10,10 }; // however many spots you want.
     
    behdadsoft likes this.
  17. behdadsoft

    behdadsoft

    Joined:
    Aug 25, 2012
    Posts:
    150
  18. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    no problem. Just lending a hand to this .. long thread ;) smiles. Hope it all works out for ya.
     
  19. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,131
    @behdadsoft

    If you want to stick with your approach, you don't need to initialize the list, you can add to a list just like this: RandBonusList.Add(Rand). I have the feeling though that you are on the wrong track with you approach.

    I recommend you to look at my post #12 and try to understand it (unless I completely did not get what you want).
    You basically throw numbers in a pot and then pick them one by one randomly until the pot is empty.
     
    behdadsoft likes this.
  20. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,150
    There isn't anything wrong with the approach. It's actually pretty fast with a small amount of numbers that you'll never notice it. There are many ways to generate a random list of numbers and what works best usually depends on the size. But on a small set like this, you'll not notice much difference between what method is chosen.
     
  21. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    True, there are many ways.. I was just hoping to help him finalize some code that was nearly done so it was working lol. With a little practice, time, and experience he will be aware of a number of ways :)
     
  22. UNITY3D_TEAM

    UNITY3D_TEAM

    Joined:
    Apr 23, 2012
    Posts:
    720
    any good script for no repeating random number generation?
     
  23. tamizhan

    tamizhan

    Joined:
    Feb 23, 2018
    Posts:
    6
    I want create a random number between (1-5) without repeating.
     
  24. Olmi

    Olmi

    Joined:
    Nov 29, 2012
    Posts:
    1,553
    @tamizhan look into Linear Congruential Generators; that kind of setup can generate you a pseudo-random (quite poor, though) sequence, which will be random so that each value is never repeated within the range. But the very good thing in LCG is that it doesn't require you to have anything precomputed like a an array of numbers, so you can generate 100,000 numbers in random order without extra cost.
     
  25. Bryarey

    Bryarey

    Joined:
    Aug 24, 2014
    Posts:
    25
    Hi, guys! Just want to let you know: I created utility to easely handle random without repetitions, flat distributed random, and weighted lists (universal property drawer included!). You can find it on GtiHub and use freely (as well as examine how it works ;) ):
    https://github.com/khadzhynov/RandomUtils
     
  26. athgen113

    athgen113

    Joined:
    May 25, 2018
    Posts:
    46
  27. unity_JvYnM7IjyQ8PPw

    unity_JvYnM7IjyQ8PPw

    Joined:
    Dec 25, 2020
    Posts:
    1
    i have the same problem. then i found the golden answer
    look at this:
    Code (CSharp):
    1.  
    2. private int[] mainarray = new int[6] //the array that you want to pick elements.
    3.  
    4. List<int> list = new List<int>(); // the list that you put non repetitive elements in it. for example we want 3 element.
    5.        
    6.  
    7.         while (list.Count != 4)
    8.         {
    9.             var element = mainarray[Random.Range(0, 5)];
    10.             if (!list.Contains(element))
    11.             {
    12.                 list.Add(element);
    13.           }
    14.         }
     
    frikic likes this.
  28. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    What I would do is create an integer list with all possible numbers, since the possible random results in this case is very small. Then what you do is generate a random index. Pull the value from the list by that index, then remove the value at that index from the list. You'll never get repeats, and can't get into the theoretical problem of the random number generator repeatedly picking a random number which has already been used.
     
  29. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    20,184
    What you've written is a good first attempt but it has a major flaw to it. If you want to see the flaw in action you need only count how many times the while loop iterates before the list of numbers is complete. For a set of five numbers the best case scenario is five iterations but running your code multiple times gave results into the 20s.

    For a set of five numbers this may not seem that big of a deal but it's far worse once you step beyond that list of five numbers. A standard deck of cards has 56 unique cards. A proper deck randomization should take no more than 56 iterations of a while loop. On average yours took into the 200s with iteration counts as high as 400+.

    Why is this happening? Because the random number generator is not guaranteed to take exactly 56 iterations to generate all 56 numbers. Want to know what the worst case scenario is? Infinite. Yes, it may take as low as 56 calls to the generator but if it never lands on the number it wants it may take an infinite number of calls.

    Below is a proper implementation. Lines 9 to 11 create the list of valid numbers. Lines 13 to 20 randomly select one of the valid numbers, adds it to the list of results, and then removes it from the list of valid numbers thereby making it impossible for it to be selected again. Thus this code will always take exactly one iteration per number.

    Code (csharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class RandomNumbers : MonoBehaviour
    6. {
    7.     public static List<int> GenerateRandomNumbers(int count)
    8.     {
    9.         List<int> numbers = new List<int>();
    10.         for (var index = 0; index < count; index++)
    11.             numbers.Add(index);
    12.    
    13.         List<int> results = new List<int>();
    14.         while (numbers.Count > 0)
    15.         {
    16.             int position = Random.Range(0, numbers.Count);
    17.  
    18.             results.Add(numbers[position]);
    19.             numbers.RemoveAt(position);
    20.         }
    21.  
    22.         return results;
    23.     }
    24.  
    25.     void Start()
    26.     {
    27.         foreach (var number in GenerateRandomNumbers(5))
    28.             Debug.Log(number);
    29.     }
    30. }
    Once again though yours was a good first attempt and very much the same as what I wrote back when I was first getting started, and like me you would have eventually realized there was a problem when it was taking entirely too long to generate a list of numbers (in my case I was on a 1980s computer so the problem stood out very quickly).
     
  30. KantosCode

    KantosCode

    Joined:
    Oct 23, 2019
    Posts:
    8
    Great code @Ryiah

    Here's a small modification if anyone, like me, happens to want to generate a certain number of random numbers from a range. Such as if you want to pick out 15 random objects from a list of 20 with each one being unique.

    Code (CSharp):
    1.  
    2. public static List<int> GenerateRandomNumbers(int count, int minValue, int maxValue)
    3. {
    4.         List<int> possibleNumbers = new List<int>();
    5.         List<int> chosenNumbers = new List<int>();
    6.  
    7.         for (int index = minValue; index < maxValue; index++)
    8.             possibleNumbers.Add(index);
    9.        
    10.         while (chosenNumbers.Count < count)
    11.         {
    12.             int position = Random.Range(0, possibleNumbers.Count);
    13.             chosenNumbers.Add(possibleNumbers[position]);
    14.             possibleNumbers.RemoveAt(position);
    15.         }
    16.         return chosenNumbers;
    17. }
    18.  
     
    dick likes this.
  31. ChillyRolande

    ChillyRolande

    Joined:
    Apr 20, 2019
    Posts:
    5
    ok this is amazing. I am quite new at coding if anyone knows how I can turn this list into something that can then access a gameobject that would be quite helpful. Thank you in advance!
     
  32. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,150
    Which is why you need to read up on some tutorials, in this case more on C# basics.

    The following code doesn't make sense because it's not a collection of ints, it's a collection of gameobjects. See if that's enough to get you where you need to be. :)
    Code (CSharp):
    1. int randomNumber = cards[index];
     
    ChillyRolande likes this.
  33. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    Wow, lot of code, I'm going to add my approach to that:
    Code (csharp):
    1.  
    2. public List<int> RandomList(int fromInclusive, int toExclusive, int count)
    3. {
    4.     List<int> available = new List<int>();
    5.     List<int> result = new List<int>();
    6.     for (int i = 0;i < count;i++)
    7.     {
    8.         if (available.Count == 0)
    9.         {
    10.             for (int index = fromInclusive;index < toExclusive;index++) available.Add(index);
    11.         }
    12.         int selected = available[Random.Range(0, available.Count)];
    13.         available.Remove(selected);
    14.         result.Add(selected);
    15.     }
    16.     return result.
    17. }
    18.  
    Creates a list of size <count> with numbers from <fromInclusive> to <toExclusive>. It will balance the number of appearances by keeping a list of available numbers. You could say it's a "bingo" algorithm. It will however also work when the <count> is higher than the numbers available between <fromInclusive> and <toExclusive> by repopulating the list of available numbers when empty.
     
    Last edited: Dec 17, 2021
  34. ChillyRolande

    ChillyRolande

    Joined:
    Apr 20, 2019
    Posts:
    5
    I appreciate the candidness of your comment. I am quite new and sometimes the code gets so confusing :eek:. I am now doing basic tutorials on arrays and lists. Thank you! (I deleted my comment above as it didn't really contribute). Off to study more...
     
  35. seejayjames

    seejayjames

    Joined:
    Jan 28, 2013
    Posts:
    685
    Code (CSharp):
    1. // Input list, randomize order
    2.     public static List<int> randomizeList(List<int> randList)
    3.     {
    4.         for (int i = 0; i < randList.Count; i++)
    5.         {
    6.             int temp = randList[i];
    7.             int rand = Random.Range(i, randList.Count);
    8.             randList[i] = randList[rand];
    9.             randList[rand] = temp;
    10.         }
    11.         //Debug.Log("randList = " + string.Join(",", randList));
    12.         return randList;
    13.     }
     
    Bunny83 likes this.
  36. Krooq

    Krooq

    Joined:
    Jan 30, 2013
    Posts:
    180
    This is known as a combination https://en.wikipedia.org/wiki/Combination without repetition aka "n choose k".

    So if you want to choose "k" things from a set of "n" items you want something like untested code below.
    It's pretty simple, you create a list of the "items" and take "k" things from it.

    If your "items" are indexes into some other array or list then you can choose any number of anything.

    Code (CSharp):
    1. public static int[] Choose(int k, int min, int max)
    2. {
    3.     var items = new List<int>();
    4.     for(var i=min; i<max; i++) items.Add(i);
    5.  
    6.     var choices = new int[k];
    7.     for (var i = 0; i < k; i++)
    8.     {
    9.         var index = UnityEngine.Random.Range(0, items.Count);
    10.         choices[i] = items[index];
    11.         items.RemoveAt(index);
    12.     }
    13.     return choices;
    14. }
     
  37. LeviTM

    LeviTM

    Joined:
    May 28, 2021
    Posts:
    2
    Great code, but I'm just confused on how I can apply this list to what I'm creating. I have a list of 3 objectives the player needs to complete, and obviously the list cannot include the same objectives twice. I have a variable for what each objective's value is, and I can't grab a specific index to what this "chosenNumbers" list entails. I want to have them in randomized order, but when I try to use a foreach loop to retrieve the respective indexes, it returns the same for all. How can I approach this better?
     
  38. Kayckbr

    Kayckbr

    Joined:
    Oct 25, 2021
    Posts:
    5
    im years late but i got this working:

    Code (CSharp):
    1. int iteration = 0;
    2.         for(int i = 0; i < maxItemSpawnPoints; i++)
    3.         {
    4.  
    5.             randomIndex = Random.Range(0, maxItemSpawnPoints);
    6.             while(listIndexes.Contains(randomIndex))
    7.             {
    8.                 randomIndex = Random.Range(0, maxItemSpawnPoints);
    9.                 if (iteration > maxItemSpawnPoints)
    10.                 {
    11.                     Debug.Log("Break While loop");
    12.                     break;
    13.                 }
    14.                 iteration++;
    15.                 Debug.Log(iteration + " Iteration number");
    16.             }
    17.             if(!listIndexes.Contains(randomIndex))
    18.             {
    19.                 listIndexes.Add(randomIndex);
    20.                 Debug.Log("Value added to listindexes");
    21.             }
    22.         }
    23.         //debug only
    24.         foreach(var item in listIndexes)
    25.         {
    26.             Debug.Log(item + " foreach");
    27.         }
     
  39. CodeRonnie

    CodeRonnie

    Joined:
    Oct 2, 2015
    Posts:
    323
    I just skimmed this, so I'm sorry if I missed it, but you really should just create an array of integers, fill it out with every value from min to max, then go through the array and randomize it. Iterate through, swapping the current value with another at a random index in your array. There may be better ways than this to achieve the type of randomization you want, that's just off the top of my head. This all happens during load time, when the random numbers need to be reshuffled, and previous numbers can come back up again.

    Then, at run time, you just iterate through the shuffled list one index at a time whenever they need a new "random" number. That way you're not performing expensive operations, looping unnecessarily, calling Array.Copy() under the hood, generating garbage, or anything nasty like that when your game is actually running. No number that they've had previously will come up again because each possible number is only in the array once.
     
    orionsyndrome, halley and Ryiah like this.
  40. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    1,905
    Yup, this is the best approach. Think of it like a deck of cards. Shuffle once. Can't deal the same card twice until you exhaust the deck or shuffle again.

    This approach also works well with a growing deck/array of possible answers. You can shuffle the remaining items whenever you want, and add new items to the remaining deck. Let's say you have an NPC who will give you a cookie every time you show him an [insert random game item]. But you only want this NPC to ask about items which you may have already encountered somewhere in the game before; when the character is just starting out you want choices like Apple or SharpPointyStick, but you don't want to give any spoilers like AlienDeathRayGun until they got to the "aliens are real" chapter. When the player first encounters a new possible cookie-rewardable item, add it to the remaining cookie-rewards array and shuffle. If the rewardables array happens to be empty at the time, the NPC can just not give out the quest.
     
    orionsyndrome likes this.
  41. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    If nothing else, these old threads serve as a testament to how much Unity community has matured over time.