可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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]
.