Search Unity

CODE: Unique random numbers

Discussion in 'Scripting' started by iEarthHero1, Sep 10, 2010.

  1. iEarthHero1

    iEarthHero1

    Joined:
    Jul 28, 2009
    Posts:
    301
    I could not find a Uniscript specific code for generating unique random numbers.

    I just spent two days trying to do this myself. While in theory this should be unbelievably easy... having to kill Unity every time an infinite loop starts is nightmare for debugging and trouble shooting.

    I post this code below so that there will be least one Uniscript version on the forum. Honestly, I think it sucks but it works and I am tired of dealing with it.

    If anyone wants to add a more elegant/effecient version to this thread I would be very happy to see it.

    Code (csharp):
    1.  
    2. FUNCTION:
    3.  
    4. // random_number_unique_fun()
    5.        
    6.     function random_number_unique_fun(array_intarr : int[], array_min_int : int, array_max_int : int)
    7.     {
    8.     for (var for_loop_1_int : int = array_min_int; for_loop_1_int < array_max_int; for_loop_1_int++)
    9.         {
    10.             while(true)
    11.             {
    12.                 number_random_int = Random.Range(0, 24);
    13.                 for (var for_loop_2_int : int = array_min_int; for_loop_2_int < array_max_int; for_loop_2_int++)
    14.                 {
    15.                     if(array_intarr[for_loop_2_int] == number_random_int) break;
    16.                 }
    17.                 if(for_loop_2_int == array_max_int || (for_loop_1_int == (array_max_int - 1)  for_loop_2_int == (array_max_int - 1)))
    18.                 {
    19.                     array_intarr[for_loop_1_int] = number_random_int;
    20.                     break;
    21.                 }
    22.             }
    23.         }
    24.     }
    25. // var random_number_unique_fun
    26.  
    27.     var number_random_int : int;
    28.  
    Code (csharp):
    1. CALL:
    2.  
    3. random_number_unique_fun(array, 0, 24)

    Thanks.
     
  2. appels

    appels

    Joined:
    Jun 25, 2010
    Posts:
    2,687
    Random.Range(-10, 10)
    it's in the script reference
     
  3. iEarthHero1

    iEarthHero1

    Joined:
    Jul 28, 2009
    Posts:
    301
    Lol...

    Um... I guess you did not notice the word UNIQUE.

    The above function will give you a list of random numbers from min to max THAT DO NOT REPEAT.

    If you just use what you describe you will get repeats from repeated calls.

    I am a little surprised that you did not pick up on that from looking over the code sample.

    Thanks.
     
  4. appels

    appels

    Joined:
    Jun 25, 2010
    Posts:
    2,687
    yeah, lol sorry, it's late.
    thanks for posting, will be helpfull.
     
  5. Hoff

    Hoff

    Joined:
    Jun 20, 2010
    Posts:
    223
    It looks like your code is looking to fill a 24 element array with random (and unique) values from 0 to 24? Um, isn't that the same thing as filling an array with 0 to 24, and then randomize the array itself? I am assuming this because you hardcoded the rabdom function with a range of 0 - 24, you pass in the array min and max, but the array will only ever have 25 unique elements, period.
     
  6. IJM

    IJM

    Joined:
    Aug 31, 2010
    Posts:
    143
    Nice 8)
     
  7. Chris-Sinclair

    Chris-Sinclair

    Joined:
    Jun 14, 2010
    Posts:
    1,326
    Unfortunately, iEarthHero1, it is pretty easy, but my quick run at it has some serious implications (see later) Sorry you had to spend so much time on it; I know how frustrating it can be sometimes:
    (untested, written in notepad, etc.)
    Code (csharp):
    1. var sortedArray : Array = BuildArray(24);
    2. var randomizedArray : Array = RandomizeArray(sortedArray);
    3.  
    4.  
    5. function BuildArray(size : int) : Array
    6. {
    7.     var builtArray : Array = new Array();
    8.     for (var i : int = 0; i < size; i++)
    9.     {
    10.         builtArray.Push(i);
    11.     }
    12. }
    13.  
    14. function RandomizeArray(data : Array) : Array
    15. {
    16.     var randomizedArray : Array = new Array();
    17.    
    18.     while (data.length > 0)
    19.     {
    20.         var index : int = Random.Range(0, data.length);
    21.         randomizedArray.Push(data[index]);
    22.         data.RemoveAt(index);
    23.     }
    24.  
    25.     return data;
    26. }
    Keep plugging away though. This is how developers improve! Though it looks like yours avoids creating an additional array, which is good. And I'm sure there's an even easier way of doing this than what I posted here. Mine note only creates a new array, but destroys the input array in the process! :O Oh yeah, am I lazy or what? :D
     
  8. Hoff

    Hoff

    Joined:
    Jun 20, 2010
    Posts:
    223
    You basically did what I said :p

    Unfortunately you use two arrays but it can be done in one
     
  9. iEarthHero1

    iEarthHero1

    Joined:
    Jul 28, 2009
    Posts:
    301
    Your code looks clever and elegant but I am not familiar with the syntax.

    Your example will allow me to learn the syntax.

    But I am confused.

    How do I call this function and how do I get the values out of it for use in my program?

    Maybe something like this?...


    Code (csharp):
    1.  
    2. var square_location_intarr : int[];
    3.   square_location_intarr = new int[24];
    4. RandomizeArray(square_location_intarr);
    5.  
    [EDIT: I realize I have to fill the array first.]

    Thanks.
     
  10. Chris-Sinclair

    Chris-Sinclair

    Joined:
    Jun 14, 2010
    Posts:
    1,326
    I had another thought while I was on the toilet:
    (again, notepad code. Likely buggy, "size" might have to be length-1 for example)
    Code (csharp):
    1. function RandomizeArray(data : object[]) : void
    2. {  
    3.     var size : int = data.length;
    4.  
    5.     for (var i : int = 0; i < size; i++)
    6.     {
    7.         var indexToSwap : int = Random.Range(i, size);
    8.        
    9.         var oldValue : object = data[i];
    10.         data[i] = data[indexToSwap];
    11.         data[indexToSwap] = oldValue;
    12.     }
    13. }
    14.  
    15. //how to use
    16.  
    17. var myArray : int[] = BuildSortedArray();
    18. //myArray is sorted 0, 1, 2, 3, 4...
    19.  
    20.  
    21. RandomizeArray(myArray);
    22. //myArray is now randomized 8, 3, 5, 2, 7...
    Also, iEarthHero1, I had some sample usage at the top of my code snippet in the previous post. In my old snippet, it returned the randomized array so you would have to assign its return value. In my new snippet, it alters the passed array rather than creating a new one. It should also work on any array type.
     
  11. iEarthHero1

    iEarthHero1

    Joined:
    Jul 28, 2009
    Posts:
    301
    Wow.

    That is a brilliant piece of code... simple, clean, elegant.

    Nice.

    I now see how placing the numbers into an array is how to avoid the repeats.

    I appreciate your efforts. This is very instructive to me.

    Thanks.
     
  12. iEarthHero1

    iEarthHero1

    Joined:
    Jul 28, 2009
    Posts:
    301
    Question on the array that you are using.

    It is my understanding that Javascript arrays are slow but Unity arrays are fast.

    Are you creating and using a slow Javascipt array or a fast Unity array?

    Thanks.
     
  13. Chris-Sinclair

    Chris-Sinclair

    Joined:
    Jun 14, 2010
    Posts:
    1,326
    I have no idea; I don't even touch UnityScript with a ten-foot pole. I only pump it out on the forums because that's what everyone needs help in. :D The only reason I can script in it is because I have boatloads of experience programming JavaScript (real JavaScript) and ActionScript. Which is also one of the main reasons why I despise it so much because it isn't actually JavaScript and has some of its own quirks (which are pretty deadly quirks in my opinion).

    My second script though should be fast regardless of the array type because it's just swapping the elements around; it's not resizing the array (typically it's the resizing operations that are expensive).

    EDIT: looking at the script, I think the Random.Range call needs to be modified to:
    Random.Range(i, size - 1);

    Just a guess if you end up getting an OutOfRange-ish exception.
     
  14. iEarthHero1

    iEarthHero1

    Joined:
    Jul 28, 2009
    Posts:
    301
    Finally I can contribute... : ) your code is good as is...

    Code (csharp):
    1. Description
    2.  
    3. Returns a random integer number between min [inclusive] and max [exclusive] (Read Only).
    4.  
    5. If max equals min, min will be returned. The returned value will never be max unless min equals max.
     
  15. Chris-Sinclair

    Chris-Sinclair

    Joined:
    Jun 14, 2010
    Posts:
    1,326
    Ahh good stuff. I wonder why they designed the random function like that.
     
  16. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    So you don't have to subtract one from the max all the time, considering the usage Random.Range usually gets.

    --Eric
     
  17. justinlloyd

    justinlloyd

    Joined:
    Aug 5, 2010
    Posts:
    1,680
    Your Random.Range call is correct. Random.Range when using integers as the parameters is inclusive-exclusive. It is to do with the way random numbers or generated and also the modulo operator.

    Fizix's solution however has a number of fundamental bugs, not least of which is swapping the last number in the array with itself, i.e. as the iteration progresses, you receive diminishing returns on the swap order.

    Whilst the solutions discussed are satisfactory, and also one of the many proposed solutions you will find on websites such as Stack Overflow, there are "better" ones if you don't want to shuffle around elements of an array or list or squander precious memory. What you are after is a Full Cycle LCG PRNG or Full Cycle LFSR PRNG.

    A modified Mersenne Twister with a period equal to the series of numbers to be generated is a great solution, though has a sizable computational overhead for small systems.

    Alternatively, if all you need to do is shuffle a deck of cards, or a couple of decks, you can use an n-bit non-repeating generator such as a 6-bit LFSR, which is forced to only return results in the proper range. I believe OpenBSD uses such a system for generating certain ID numbers. This solution, in various incarnations, has been around since the late 1970's at least.

    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. using System.Text;
    6. using System.Diagnostics;
    7.  
    8. namespace FullCyclePRNGThrowaway
    9. {
    10.     class Program
    11.     {
    12.         /// <summary>
    13.         /// Generates a random list of numbers that guarantees all of the numbers
    14.         /// are unique, i.e. no number will be duplicated.
    15.         /// </summary>
    16.         static void Main(string[] args)
    17.         {
    18.             Debug.WriteLine("Shuffled: ");
    19.             Random rnd = new Random();
    20.             int lfsr = (rnd.Next() % 24) + 1;
    21.             for (int i = 0; i < 24; i++)
    22.             {
    23.                 do
    24.                 {
    25.                     lfsr ^= (lfsr << 9);
    26.                     lfsr = 31;
    27.  
    28.                     lfsr ^= (lfsr >> 2);
    29.                     lfsr = 31;
    30.  
    31.                     lfsr ^= (lfsr << 3);
    32.                     lfsr = 31;
    33.  
    34.                 } while (lfsr > 24);
    35.  
    36.                 Debug.Write(lfsr.ToString() + ", ");
    37.             }
    38.  
    39.         }
    40.  
    41.     }
    42.  
    43. }
    44.  
    45.  

    And the output:
    Code (csharp):
    1.  
    2. Shuffled: 21, 16, 20, 7, 22, 11, 1, 9, 19, 15, 12, 23, 2, 18, 6, 24, 14, 5, 4, 13, 17, 10, 8, 3,
    3.  
    Twenty-four numbers, in the range 1..24, randomly shuffled, no repeats, using a very simple function.

    If you were to extend the for loop to a value greater than the sample size, the sequence of randomly generated numbers would repeat, that is:

    Code (csharp):
    1.  
    2. for (int i = 0; i < 24 * 3; i++)
    3.  
    With the result being:
    Code (csharp):
    1.  
    2. Shuffled: 1, 9, 19, 15, 12, 23, 2, 18, 6, 24, 14, 5, 4, 13, 17, 10, 8, 3, 21, 16, 20, 7, 22, 11, 1, 9, 19, 15, 12, 23, 2, 18, 6, 24, 14, 5, 4, 13, 17, 10, 8, 3, 21, 16, 20, 7, 22, 11, 1, 9, 19, 15, 12, 23, 2, 18, 6, 24, 14, 5, 4, 13, 17, 10, 8, 3, 21, 16, 20, 7, 22, 11,
    3.  
    I've been using a variation of this code since about 1979 and I cringe every time I see someone put a list of numbers in to an array and then shuffle the array around for something so simple as a couple of random numbers being returned.

    I'll put up a re-usable Unity-fied C# class on my website (http://www.otakunozoku.com) for those that want a drop-in solution for Unity3D in the next day or so.
     
  18. Chris-Sinclair

    Chris-Sinclair

    Joined:
    Jun 14, 2010
    Posts:
    1,326
    Good stuff, JustinLloyd. :D I'll have to save that snippet, although I don't think I've ever had to create a list of unique random numbers... yet.
     
  19. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    Just for future reference, there's a library of random number routines (including one for unique random numbers) in this thread.
     
  20. MathieuB_legacy

    MathieuB_legacy

    Joined:
    Dec 23, 2010
    Posts:
    1
    I modified a few things because it didn't work for me.
    Now this return a randomized version of a given Array.
    Here is my version :

    Code (csharp):
    1. function RandomizeArray(data : Array) : Array
    2. {  
    3.     var size : int = data.length;
    4.  
    5.     for (var i : int = 0; i < size; i++)
    6.     {
    7.         var indexToSwap : int = Random.Range(i, size);
    8.        
    9.         var oldValue = data[i];
    10.         data[i] = data[indexToSwap];
    11.         data[indexToSwap] = oldValue;
    12.     }
    13.    
    14.     return data;
    15. }
    thx.
     
  21. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    I think I dig the two array concept more, fill an array, generate a random number between 0 and the end of the array. append that number to the new array and remove the number from the old one. Simple and fast.
     
  22. KevinCodes4Food

    KevinCodes4Food

    Joined:
    Dec 6, 2013
    Posts:
    61
  23. Rphysx

    Rphysx

    Joined:
    Mar 26, 2014
    Posts:
    54
    I've come up to this after few tries and it seems to work nice and well and it gives truly random unique numbers. Just test it and tell me if it works for you

    Code (csharp):
    1.  
    2. public static class ShuffleLike
    3.     {
    4.         public static int[] arr = new int[100];
    5.         public static Random R = new Random();
    6.         public static int Aint;
    7.         public static List<int> IntList= new List<int>();
    8.  
    9.  
    10.  
    11.         public static void FillList()       //Static method to call            
    12.         {
    13.             for (int i = 1; i < 101; i++)
    14.             {
    15.                 IntList.Add(i);
    16.             }
    17.             FillArray();
    18.         }
    19.  
    20.         static void FillArray()
    21.         {
    22.             for (int i = 0; i < 100; i++)
    23.             {
    24.                 Aint = R.Next(0, IntList.Count);
    25.                 arr[i] = IntList[Aint];
    26.                 IntList.RemoveAt(Aint);
    27.             }
    28.         }
    29.     }
    30.