39

I've searched for a while and been struggling to find this, I'm trying to generate several random, unique numbers is C#. I'm using System.Random, and I'm using a DateTime.Now.Ticks seed:

public Random a = new Random(DateTime.Now.Ticks.GetHashCode());
private void NewNumber()
{
    MyNumber = a.Next(0, 10);
}

I'm calling NewNumber() regularly, but the problem is I often get repeated numbers. Some people suggested because I was declaring the random every time I did it, it would not produce a random number, so I put the declaration outside my function. Any suggestions or better ways than using System.Random ? Thank you

demo
  • 6,038
  • 19
  • 75
  • 149
Christian Peut
  • 409
  • 1
  • 4
  • 4
  • 2
    http://csharpindepth.com/Articles/Chapter12/Random.aspx – Habib Jan 23 '13 at 05:56
  • As long as you are only creating the Random object once, you shouldn't have a problem. If you are wanting the numbers to be unique (haven't already had that number) then you'll need to add extra than just using Random – RoneRackal Jan 23 '13 at 05:57
  • 2
    Are you looking for "permutation of numbers 1..10" instead of "random number in range 1..10"? (Definiitely give you 10 unique numbers in random sequence) – Alexei Levenkov Jan 23 '13 at 06:00
  • 1
    why don't you generate guid? do you need int or any unique string is enough? – Burhan Uddin Jan 23 '13 at 06:00
  • 2
    What do you expect your code to produce after all ten unique values are produced? What should be 11th value (of 10)? – Andrew Savinykh Jan 23 '13 at 06:02

18 Answers18

32

I'm calling NewNumber() regularly, but the problem is I often get repeated numbers.

Random.Next doesn't guarantee the number to be unique. Also your range is from 0 to 10 and chances are you will get duplicate values. May be you can setup a list of int and insert random numbers in the list after checking if it doesn't contain the duplicate. Something like:

public Random a = new Random(); // replace from new Random(DateTime.Now.Ticks.GetHashCode());
                                // Since similar code is done in default constructor internally
public List<int> randomList = new List<int>();
int MyNumber = 0;
private void NewNumber()
{
    MyNumber = a.Next(0, 10);
    if (!randomList.Contains(MyNumber))
        randomList.Add(MyNumber);
}
anastaciu
  • 23,467
  • 7
  • 28
  • 53
Habib
  • 219,104
  • 29
  • 407
  • 436
  • 3
    +1. Also for anything more than 10 list would be poor choice, HashSet would be better. And there is no need to initialize random this way - similar code done in default constructor anyway... – Alexei Levenkov Jan 23 '13 at 06:02
  • 11
    Algorithmic complexity is bad. This should not be accepted as an answer. – Sergey Kostrukov Dec 03 '18 at 21:43
  • This is not a random list, you check for dublicates, but is real world duplicates may happen!! – Arsalan Mar 06 '20 at 10:24
  • I think the solution will matter on the accepted range, and the amount of chosen numbers. If the amount of chosen numbers is high, then shuffling Enumerable.Range.ToArray() will work the best, whereas if you choose low amount of numbers from huge range, then HashSet makes sense. – Ferazhu Jun 10 '21 at 07:00
  • 2
    really? this is a very bad algorithm. DO NOT USE IT! – Nellymandela Nov 08 '21 at 10:09
26

You might try shuffling an array of possible ints if your range is only 0 through 9. This adds the benefit of avoiding any conflicts in the number generation.

var nums = Enumerable.Range(0, 10).ToArray();
var rnd = new Random();

// Shuffle the array
for (int i = 0;i < nums.Length;++i)
{
    int randomIndex = rnd.Next(nums.Length);
    int temp = nums[randomIndex];
    nums[randomIndex] = nums[i];
    nums[i] = temp;
}

// Now your array is randomized and you can simply print them in order
for (int i = 0;i < nums.Length;++i)
    Console.WriteLine(nums[i]);
