How to create an array of non repeating random num

2019-01-28 06:29发布

问题:

I have a lottery application in C# which takes in the number of numbers to draw and also the maximum number to draw.I have coded up to creating an array holding the required random numbers but I need them to be unique and am having trouble doing it.I would be very grateful if someone could give me some advice on this,Thanks

Here is my code so far:

class Lottery

{

  static int[] numberHolder; //array to be filled with numbers up to an 
                             //amount entered by the user eg 42 Max

  static int[] drawHolder;   //array to hold the each random number 
                             //drawn from the pool of numbers eg 7 numbers

    public Lottery() //Lottery class Constructor
    {

    }


    //method which takes in a number limit and amount of numbers to be drawn
    public String drawNumbers(int numLimit, int numAmount) 
    {

        Random RandomNumber = new Random();

        for (int i = 0; i < numLimit ; i++) //loop to fill up numberHolder array
                                            // with predefined limit of numbers

        {
            numberHolder[i] = i++;
        }

        for (int i = 0; i < numAmount; i++)
        {


            // code to pick unique random numbers no greater than numAmount 
            // and add them to the drawHolder[] array
            drawHolder[i] = RandomNumber.Next(1, numLimit);

        }





        //return the drawHolder array to String
        return null;
    }





}

回答1:

In my opinion, you should change your approach.

Instead of thinking "I'll generate random indexes to pick my numbers", where you have to be sure you don't get any duplicates, I would simply shuffle the array and take the X first you need. That way, you don't need to worry about indexes or duplicates.

So your second for loop would be changed to

drawHolder = numberHolder.OrderBy(x => new Guid()).Take(numAmount);

