- Home /
Randomizing numbers on a list.
Im doing a simple Quiz game and I needed a way to get unique random numbers, so I can play the questions in a random order without repeating them. Found the code.
private int maxNumbers = 6;
private List<int> uniqueNumbers;
private List<int> finishedList;
void Start ()
{
uniqueNumbers = new List<int>();
finishedList = new List<int>();
GenerateRandomList();
}
public void GenerateRandomList()
{
for (int i = 0; i < maxNumbers; i++)
{
uniqueNumbers.Add(i);
}
for (int i = 0; i < maxNumbers; i++)
{
int ranNum = uniqueNumbers[Random.Range(0, uniqueNumbers.Count)];
finishedList.Add(ranNum);
uniqueNumbers.Remove(ranNum);
}
//Just to see them in the console
foreach (int data in finishedList)
{
Debug.Log(data);
}
}
The problem here is that the random numbers appear all in once. I need one number at a time and the next randomization just start when I want to, by pressing a button or whatever. But that's just the way I feel I can do. For sure there's a way to do with the numbers all in once in the list.
They are appearing all at once because youre iterating through every number in those for loops.
Youre just shuffling th around, which is pointless. Just grab a random one from the list, use it, then remove it. No need for a second List unless youre reusing the questions.
Answer by AMU4u · Feb 25, 2017 at 05:35 PM
What you want is the Fisher-Yates Shuffle, my friend!
https://www.dotnetperls.com/fisher-yates-shuffle
...and a video!
https://www.youtube.com/watch?v=H5LGwDanx2U
Edit - I made it so your script works the way you kind of described it wanting.
private int maxNumbers = 6;
private List<int> uniqueNumbers;
private List<int> finishedList;
void Start ()
{
uniqueNumbers = new List();
finishedList = new List();
}
public void GenerateRandomList()
{
for (int i = 0; i < maxNumbers; i++)
{
uniqueNumbers.Add(i);
}
for (int i = 0; i < maxNumbers; i++)
{
int ranNum = uniqueNumbers[Random.Range(0,uniqueNumbers.Count)];
finishedList.Add(ranNum);
Debug.Log(ranNum);
uniqueNumbers.Remove(ranNum);
}
}
void OnGUI()
{
if (GUI.Button(new Rect(100, Screen.height - 175, 100, 30), "Print num"))
{
GenerateRandomList();
}
In the end, the code I showed does the same thing. I'm quite a beginner, so it might be me but I dont see the solution for my problem in there.
If you understand the method, than you should really understand how to return the shuffled variable you are looking for.
Your question says it returns them all at the same time. With the Fischer-Yates method, you'll learn to return the unique random variable while shuffling it at the end.
I must be missing something? why would he ever meed to shuffle the list when he is already randomly picking them using Random.Range(count)?
this function would shuffle a list:
public List<int> mix(List<int> n){
List<int> r = new List<int> ();
while (n.Count>0) {
int i = Random.Range(0,n.Count);
r.Add(n[i]);
n.RemoveAt(i);
}
return r;
}
but even easier, why not just randomly pick a number from your list and remove it?
A$$anonymous$$U4U has given you an answer that works ...here is another example of the same algorithm and how to use it.
public static class IEnumerableExtensions
{
private static Random rng = new Random();
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1) {
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
}
public class QuizItem
{
public string Name { get; set; }
public QuizItem(string name)
{
Name = name;
}
}
class Program
{
static void $$anonymous$$ain(string[] args)
{
var quiz = new List<QuizItem>{
new QuizItem("Question 1"),
new QuizItem("Question 2"),
new QuizItem("Question 3"),
new QuizItem("Question 4"),
new QuizItem("Question 5"),
};
quiz.Shuffle();
Console.WriteLine(string.Join(", ", quiz.Select(q => q.Name)));
quiz.Shuffle();
Console.WriteLine(string.Join(", ", quiz.Select(q => q.Name)));
}
}
Answer by CarterG81 · Feb 25, 2017 at 11:26 PM
Perhaps I am not understanding the question, but...
Wouldnt this be as simple as....
int PickAndRemoveOneRandom()
{
int randomNum = Random.Range(0, myList.Count); //Pick one random number based on total numbers.
int theNumber = myList[randomNum]; //store the number
myList.Remove(randomNum); //Remove number so it cant be used again.
return theNumber; //return the number
}
Then use the method in your code
int theNumber = PickAndRemoveOneRandom();
Or onClick event on a Unity UI Button --> PickAndRemoveOneRandom() and add your " //do stuff code" in the function instead of returning an int
Options:
You could pass the list as parameter: void myMethod(List myList) { }
You could make this support generic types (return & remove ANY type from any List, insuring uniqueness
Keep used numbers in a second List (just add one line before return. usedNumberList.Add(theNumber)
To create the list, you could simply use Unity editor.
public List myQuestions = new List()
Create a gameobject, attach script. In the Inspector add elements to the List, type in your questions, and save them as prefab Gameobjects.
And thr function:
void PickAndRemoveOneQuestion()
{
int randomNum = Random.Range(0, myQuestions.Count);
string theQuestion = myQuestions[randomNum];
DisplayQuestion(theQuestion);
myQuestions.Remove(theQuestion);
}
Answer by Nailon_amstronic · Mar 21, 2020 at 11:55 AM
This is preety old but I was looking for an answer to the same question and I ended writing an algorithm my self. Seems to work preety well.
//"from" and "to" are inclusive
public List<int> GenerateRandomIntegerList(int from, int to)
{
List<int> numbersDone = new List<int>();
while (true)
{
int i = Random.Range(from, to + 1);
bool isNew = true;
foreach (int num in numbersDone)
{
if (num == i)
{
isNew = false;
}
}
if(isNew)
{
numbersDone.Add(i);
if(numbersDone.Count == to + 1)
break;
}
}
return numbersDone;
}
Answer by TCMOREIRA · Mar 21, 2020 at 11:22 PM
Just for the sake of completeness, since all the other answers seem to be pretty good already, but I usually do this using a Set structure (HashSets of int in particular).
If you know your numbers beforehand: Here's some pseudo-code:
- Let there be an empty Set of type Set of Integers called 'SortitionSet'
- Let there be an Integer Array containing the numbers we want to Sortition called 'IntegersArray'
Generate the SortitionSet:
While(Length of IntegersArray > 0)
Let there be an auxiliary variable which receives a the resulting element from the deletion of the first element of the array called 'aux'
Try to insert aux into SortitionSet (It should return false in case of a repeated number)
In case the insertion was successful continue, otherwise throw exception
Return SortitionSet
Give a Random Number from the Set by Removing it:
Return SortitionSet.Remove(Random Index)
</end>
If you don't know your numbers beforehand:
Here's some pseudo-code:
<begin/>
- Let there be an empty Set of type Set of Integers called 'SortitionSet'
- Let there be a positive Integer number greater than zero called 'AmountOfNumbersToGenerate'
Then:
While(AmountOfNumbersToGenerate > 0)
Let there be an auxiliary variable which receives a randomly generated number called 'aux'
Try to insert aux into SortitionSet (It should return false in case of a repeated number)
In case the insertion was successful reduce AmountOfNumbersToGenerate by 1
Return SortitionSet
</end>
Hope this helps someone! Cheers!
Your answer
Follow this Question
Related Questions
Pick between two floats 2 Answers
Random element from the list 1 Answer
Printing a GUI selection grid in order 1 Answer
How can I display 4 items in random order but never double? 2 Answers
A node in a childnode? 1 Answer