itsme86
  • 19,266
  • 4
  • 41
  • 57
  • I just tested that one out and it worked well too! thanks very much! – Christian Peut Jan 23 '13 at 06:13
  • Beware! That is an incorrect shuffle implementation! I'll post a correct implementation in a moment. – Matthew Watson Jan 23 '13 at 09:03
  • (Too late to edit my comment above now). Please see my post below for a correct implementation, as well as a link to some discussion about it. – Matthew Watson Jan 23 '13 at 09:13
  • Why not just `Enumerable.Range(0, 10).OrderBy(x => rnd.NextDouble()).ToArray();` ? – thepirat000 Nov 14 '15 at 20:00
  • 3
    @thepirat000 The only real reason would be speed. For 10 elements it obviously doesn't make a difference, but it will for large collections.OrderBy() is likely an O(N log N) algorithm, where the shuffle is O(N). – itsme86 Nov 15 '15 at 01:59
  • @itsme86 plus if you combine this approach with returning shuffled value as you go (http://stackoverflow.com/questions/1287567/is-using-random-and-orderby-a-good-shuffle-algorithm) it gives O(1) for getting single value and O(number_of_desired_items) for whole sequence (rather than O(range_size) if you shuffle whole range first). – Alexei Levenkov Apr 27 '17 at 17:17
14

NOTE, I dont recommend this :). Here's a "oneliner" as well:

var result = Enumerable.Range(0,9).OrderBy(g => Guid.NewGuid()).ToArray();
JOSEFtw
  • 9,781
  • 9
  • 49
  • 67
  • 1
    Like where you've gone here. But why not: `Enumerable.Range(0, 9).OrderBy(g => rand.NextDouble()).ToList()` then you get the Range as per question. – SDK Jan 15 '16 at 15:28
  • 1
    If you want two unique numbers between 1 and 10,000,000 this will be extremely slow. – Rob Jan 16 '17 at 04:54
10

I'm posting a correct implementation of a shuffle algorithm, since the other one posted here doesn't produce a uniform shuffle.

As the other answer states, for small numbers of values to be randomized, you can simply fill an array with those values, shuffle the array, and then use however many of the values that you want.

The following is an implementation of the Fisher-Yates Shuffle (aka the Knuth Shuffle). (Read the "implementation errors" section of that link (search for "always selecting j from the entire range of valid array indices on every iteration") to see some discussion about what is wrong with the other implementation posted here.)

using System;
using System.Collections.Generic;

namespace ConsoleApplication2
{
    static class Program
    {
        static void Main(string[] args)
        {
            Shuffler shuffler = new Shuffler();
            List<int> list = new List<int>{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            shuffler.Shuffle(list);

            foreach (int value in list)
            {
                Console.WriteLine(value);
            }
        }
    }

    /// <summary>Used to shuffle collections.</summary>

    public class Shuffler
    {
        public Shuffler()
        {
            _rng = new Random();
        }

        /// <summary>Shuffles the specified array.</summary>
        /// <typeparam name="T">The type of the array elements.</typeparam>
        /// <param name="array">The array to shuffle.</param>

        public void Shuffle<T>(IList<T> array)
        {
            for (int n = array.Count; n > 1; )
            {
                int k = _rng.Next(n);
                --n;
                T temp = array[n];
                array[n] = array[k];
                array[k] = temp;
            }
        }

