Random number with no repetition

Hi, I have now made a question answering game, using JSON to connect the questions. Now I need to use button to activate a random number that does not repeat within the range to make the questions appear randomly until all the questions in this question bank have appeared. However, I have encountered some problems, and the random number method I use cannot be completed.

 public int[] values = new int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    private int A;
    public void test() {
    int GetRandomIndex(int userindex)
    {
        int modelindex = Random.Range(1, 10);
        if (values[userindex] != modelindex)
        {
            var a = from k in values where k == modelindex select k;
            while (a.ToArray().Length <= 1) 
            {
                return modelindex; 
            }
        }
        return GetRandomIndex(userindex);
    }
        A = GetRandomIndex(A);
        Debug.Log(A);
    }

look for some shuffle array scripts,
like Randomize Array in C#

so it first makes the array to be in random order just once,
then can pick the items 1 by 1 starting from 0 index.

I implemented that algorithm and got repeats. Maybe I missed something but was pretty careful.

Here’s what I wrote. Probably can be more compact (?) but works fine. I put it in a static class with other utility functions like remapping a value from one range to another…which should be in the Mathf library, cough, cough… :wink:

public static List<int> randomizeList(List<int> argList) // take list in, randomize order
    {
        int argListCount = argList.Count; // need holder for original length as values are removed in the for loop
        List<int> temp = new List<int>(); // list to hold the randomized values

        for (int i = 0; i < argListCount; i++)
        {
            int argListIndex = Random.Range(0, argList.Count);
            temp.Add(argList[argListIndex]);
            argList.RemoveAt(argListIndex); // remove so no repeats
            Debug.Log(temp[i]);
        }
        return temp;
    }

Whoa, I dunno what that crazy stuff is… throw that away. Just reason through the code: you choose X numbers and Random never gives you one number (very likely). That number won’t be in your series! COMPLETELY broken!

(Correction: @seejayjames Look at your code, you have a clear bug on the random number. You did not implement the Knuth shuffle. The first argument to Random.Range() cannot be zero. Your bug is on Line 8.)

Either way, here is what I always use: FAR less memory intensive, no lists, no adding/removing, no garbage collection done.

Here is what you want:

  1. Make an array, put the numbers 0 to max into each one.

  2. Now implement a Fisher Yates shuffle, pseudocode here:

  1. and then iterate the items in order.

Seriously, That’s IT. I’ve been shuffling for a long time and I’ve worked on a lot of casino titles.

2 Likes

Nice.
Note: that was my own code, not my implementation of the Knuth shuffle. Looking at Fisher-Yates, I think my code is identical to their original “paper-and-pencil” method:

  1. Take an arbitrary list
  2. Choose one element at random (using the length of the list to pick the random index)
  3. Add that element to another list (that starts empty)
  4. Remove that element from the original list
  5. Repeat steps 2-4 until original list is empty

Though I imagine adding and removing are wasteful, so swapping works better.

1 Like

Knuth’s:

void List<int> randomizeList(List<int> argList) // take list in, randomize order
    {
        for (int i = 0; i < argList.Count; i++)
        {
            int temp = argList[i];
            int rand = Random.Range(i, argList.Count);
            argList[i] = argList[rand];
            argList[rand] = temp;
            Debug.Log(argList[i]);
        }
        return argList;
    }

Looking up an implementation of Fisher Yates Shuffle in C# and changed it up a slight bit (naming, namespace etc).

using System;
using System.Collections.Generic;

namespace Utilities
{
    public static class ShuffleUtility
    {
        private static readonly Random random = new Random();

        /// <summary>
        /// Shuffle the array.
        /// </summary>
        /// <typeparam name="T">Array element type.</typeparam>
        /// <param name="array">The Array to shuffle.</param>
        public static void Shuffle<T>(IList<T> array)
        {
            var arrayCount = array.Count;
            for (var index = 0; index < (arrayCount - 1); index++)
            {
                var randomIndex = index + random.Next(arrayCount - index);
                var arrayValue = array[randomIndex];
                array[randomIndex] = array[index];
                array[index] = arrayValue;
            }
        }
    }
}