I'm trying to create a method that produces 10 unique random numbers, but the numbers are not unique, I get several duplicates! How can I improve the code to work as I want?
int[] randomNumbers = new int[10];
Random random = new Random();
int index = 0;
do
{
int randomNum = random.Next(0, 10);
if (index == 0)
{
randomNumbers[0] = randomNum;
index++;
}
else
{
for (int i = 0; i < randomNumbers.Length; i++)
{
if (randomNumbers[i] == randomNum)
break;
else
{
randomNumbers[index] = randomNum;
index++;
break;
}
}
}
}
while (index <= 9);
foreach (int num in randomNumbers)
System.Console.Write(num + " ");
EDIT 2: I have corrected all errors now, but this code isn't working becuase the numbers are not unique! I have updated my code above to the latest version. I preciate some help to solve this! Thanks!
The simplest way to have this is to have an array of the numbers you want (i.e. 1-10) and use a random shuffling algorithm.
The Fisher-Yates shuffle is the easiest way to do this.
EDIT:
Here's a link to a C# implementation: http://www.dotnetperls.com/fisher-yates-shuffle
random.next(0, 10) returns a random number between 0 and 10. It can happen, that it returns the same number multiple times in a row and it is not guaranteed, that you get every number between 0 and 10 exactly one time, when you call it 10 times.
What you want is a list of numbers, every number is unique, between 0 and 9 (or 1 and 10). A possible solution to this would be something like this:
//Create the list of numbers you want
var list = new List<int>();
for(var x = 0; x < 10; x++)
{
list.Add(x);
}
var random = new Random();
//Prepare randomized list
var randomizedList = new List<int>();
while(list.Length > 0)
{
//Pick random index in the range of the ordered list
var index = random.Next(0, list.Length);
//Put the number from the random index in the randomized list
randomizedList.Add(list[index]);
//Remove the number from the original list
list.RemoveAt(index);
}
Explained in words, this is what you do:
Create the list with all the numbers, that your final list should contain
Create a second empty list.
Enter a loop. The loop continues, as long as the ordered list still has numbers in it.
Pick a random index between 0 and list.Length
Put this random number in the randomized list
Remove the item at the index position from the ordered list.
Like this you create the set of numbers you want to have in your randomized list and then always pick a random entry from the ordered list. With this technique, you can also achieve, that certain values occur multiple time in the list.
I'm not sure if my code compiles against c#, as I currently do not have visual studio running here, but I think the code should be mostly correct.
Something like this?
const int maxNumbers = 10;
List<int> numbers = new List<int>(maxNumbers);
for (int i = 0; i < maxNumbers; i++)
{
numbers.Add(i);
}
Random r = new Random();
while (numbers.Count > 0)
{
int index = r.Next(numbers.Count);
Console.Write("{0} ", numbers[index]);
numbers.RemoveAt(index);
}
Console.WriteLine();
Edit: For any random numbers:
const int maxNumbers = 10;
const int biggestNumbers = 10000;
List<int> numbers = new List<int>(maxNumbers);
Random r = new Random();
while (numbers.Count < maxNumbers)
{
int index = r.Next(biggestNumbers);
if (numbers.IndexOf(index) < 0)
{
numbers.Add(index);
Console.Write("{0} ", index);
}
}
Console.WriteLine();
Related
I'm trying to create an array list using random numbers. But sometimes I get a zero in results. I do not understand why.
I'm grateful if anyone can explain.
int[] number = new int[6];
Random rnd = new Random();
for (int i = 0; i < number.Length; i++)
{
int random = rnd.Next(1, 26);
if (!number.Contains(random))
{
number[i] = random;
}
}
foreach (int nr in number)
{
Console.Write("|" + nr + "|");
}
//results
|6||12||0||22||25||11|
int[] number = new int[6];
Here number array is created with default int value i.e 0
The issue with your code is in some cases this value is not getting updated due to the check
if (!number.Contains(random))
You can change your code to include a loop to guarantee your random number doesn't lie in the array.
int[] number = new int[6];
Random rnd = new Random();
for (int i = 0; i < number.Length; i++)
{
int random = rnd.Next(1, 26);
while (number.Contains(random))
{
random = rnd.Next(1, 26);
}
number[i] = random;
}
foreach (int nr in number)
{
Console.Write("|" + nr + "|");
}
Please note that current approach is quite performance hungry as for every new random value we are iterating through entire array everytime to check if it exists. You can reduce the performance by using as HashSet<int> if possible
When you declare the array in following statement it initializes with 6 integers as 0
int[] number = new int[6];
While the random number generated for each array element, check for duplication in following statement may have resulted false
if (!number.Contains(random))
That's why it was never updated to new assigned number.
You can add else condition to it and regenerate random number
Just use your debugger to step through your code and inspect your variables to see what's happening. Apart from all the suggestions here, which are bad because they can loop way too many times or even forever, you appear to want to get 6 random, unique numbers between 1 and 26.
The de facto way to do that, is to generate a list with those numbers (Enumerable.Range()), shuffle them (Fisher-Yates) and Take() the first six.
Use while loop.
int[] number = new int[6];
Random rnd = new Random();
int i = 0;
while (i < number.Length)
{
int random = rnd.Next(1, 26);
if (!number.Contains(random))
{
number[i] = random;
i++;
}
}
foreach (int nr in number)
{
Console.Write("|" + nr + "|");
}
i would like to have 6 non equal- random numbers for my listbox.
private void Button1_Click(object sender, EventArgs e)
{
listBox1.Items.Clear();
int[] numbers = new int[5];
Random rnd = new Random();
for (int i = 0; i < numbers.Length; i++)
{
numbers[i] = rnd.Next(1, 50);
if (Array.IndexOf(numbers, i) == -1)
{
listBox1.Items.Add(numbers[i]);
}
}
}
i can't figure out what mistake i have made about this code.
The problem is your for loop that continue to increment its indexer even if you haven't found an unique number and your array is filled immediately without checking if the number is already present. In this way you have a list that sometimes has less items (unique) than the numbers array (with duplicates).
The best approach in this case is to use, instead of a for loop, a while loop with an explict indexer that you increment when you find an unique number until it reaches the last position in the array
int[] numbers = new int[6];
Random rnd = new Random();
// First position in the array to fill
int index = 0;
while(index < number.Length)
{
int number = rnd.Next(0, 50);
if (!numbers.Contains(number))
{
// OK we don't have it add to the array and to the list
numbers[index] = number;
listBox1.Items.Add(numbers[index]);
// Point to the next position to fill
index++;
}
}
Now there is also a different and very elegant way to do the same thing with a bit of linq:
var indexes = Enumerable.Range(1, 50).ToList();
Random rnd = new Random();
int[] numbers = indexes.OrderBy(x => rnd.Next()).Take(6).ToArray();
But then you need a loop to add the numbers array to the list and probably, as pointed out in the comment below, it could be not very efficient
This question already has answers here:
Generating random, unique values C#
(17 answers)
Closed 4 years ago.
I want to generate random numbers and put them in an array, but they should only appear once in this array. It's like a mini lotto game.
This is the code I have right now:
int[] arrA = new int[10];
Random random = new Random();
for (int i = 0; i <= arrA.Length -1; i++)
{
arrA[i] = random.Next(1, 15);
Console.WriteLine(arrA[i]);
}
Console.ReadKey();
Random numbers are generated and put in this Array. I only need to know how it's possible to program that they only appeare once.
Use HashSet<T>. A set is a collection that contains no duplicate elements, and whose elements are in no particular order.
Something like this:
using System;
using System.Collections.Generic;
HashSet<int> numbers = new HashSet<int>();
for (int i = 0; i < 10; i++)
{
// Start with a random number
//
int value = random.Next(1,15);
// Check whether you already have that number
// Keep trying until you get a unique
//
while (numbers.Contains(value)) {
value = random.Next(1,15);
}
// Add the unique number to the set
numbers.Add(value);
}
foreach (int i in numbers)
{
Console.Write(" {0}", i);
}
int[] arrA = new int[10];
Random random = new Random();
for (int i = 0; i <= arrA.Length - 1; i++)
{
var number = random.Next(1, 15);
while (arrA.Any(n => n == number))
{
number = random.Next(1, 15);
}
arrA[i] = number;
Console.WriteLine(arrA[i]);
}
Console.ReadKey();
Everytime you generate a new number, instead of putting it in the array right away check the array from the start to the current position (using a for-loop). If it's not there insert the generated number, otherwise generate a new one and repeat the process.
So basically, I need to have the numbers 1-10 randomly ordered upon startup into an array, and on when the form loads, it should load the first number. Each time the user clicks a button, it will load the info associated with the next number.
This is my code, but for some reason it generates a number that is not an integer a lot.
Random rng = new Random(10);
int[] QuestionOrder = new int[10];
for (int i = 0; i < QuestionOrder.Length; i++)
{
int temp = rng.Next(1,10);
while(!(QuestionOrder.Contains(temp)))
{
QuestionOrder[i] = temp;
}
}
each time it generates a number 1 - 10 and checks if it has already been stored in the array, if not, stores it and runs again.
For some reason, its generating numbers that are not integers 1 - 10, and i cant figure out why.
I think you need to overwrite the value of temp inside the while loop and change your range to 1-11 instead of 1-10 and use an if statement before the loop:
int temp = rng.Next(1, 11);
if(!QuestionOrder.Contains(temp)) QuestionOrder[i] = temp;
else
{
while(QuestionOrder.Contains(temp))
{
temp = rng.Next(1, 11);
QuestionOrder[i] = temp;
}
}
You could generate ten random numbers first, then iterate through them.
var rnd = new Random(Environment.TickCount);
var arr = Enumerable.Range(1,10).OrderBy(x => rnd.Next()).ToArray();
this does work nicely
static void Main(string[] args)
{
Random random = new Random();
var range = Enumerable.Range(1, 10).ToList();
int[] rnd = new int[10];
int j = 0;
do
{
int i = random.Next(0, range.Count);
rnd[j++] = range[i];
range.RemoveAt(i);
}
while(range.Count > 1);
rnd[j] = range[0];
}
less cpu intensive than using a contains in a loop
First, your seed is always the same. Either omit it, or use maybe the current date.
Second, you're generating numbers between 1 and 9, so you will never get 10, and one of your array cell will stay empty.
Change temp = rng.Next(1, 11);
As others have pointed out, you're not regenerating a new number when the generated number is already present in the list, leaving you with various default values of 0 in your result.
There are however better ways to 'shuffle' a list of numbers, see Randomize a List in C#:
public static IList<T> Shuffle<T>(this IList<T> list)
{
Random rng = new Random();
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;
}
return list;
}
Which you can call like this:
IList<int> QuestionOrder = Enumerable.Range(1, 10)
.ToList()
.Shuffle();
The best algorithm to shuffle an array is Knuth-Fisher-Yates, that can be implemented as follows:
public static class Extensions
{
public static void Shuffle<T>(this IList<T> list, Random rand)
{
for (int i = list.Count - 1; i > 0; i--)
{
int n = rand.Next(i + 1);
int tmp = list[i];
list[i] = list[n];
list[n] = tmp;
}
}
}
Usage:
// to get different random sequences every time, just remove the seed (1234)
Random rng = new Random(1234);
// create an array containing 1,2...9,10
int[] questionOrder = Enumerable.Range(1,10).ToArray();
// shuffle the array
questionOrder.Shuffle(rand);
Side note:
To know why Knuth-Fisher-Yates algorithm is better than a naive implementation, read this interesting post by Jeff
System.Random generator = new Random(DateTime.Now.Millisecond);
int[] lotteryNumber = new int[7];
Console.WriteLine("Your lottery numbers: ");
for (int i = 0; i<7; i++)
{
lotteryNumber[i] = generator.Next(1, 37);
Console.Write("{0} ",lotteryNumber[i]);
}
Console.ReadLine();
I need to make a program that prints 7 lottery numbers, but without duplicates. The code above prints 7 random numbers in the range of (1-37), but duplicates appaer. I need a way to prevent duplicate numbers from appearing.
The simplest approach IMO would be to generate a sequence of all the possible numbers (i.e. 1-37), shuffle the collection, then take the first seven results.
Searching on Stack Overflow for "Fisher-Yates shuffle C#" will find lots of examples.
In fact, you could modify the Fisher-Yates shuffle to yield results as you took them, so you could write a method such as:
var numbers = Enumerable.Range(1, 37).Shuffle().Take(7).ToList();
You could take a dictionary but make sure that you prevent duplicate key insertion. Keys of dictionary would serve as the unique numbers you need
You could toss them into a HashSet<int>. If you Add and it returns false, generate a new number.
If you're trying to pick numbers from a range without repetitions, you need to create an array of all the possible numbers and then "shuffle" a random selection out:
int[] allPossibleNumbers = Enumerable.Range(1, 37).ToArray();
int[] lotteryNumber = new int[7];
for (int i = 0; i < 7; i++)
{
int index = r.Next(i, 37);
lotteryNumber[i] = allPossibleNumbers[index];
allPossibleNumbers[index] = allPossibleNumbers[i];
// This step not necessary, but allows you to reuse allPossibleNumbers
// rather than generating a fresh one every time.
// allPossibleNumbers[i] = lotteryNumber[i];
}
Generate a list with your 37 items.
Then in your for, select one and delete the selected
Maybe this could help, if you get the existing number just try to find new one that isn't in the array:
static void Main(string[] args)
{
System.Random generator = new Random(DateTime.Now.Millisecond); int[] lotteryNumber = new int[7];
Console.WriteLine("Your lottery numbers: ");
for (int i = 0; i < 7; i++)
{
int lNumber = 0;
do
{
lNumber = generator.Next(1, 37);
}
while (lotteryNumber.Contains(lNumber));
lotteryNumber[i] = lNumber;
Console.Write("{0} ", lotteryNumber[i]);
}
Console.ReadLine();
}
HashSet<int> set = new HashSet<int>();
System.Random generator = new Random(DateTime.Now.Millisecond);
while(set.Count < 7){
set.Add(generator.Next(1,37);
}
That should work, since a HashSet will automatically ignore duplicates. Just loop until the set reaches the number of units you need. Only potential problem is it has the POTENTIAL (unlikely) to loop for a long time, but it should eventually respond.
so I took your original code...found some logic errors and added the fix you were looking for to prevent random number duplicates.
Enjoy!
System.Random generator = new Random(DateTime.Now.Millisecond);
int[] lotteryNumber = new int[7];
int lowerBounds = 1;
int upperBounds = 8;
int maxNumberLotteryValues = 7;
if ( ( upperBounds - lowerBounds ) < (maxNumberLotteryValues))
{
Console.Write("Warning: Adjust your upper and lower bounds...there are not enough values to create a unique set of Lottery numbers! ");
}
else
{
Console.WriteLine("Your lottery numbers: ");
for (int i = 0; i < maxNumberLotteryValues; i++)
{
int nextNumber = generator.Next(lowerBounds, upperBounds);
int count = lowerBounds; //Prevent infinite loop
while ((lotteryNumber.Contains(nextNumber))
&& (count <= upperBounds))
{
nextNumber = generator.Next(lowerBounds, upperBounds);
count++; //Prevent infinite loop
}
lotteryNumber[i] = nextNumber;
Console.Write("{0} ", lotteryNumber[i]);
}
}
Console.ReadLine();
const int nBalls = 37;
const int nPicks = 6;
int[] balls = new int[nPicks];
Random rnd = new Random(DateTime.Now.Millisecond);
int remainingBalls=nBalls;
int remainingPicks=nPicks;
for (int i = 1; i <= nBalls; i++)
{
if (rnd.Next(1, remainingBalls+1) <= remainingPicks)
balls[--remainingPicks]=i;
remainingBalls--;
}
Console.WriteLine(string.Join(",",balls));
Will outperform Shuffle and HashSet methods as nPicks/nBalls gets larger.