        private System.Random _rng;
    }
}
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • @downvoters: Did you investigate the problem with the other random shuffle? The answer in question is the one below the accepted answer. It is using an incorrect shuffle algorithm. Also see my comments to that answer. – Matthew Watson Jun 11 '16 at 19:30
  • Why is this better than the other? (apar from that you made it generic) – Mitulát báti Apr 04 '17 at 20:59
  • 1
    @Mitulátbáti Which answer do you mean by "the other"? If you mean "the accepted answer" then this one is better because it has complexity `O(N)` whereas the accepted answer has complexity `O(N^2)`. – Matthew Watson Apr 05 '17 at 12:10
  • 1
    Even you referred to it as "the other one" several times. I meant that one. – Mitulát báti Apr 10 '17 at 21:31
  • @Mitulátbáti I specifically referred to the other shuffle algorithm, so assuming that is indeed what you mean then this answer is better than that one because this one produces a uniform shuffle - the other shuffle algorithm (there is only one other shuffle algorithm posted as an answer) has a bug and produces a non-uniform shuffle. Note that my answer here already provides a link to the Wikipedia article which has some details about why the other shuffle algorithm is broken. – Matthew Watson Apr 11 '17 at 07:53
4

This is a unity only answer:

Check this ready-to-use method: Give in a range & count of number you want to get.

public static int[] getUniqueRandomArray(int min, int max, int count) {
    int[] result = new int[count];
    List<int> numbersInOrder = new List<int>();
    for (var x = min; x < max; x++) {
        numbersInOrder.Add(x);
    }
    for (var x = 0; x < count; x++) {
        var randomIndex = UnityEngine.Random.Range(0, numbersInOrder.Count);
        result[x] = numbersInOrder[randomIndex];
        numbersInOrder.RemoveAt(randomIndex);
    }

    return result;
}
Evren Ozturk
  • 918
  • 2
  • 19
  • 39
  • Well UnityEngine.Random is a bit different then the C# equivalent. Of course you can convert to C# with little efforts. – Evren Ozturk Jan 30 '21 at 07:33
  • first for should probably go like this, otherwise max won't be selected. for (var x = min; x <= max; x++) – Filip12345 Apr 02 '23 at 20:24
3

Same as @Habib's answer, but as a function:

List<int> randomList = new List<int>();
int UniqueRandomInt(int min, int max)
{
    var rand = new Random();
    int myNumber;
    do
    {
       myNumber = rand.Next(min, max);
    } while (randomList.Contains(myNumber));
    return myNumber;
}

If randomList is a class property, UniqueRandomInt will return unique integers in the context of the same instance of that class. If you want it to be unique globally, you will need to make randomList static.

John Pankowicz
  • 4,203
  • 2
  • 29
  • 47
2

Depending on what you are really after you can do something like this:

using System;
using System.Collections.Generic;
using System.Linq;

namespace SO14473321
{
    class Program
    {
        static void Main()
        {
            UniqueRandom u = new UniqueRandom(Enumerable.Range(1,10));
            for (int i = 0; i < 10; i++)
            {
                Console.Write("{0} ",u.Next());
            }
        }
    }

    class UniqueRandom
    {
        private readonly List<int> _currentList;
        private readonly Random _random = new Random();

        public UniqueRandom(IEnumerable<int> seed)
        {
            _currentList = new List<int>(seed);
        }

        public int Next()
        {
            if (_currentList.Count == 0)
            {
                throw new ApplicationException("No more numbers");
            }

            int i = _random.Next(_currentList.Count);
            int result = _currentList[i];
            _currentList.RemoveAt(i);
            return result;
        }
    }
}
Andrew Savinykh
  • 25,351
  • 17
  • 103
  • 158
1

And here my version of finding N random unique numbers using HashSet. Looks pretty simple, since HashSet can contain only different items. It's interesting - would it be faster then using List or Shuffler?

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class RnDHash
    {
        static void Main()
        {
            HashSet<int> rndIndexes = new HashSet<int>();
            Random rng = new Random();
            int maxNumber;
            Console.Write("Please input Max number: ");
            maxNumber = int.Parse(Console.ReadLine());
            int iter = 0;
            while (rndIndexes.Count != maxNumber)
            {
                int index = rng.Next(maxNumber);
                rndIndexes.Add(index);
                iter++;
            }
            Console.WriteLine("Random numbers were found in {0} iterations: ", iter);
            foreach (int num in rndIndexes)
            {
                Console.WriteLine(num);
            }
            Console.ReadKey();
        }
    }
}
1