(Please note I've used new Guid() so you can remove your RandomNumber declaration line. As discussed before, a GUID is a unique value and not meant for to be used as a random gen. You could also use x => RandomNumber.Next(), but if you really need a strong and reliable shuffe, read about Fisher-Yates)

You can also replace your numberHolder array with a simple Enumerable.Range

So your whole code would become (And please note I've changed your method name to use C# conventions, method names should be in PascalCase)

public string DrawNumbers(int numLimit, int numAmount) 
{
    drawHolder = Enumerable.Range(0, numLimit).OrderBy(x => new Guid()).Take(numAmount);

    return string.Join(", ", drawHolder);
} 


回答2:

There are a few options, you could use the Contains method.

numberHolder.Contains(value)

The Any() method.

numberHolder.Any(x=>x == value);


回答3:

Sounds like shuffling your array is better than generating random numbers. You could do it like this:

int[] ShuffleArray(int[] array)
{
  Random r = new Random();
  for (int i = array.Length; i > 0; i--)
  {
    int j = r.Next(i);
    int k = array[j];
    array[j] = array[i - 1];
    array[i - 1]  = k;
  }
  return array;
}

Credit goes to @RohitArora.



回答4:

This is easily achieved using a shuffling algorithm on an array of non-repeating integers:

public int[] GenerateNonRepeatingNumbers(int seed, int min, int range)
    {
        // Make sure range is an appropriate value
        if (range <= 0)
        {
            throw new ArgumentException("Range must be greater than zero.");
        }

        // Make an array to hold our numbers
        int[] numbers = new int[range];

        // Seed the RNG.
        Random rng = new Random(seed);

        // Fill the array with all numbers from min to min + range
        for (int i = 0; i < range; numbers[i] = min + i++) { }

        int
            a = 0, // Swap index
            t = 0; // Temporary value storage

        // Scramble the values
        for (int i = 0; i < range; i++)
        {
            // Get a random index that isn't i
            while ((a = rng.Next(range)) == i) { };
            // Store the old value at i
            t = numbers[i]; 
            // Change the old value to the value at the random index
            numbers[i] = numbers[a]; 
            // Set value at random index to our old value from numbers[i]
            numbers[a] = t; 
        }

        return numbers;
    }


回答5:

I would keep it simple:

private Random _rnd = new Random();
public String drawNumbers(int numLimit, int numAmount) 
{
    return String.Join("," ,
        Enumerable
            .Range(1, numLimit)
            .OrderBy(x => _rnd.Next())
            .Take(numAmount));
}

Keep the declaration of the Random instance outside of the method to prevent returning identical sequences if you call the method in quick succession.



回答6:

I'd use Enumerable.Distinct like this:

public String drawNumbers(int numLimit, int numAmount) {
    var numbers = Infinite(() => random.Next(numLimit)).Distinct().Take(numAmount);
    return string.Join(" ", numbers);
}
private Random random = new Random();
private static IEnumerable<T> Infinite<T>(Func<T> generator) {
    while (true) yield return generator();
}

In most lotteries where numLimit is large and numAmount is 10 or less, this method is more efficient than creating and shuffling an array of length numLimit.

If real money is involved, you should use a better randomness generator than Random or Guid. Here's a full Lottery class that uses a cryptographic random number generator (based on Microsoft's RNGCryptoServiceProvider).

class Lottery
{
    // Use Random if this is in a school project or toy application
    // private Random randomGenerator = new Random();

    // Use CryptoRandom if you're dealing with real money or prizes
    private CryptoRandom randomGenerator = new CryptoRandom();

    //method which takes in a number limit and amount of numbers to be drawn
    public String drawNumbers(int numLimit, int numAmount) 
    {
        return drawNumbers(1, numLimit, numAmount);
    }

    // takes the minimum and maximum lottery numbers and how many numbers to draw
    public String drawNumbers(int minNumber, int maxNumber, int count)
    {
        if (maxNumber < minNumber) return string.Empty;
        int maxCount = (int)Math.Min(int.MaxValue, 1L + maxNumber - minNumber);
        if (count > maxCount) count = maxCount;
        if (count < 1) return string.Empty;
        var numbers = Infinite(() => randomGenerator.Next(minNumber, maxNumber)).Distinct().Take(count);
        return string.Join(" ", numbers);
    }

    private static IEnumerable<T> Infinite<T>(Func<T> generator)
    {
        while (true)
            yield return generator();
    }

    private class CryptoRandom
    {
        private readonly RNGCryptoServiceProvider _rng = new RNGCryptoServiceProvider();
        private readonly byte[] _uint32Buffer = new byte[4];
        public int Next(int minValue, int maxValue)
        {
            if (minValue == maxValue) return minValue;
            long diff = (long)maxValue - minValue;
            const long Max = 1L + uint.MaxValue;
            long randLimit = Max - (Max % diff);
            uint rand;
            do {
                _rng.GetBytes(_uint32Buffer);
                rand = BitConverter.ToUInt32(_uint32Buffer, 0);
            } while (rand >= randLimit);
            return (int)(minValue + (rand % diff));
        }
    }
}


回答7:

It's too late but I use a Method named M_Randomizer created by me.

using System;
class Randomizer
{
    public int[] M_Randomizer(int x)
    {
        bool b = false;
        if (x < -1)
        {
            b = true;
            x = -1 * x;
        }
        if(x == -1)
            x = 0;
        if (x < 2)
            return new int[x];

        int[] site;
        int k = new Random(Guid.NewGuid().GetHashCode()).Next() % 2;
        if (x == 2)
        {
            site = new int[2];
            site[0] = k;
            site[1] = 1 - site[0];
            return site;
        }
        else if (x == 3)
        {
            site = new int[3];
            site[0] = new Random(Guid.NewGuid().GetHashCode()).Next(0, 3);
            site[1] = (site[0] + k + 1) % 3;
            site[2] = 3 - (site[0] + site[1]);
            return site;
        }
        site = new int[x];
        int a = 0, m = 0, n = 0, tmp = 0;
        int[] p = M_Randomizer(3);
        int[] q;

        if (x % 3 == 0)
            q = M_Randomizer(x / 3);
        else
            q = M_Randomizer((x / 3) + 1);
        if (k == 0)
        {
            for (m = 0; m < q.Length; m++)
            {
                for (n = 0; n < p.Length && a < x; n++)
                {
                    tmp = (q[m] * 3) + p[n];
                    if (tmp < x)
                    {
                        site[a] = tmp;
                        a++;
                    }
                }
            }
        }
        else
        {
            while (n < p.Length)
            {
                while (a < x)
                {
                    tmp = (q[m] * 3) + p[n];
                    if (tmp < x)
                    {
                        site[a] = tmp;
                        a++;
                    }
                    m = m + k;
                    if (m >= q.Length)
                        break;
                }
                m = m % q.Length;
                n++;
            }
        }

        a = (new Random(Guid.NewGuid().GetHashCode()).Next() % 2) + 1;
        k = new Random(Guid.NewGuid().GetHashCode()).Next() % 10;
        if (k > 5)
            for (int i = a; i < k; i++)
                while (a < site.Length)
                {
                    if (k % (a + 1) == 0)
                    {
                        tmp = site[a - 1];
                        site[a - 1] = site[a];
                        site[a] = tmp;
                    }
                    a = a + 2;
                }

        k = new Random(Guid.NewGuid().GetHashCode()).Next() % 10;
        if (k > 5)
        {
            n = x / 2;
            k = 0;
            if (x % 2 != 0)
                k = (new Random(Guid.NewGuid().GetHashCode()).Next() % 2);

            p = new int[n + k];
            m = (x - n) - k;
            for (a = 0; m < x; a++, m++)
                p[a] = site[m];

            m = n + k;
            for (a = (x - m) - 1; a >= 0; a--, m++)
                site[m] = site[a];

            for (a = 0; a < p.Length; a++)
                site[a] = p[a];
        }

        int[] site2;
        int[] site3 = new int[x];
        if (b)
            return site;
        else
            site2 = M_Randomizer(-1 * x);

        for (a = 0; a < site.Length; a++)
            site3[site2[a]] = site[a];

        return site3;
    }

    public int[] M_Randomizer(int x, int start)
    {
        int[] dm = M_Randomizer(x);

        for(int a = 0; a < x; a++)
            dm[a] = dm[a] + start;

        return dm;
    }
}


回答8:

Lots of ways to do this. You could sort and then compare if array[i] == array[i+1].