I noted that the accepted answer keeps adding int to the list and keeps checking them with if (!randomList.Contains(MyNumber)) and I think this doesn't scale well, especially if you keep asking for new numbers.

I would do the opposite.

  1. Generate the list at startup, linearly
  2. Get a random index from the list
  3. Remove the found int from the list

This would require a slightly bit more time at startup, but will scale much much better.

public class RandomIntGenerator
{
    public Random a = new Random();
    private List<int> _validNumbers;

    private RandomIntGenerator(int desiredAmount, int start = 0)
    {
        _validNumbers = new List<int>();
        for (int i = 0; i < desiredAmount; i++)
            _validNumbers.Add(i + start);
    }

    private int GetRandomInt()
    {
        if (_validNumbers.Count == 0)
        {
            //you could throw an exception here
            return -1;
        }
        else
        {
            var nextIndex = a.Next(0, _validNumbers.Count - 1);
            var number    = _validNumbers[nextIndex];
            _validNumbers.RemoveAt(nextIndex);
            return number;
        }
    }
}
Jack Mariani
  • 2,270
  • 1
  • 15
  • 30
  • 1
    but doesn't removing an element from a list take O(n) time? the index approach is a nice one, but removing an element from a list(built on an array in .NET) would consume time, especially when the list is very big! – Artavazd Aug 12 '20 at 15:29
  • Yes. You're right. In that case I just moved the consuming task earlier, rather than later. RemoveAt cost much early and less later, while Contains does the opposite (and it's also better memory-wise). I would need to optimize this. – Jack Mariani Aug 13 '20 at 06:53
  • I'll be glad to see your optimized approach. thanks for sharing! – Artavazd Aug 13 '20 at 08:51
  • 1
    to avoid list.Contains() you could use hashset for better performance and then ToArray() at the end – Mark Lauter Feb 02 '23 at 22:46
0

It's may be a little bit late, but here is more suitable code, for example when you need to use loops:

            List<int> genered = new List<int>();

            Random rnd = new Random();

            for(int x = 0; x < files.Length; x++)
            {
                int value = rnd.Next(0, files.Length - 1);
                while (genered.Contains(value))
                {
                    value = rnd.Next(0, files.Length - 1);
                }
                genered.Add(value);

                returnFiles[x] = files[value];
            }
Fedir Katushonok
  • 391
  • 5
  • 13
0
  • with Functional way*
        static Func<int> GetNextUniqueIntegerFunc(int min, int max)
        {
            var list = new List<int>();

            var random = new Random();

            int getNextValue()
            {
                while (true)
                {
                    var random_number = random.Next(min, max);

                    if (!list.Contains(random_number))
                    {
                        list.Add(random_number);

                        return random_number;
                    }
                }
            }

            return getNextValue;
        }
0
Random r = new Random(); int[] v = new int[10];
        for (int i = 0; i < 10; i++)
        {
            v[i] = r.Next(1, 25); // random numbers between (1) and (25)
            for (int j = 0; j < i; j++)
            {
                if (v[j] == v[i]) // if it is a duplicated value, create new one!
                    i--;
            }
        }

        for (int i = 0; i < 10; i++)
            textBox1.Text += v[i].ToString() + " ";
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Mohamed Fathallah Jul 03 '23 at 20:53
-1

unique random number from 0 to 9

      int sum = 0;
        int[] hue = new int[10];
        for (int i = 0; i < 10; i++)
        {

            int m;
            do
            {
                m = rand.Next(0, 10);
            } while (hue.Contains(m) && sum != 45);
            if (!hue.Contains(m))
            {
                hue[i] = m;
                sum = sum + m;
            }

        }
habib salimi
  • 116
  • 2
  • 8
-3

You could also use a dataTable storing each random value, then simply perform the random method while != values in the dataColumn

Herman Vercuiel
  • 69
  • 2
  • 13
-3

randomNumber function return unqiue integer value between 0 to 100000

  bool check[] = new bool[100001];
  Random r = new Random();
  public int randomNumber() {
      int num = r.Next(0,100000);
       while(check[num] == true) {
             num = r.Next(0,100000);
     }
    check[num] = true;
   return num;
 }
-3

hi here i posted one video ,and it explains how to generate unique random number

  public List<int> random_generator(){

  Random random = new Random();

   List<int> random_container = new List<int>;

     do{

       int random_number = random.next(10);

      if(!random_container.contains(random_number){

       random_container.add(random_number)
  }
}
   while(random_container.count!=10);


     return random_container; 
  }

here ,,, in random container you will get non repeated 10 numbers starts from 0 to 9(10 numbers) as random.. thank you........

-4

You can use basic Random Functions of C#

Random ran = new Random();
int randomno = ran.Next(0,100);

you can now use the value in the randomno in anything you want but keep in mind that this will generate a random number between 0 and 100 Only and you can extend that to any figure.

Mike Doe
  • 16,349
  • 11
  • 65
  • 88
-8

Try this:

private void NewNumber()
  {
     Random a = new Random(Guid.newGuid().GetHashCode());
     MyNumber = a.Next(0, 10);
  }

Some Explnations:

Guid : base on here : Represents a globally unique identifier (GUID)

Guid.newGuid() produces a unique identifier like "936DA01F-9ABD-4d9d-80C7-02AF85C822A8"

and it will be unique in all over the universe base on here

Hash code here produce a unique integer from our unique identifier

so Guid.newGuid().GetHashCode() gives us a unique number and the random class will produce real random numbers throw this

Sample: https://rextester.com/ODOXS63244

generated ten random numbers with this approach with result of:

-1541116401
7
-1936409663
3
-804754459
8
1403945863
3
1287118327
1
2112146189
1
1461188435
9
-752742620
4
-175247185
4
1666734552
7

we got two 1s next to each other, but the hash codes do not same.

Arsalan
  • 709
  • 2
  • 14
  • 27
  • 3
    Please add an explanation. – OhBeWise Feb 23 '16 at 17:52
  • @Rob Yes, this will produce a unique value, did you test that? – Arsalan Feb 02 '17 at 14:21
  • 3
    No it won't. No reason at all that two successive calls will not yield the same value. Reseeding for each sample is a classic anti pattern. – David Heffernan Jun 07 '17 at 07:11
  • A Guid is a 128 bit number while int is 32 bits. So it *will* happen that two different Guids have the same hashcode. Plus two different seed values for Random may result in the same first value, especially in a very limited range like 0-10. – Hans Kesting Jul 31 '18 at 08:50
  • @HansKesting Hi, base on this https://msdn.microsoft.com/en-us/library/system.object.gethashcode(v=vs.110).aspx `GetHashCode` returns an int32 value base on parameter object – Arsalan Jul 31 '18 at 10:16
  • Yes that is correct, but you cannot create a *unique* 32-bit value for every possible value of a 128-bit Guid - that is the point I wanted to make – Hans Kesting Jul 31 '18 at 10:30
  • David is right. See [this sample](https://rextester.com/CLXCG53095). Most times I run it, I get at least 2 duplicate values when generating 10 values. – ProgrammingLlama Mar 06 '20 at 02:00
  • @John, No he is not, if you print `Guid.NewGuid().GetHashCode()` line, it is not same when you got same random results – Arsalan Mar 06 '20 at 10:16
  • OP wants unique random values between 0 and 10. What that has to do with hash codes is beyond me. I think you've seriously misunderstood the question. – ProgrammingLlama Mar 06 '20 at 13:29
  • @John Yeap he does, but what is a random number? only have different numbers in a sequence? no, I think you did misunderstand the random sequence, all the numbers in sequence may be same or all may be different. the only thing matters is that the generated number be really random – Arsalan Mar 08 '20 at 08:08
  • Quoting OP: _"I'm trying to generate several random, **unique** numbers"_ and _"but the problem is I often get repeated numbers."_ – ProgrammingLlama Mar 08 '20 at 09:59