C# Random Number Generator with unique numbers [duplicate] - c#

This question already has answers here:
Generating random, unique values C#
(17 answers)
Closed 5 years ago.
I have managed to make an app which randomly generates a selected amount of numbers and puts all of the numbers in a rich text box (A result I would get from 1-100 with 10 numbers is '67, 55, 28, 35, 7, 69, 47, 59, 69, 82'. However, I am wanting to add an option where you can select if you want the numbers to be unique (checkbox), because the numbers box is close to the max number box, the numbers tend to duplicate.
I am not too sure how to do this though, I have tried looking online but most of the answers are a bit too complicated for me.
The code I have so far (C# Windows Form App FYI):
int minComplexNumber = Convert.ToInt32(minComplexTextBox.Text);
int maxComplexNumber = Convert.ToInt32(maxComplexTextBox.Text);
int intergersNumber = Convert.ToInt32(intergersTextBox.Text);
int numbersLeft = intergersNumber;
resultComplexTextBox.Text = "";
if (UniqueCheckBox.Checked)
{
//Need something here
}
else
{
Random comrnd = new Random();
while (numbersLeft > 1)
{
int complexResult = comrnd.Next(minComplexNumber, maxComplexNumber);
resultComplexTextBox.Text += complexResult + ", ";
numbersLeft = numbersLeft - 1;
}
if (numbersLeft == 1)
{
int complexResult = comrnd.Next(minComplexNumber, maxComplexNumber);
resultComplexTextBox.Text += complexResult;
numbersLeft = numbersLeft - 1;
}
}
Any advice on how to get unique numbers?

I like the #David suggestion, you can go with something like this:
var random = new Random();
var possibilities = Enumerable.Range(1, 100).ToList();
var result = possibilities.OrderBy(number => random.Next()).Take(10).ToArray();
Console.WriteLine(String.Join(",", result));

Use a List<int> to store the list of available numbers, then take and remove as many as you need from there:
//if checked...
Random comrndu = new Random();
var available = Enumerable.Range(minComplexNumber, (maxComplexNumber - minComplexNumber) + 1).ToList();
while (numbersLeft-- > 0)
{
int idx = comrndu.Next(0, available.Count);
int complexResult = available[idx];
available.RemoveAt(idx);
resultComplexTextBox.Text += complexResult + ", ";
}
resultComplexTextBox.Text = resultComplexTextBox.Text.Substring(0, resultComplexTextBox.Text.Length - 2);

quick and dirty solution: generate number and put it into list (if it doesn't already exist).
When done, write all numbers to texbox.
Solution that David suggested in comment is better. This is, i repeat, quick and dirty and could help you if there's not many numbers to generate
if (UniqueCheckBox.Checked)
{
Random comrnd = new Random();
List<int> generatedNumbers = new List<int>();
while (numbersLeft > 0)
{
int complexResult = comrnd.Next(minComplexNumber, maxComplexNumber);
if (!generatedNumbers.Contains(complexResult)){
generatedNumbers.Add(complexResult);
numbersLeft = numbersLeft - 1;
}
}
resultComplexTextBox.Text += string.Join(", ", generatedNumbers.ToArray());
}

Related

Get sequence of random n numbers that add up to something but only from a specific set of numbers

What I need is:
e.g:
Sum should be equal to:
120 (user input)
Number of numbers/items:
80 (user input)
Range of numbers to be used in set(from):
0 (user input)
Range of numbers to be used in set(to):
4 (user input)
Output:
1,1,3,2,1,1,0,0,1,1,2,1,0,2,3,3,1,2,0,0,0,1,3,2,3,1,0,0,2,3,2,3,2,2,1,1,0,0,2,0,1,0,1,1,3,3,1,3,1,0,0,3,2,1,0,0,2,1,2,3,0,3,1,1,3,3,2,2,1,1,3,1,3,3,3,3,3,1,2,0
These are all numbers that are between 0 and 4, their sum is 120 and are 80 in total.
What i've done is:
static void Main(string[] args)
{
bool loopOn = true;
Program p = new Program();
Console.WriteLine("____________________________________________________________________________");
Console.WriteLine("");
Console.WriteLine("Sum should be equal to:");
int sum = int.Parse(Console.ReadLine());
Console.WriteLine("Number of items:");
int items = int.Parse(Console.ReadLine());
Console.WriteLine("Range(from):");
int from = int.Parse(Console.ReadLine());
Console.WriteLine("Range(to):");
int to = int.Parse(Console.ReadLine());
while (loopOn == true)
{
List<int> number_list = p.createNumberSet(items, from, to);
if (number_list.Sum() == sum)
{
loopOn = false;
Console.WriteLine("____________________________________________________________________________");
Console.WriteLine("Start");
number_list.ForEach(Console.WriteLine);
Console.WriteLine("Stop");
Console.WriteLine("____________________________________________________________________________");
}
}
Console.WriteLine("Press any key to exit....");
Console.ReadLine();
}
public List<int> createNumberSet(int itemNumber, int range_from, int range_to)
{
List<int> number_set = new List<int>();
Random r = new Random();
for (int i = 0; itemNumber > i; i++)
{
number_set.Add(r.Next(range_from, range_to));
}
return number_set;
}
But this seems extremely in-efficent and doesn't seem to work with a lot of other examples. Does anyone have a better way of doing this?
Well, I am a bit lazy right now, so this is just an idea
Keep the first part:
bool loopOn = true;
Program p = new Program();
Console.WriteLine("____________________________________________________________________________");
Console.WriteLine("");
Console.WriteLine("Sum should be equal to:");
int sum = int.Parse(Console.ReadLine());
Console.WriteLine("Number of items:");
int items = int.Parse(Console.ReadLine());
Console.WriteLine("Range(from):");
int from = int.Parse(Console.ReadLine());
Console.WriteLine("Range(to):");
int to = int.Parse(Console.ReadLine());
Now, first of all, check is a solution exists:
if (from * items > sum) {
// There is no solution, handle accordingly
}
Let's focus on the interesting part now:
First create the list of necessary items
int[] number_set = new int[items];
for(int i = 0; i < items; i++) {
number_set[i] = from;
}
Find the difference between the wanted sum and the current sum of the list
int left_to_add = sum - from * items;
int idx = 0;
Random r = new Random();
while(left_to_add > 0) {
int toAdd = 0;
if (left_to_add < range_to - range_from) {
toAdd = r.Next(1, left_to_add);
} else {
toAdd = r.Next(1, range_to - range_from);
}
left_to_add -= toAdd;
number_set[idx] += toAdd;
idx++;
}
What's left to do is, convert the array to a list and shuffle it.
(I forgot that you actually can access list items by index, so there is no need to use an array as I did here)
At the algorithm level, this is what I would try:
Determine the number of each element, n[0], n[1], n[2], n[3] in your example (i.e. number of 0, number of 1 ...) and then generate a simple sequence by concatenating n[0] "0", n[1] "1", n[2] "2" and n[3] "3". Finally, a random sequence is obtained by performing a random permutation on this simple sequence.
The problem is therefore to determine the n[i].
The first step is to determine the average values of these n[i]. It your example, it is simple, as we can take average n_av[i]=20 for all index i.
In a more general case, we have to insure that
sum_i n_av[i]*i = sum_target (120 here) (1)
knowing that
sum_i (n[i]) = n = 80 here. (2)
In the general case, there is no necessary one unique good solution. I will try to propose an example of solution here if you provide an example of a difficult scenario.
The second step consists in selecting some random n[i] values around these average values. One possibility is to generate rounded Gaussian variables: we already know the averages, we just need to determine the variances. One possibility is to consider the variance that we will get if we were generating directly the random values, i.e. by considering the variance of the corresponding binomial variable :
var = n p(1-p). Here p[i] = n_av[i]/n
The last step consists in adjusting the values of the n[i] such that the sum of the n[i] is equal to the target. This is simply obtained by slightly increasing or decreasing some n[i] values.

List of numbers without repetition [duplicate]

This question already has answers here:
How to make this code work without repeating the numbers? [duplicate]
(3 answers)
Closed 4 years ago.
I need to print random numbers from 1 to 99 without repeating them.
The following code gives me stack overflow.
int newNumb= Random.Range(1, 99);
if(acum.Count > 0)
{
while (acum.Contains(newNumb))
{
newNumb= Random.Range(1, 99);
}
}
The typical solution to this problem is to generate the sequential ordered range from 1 to 99 and then shuffle it:
static Random _random = new Random();
public static void Shuffle<T>(IList<T> items)
{
for (int i = thisList.Count - 1; i > 0; i--)
{
int j = _random.Next(0, i);
T tmp = items[i];
items[i] = items[j];
items[j] = tmp;
}
}
var numbers = Enumerable.Range(1,99).ToList();
Shuffle(numbers);
foreach (var number in numbers)
{
Console.WriteLine(number);
}
This will generate a list of random integers, each time a different one, and add it to a list.
The shuffle method is better, but since the range is so limited, also discarding duplicate numbers could work.
Over 100 runs, measuring the elapsed time with a StopWatch(), the list generation never went over 200 Ticks.
The shuffled list 5x times (1029~1395 Ticks) on my machine.
If the list count is set to a value > then the upper range limit (100+ in this case), the the generation procedure will of course never end. There are not enough distinct random values to fill the list.
Random random = new Random();
List<int> acum = new List<int>();
while (acum.Count < 99)
{
int Number = random.Next(1, 100);
if (!acum.Contains(Number))
{
acum.Add(Number);
}
}
To verify the result, order the list and see that it's ordered from 1 to 99:
List<int> acum2 = acum.OrderBy(n => n).ToList();
the best way to do this would be to generate all the necessary numbers, and pull from that list until its empty, creating a new order; this is commonly known as shuffling.
your current code takes way too long, you need to track which numbers have been chosen, and only choose from the remaining ones. in psudocode
generate list
while list not empty
choose number from list
remove it from list
add to new list
Do this simply:
var list = new List<int>();
for (int i = 0; i < 99; i++)
{
list.Add(i);
}
var resultList = list.OrderBy(i => Guid.NewGuid());
Or as suggested by #Camilo:
var resultList = Enumerable
.Range(0, 99)
.OrderBy(i => Guid.NewGuid());
UPDATE
This solution seems to be inefficient. Please use fischer-yates shuffle (shown in #Joel's answer).
Non efficient way (see comments):
Random rand = new Random();
HashSet<int> randomHashSet = new HashSet<int>();
while (randomHashSet.Count < 99)
randomHashSet.Add(rand.Next(1, 100));
List<int> randomList = randomHashSet.ToList();
Update
Efficient way:
Random r = new Random();
var result = Enumerable.Range(1, 99).ToList();
for (int i = 0, j = r.Next(0, 99); i < 99; i++, j = r.Next(0, 99))
result[i] = result[i] + result[j] - (result[j] = result[i]); // Swap

how to write a certain array item multiple times [duplicate]

This question already has answers here:
filling a array with random numbers between 0-9 in c# [duplicate]
(3 answers)
Closed 5 years ago.
So I have myself an array like this one:
int masterNumber = randomNumber.Next(1, 7);
int childNumber;
int[] fieldArray = new int[] {masterNumber, childNumber = randomNumber.Next(1, 7)};
So now i'd like to write the master number to my console ONLY ONCE using Console.WriteLine(); and I'd like to write the random childNumber 99 times to the console, each childNumber with it's own value between 1 and 7. How would I do this?
Why you want to use fieldArray if you have "masterNumber" & "childNumber". But I think, you can do it in this way...
Random randomNumber = new Random();
int masterNumber = randomNumber.Next(1, 7);
int childNumber;
int[] fieldArray = new int[] { masterNumber, childNumber = randomNumber.Next(1, 7) };
Console.Write("Master Number " + masterNumber);
int i = 0;
while (i < 99)
{
Console.Write("Child Number " + childNumber);
Console.WriteLine(i);
i++;
}

How to generate unique random integers that do not duplicate

I have created a short program that creates 3 random integers between 1-9 and stores them in an array, however, I would not like any of them to repeat, that is, I would like each to be unique. Is there an easier way to generate 3 unique integers other than having to iterate through the array and comparing each integer to each other? That just seems so tedious if I were to increase my array to beyond 3 integers.
This is my code to generate 3 random numbers. I saw other code in Java, but I thought maybe C# has a easier and more efficient way to do it.
var number = new Numbers[3];
Random r = new Random();
for ( int i = 0; i < number.Length; i++)
{
number[i] = new Numbers(r.Next(1,9));
}
Console.WriteLine("The Three Random Numbers Are:");
foreach(Numbers num in number)
{
Console.WriteLine("{0}", num.Number);
}
I would do something like this:
var range = Enumerable.Range(1, 8);
var rnd = new Random();
var listInts = range.OrderBy(i => rnd.Next()).Take(3).ToList();
You could make an array or a list of the numbers that might be generated, e.g. 0, 1, 2, 3. Then you generate a number from 0 to this list's length, e.g. 2 and pick list[2] so for the next time you only have 0, 1, 3 in your list.
It takes longer to generate it, especially for long lists but it doesn't repeat numbers.
using System;
using System.Collections.Generic;
public class Test
{
static Random random = new Random();
public static List<int> GenerateRandom(int count)
{
// generate count random values.
HashSet<int> candidates = new HashSet<int>();
// top will overflow to Int32.MinValue at the end of the loop
for (Int32 top = Int32.MaxValue - count + 1; top > 0; top++)
{
// May strike a duplicate.
if (!candidates.Add(random.Next(top))) {
candidates.Add(top);
}
}
// load them in to a list.
List<int> result = new List<int>();
result.AddRange(candidates);
// shuffle the results:
int i = result.Count;
while (i > 1)
{
i--;
int k = random.Next(i + 1);
int value = result[k];
result[k] = result[i];
result[i] = value;
}
return result;
}
public static void Main()
{
List<int> vals = GenerateRandom(10);
Console.WriteLine("Result: " + vals.Count);
vals.ForEach(Console.WriteLine);
}
}
Grate explanation and answers from here
Source http://ideone.com/Zjpzdh

Best Way to generate a list of three unique numbers between 0 and 9

It is all in the title basically. It is simple, but I don't know why my While loop is failing sometimes. Every now and then I would get a list that is of length 2 instead of 3.
Here is my C# code:
public List<int> generateRequiredSecretCode()
{
List<int> placeHolder = new List<int>();
Random random = new Random();
int randomNo = random.Next(0, 10);
while (!placeHolder.Contains(randomNo) && placeHolder.Count != 3)
{
placeHolder.Add(randomNo);
randomNo = random.Next(0, 10);
}
return placeHolder;
}
Summary of my aim: I want a List of integers that is of length 3 and where each number in the list is between 0 and 9 and is unique
You can have a neat LINQ two-liner using
var random = new Random();
return Enumerable.Range(0,10).OrderBy(i => random.NextDouble()).Take(3).ToList();
!placeHolder.Contains(randomNo) is your problem here because the while ends if the list contains the randomNo.
Check that !placeHolder.Contains(randomNo) in an inner if like this:
while (placeHolder.Count != 3)
{
if( !placeHolder.Contains(randomNo) )
placeHolder.Add(randomNo);
randomNo = random.Next(0, 10);
}
A little late to the party, but set arithmetic seems quite elegant so couldn't resist:
private static Random RNG = new Random();
...
public static List<int> RandomNumbers() {
var numbers = new HashSet<int> { RNG.Next(0, 9), RNG.Next(0, 9), RNG.Next(0, 9) };
while (numbers.Count < 3)
{
numbers.Add(RNG.Next(0, 9));
}
return numbers.ToList();
}
It sometimes fails because, in the rare cases when Rand.Next does return an identical number to thise already in the list, !placeHolder.Contains(randomNo) will return false; false && anything = false, so the loop ends. If you'd run it long enough, you'd eventually get a list of length 1 ;)
Possible replacement:
List<int> placeHolder = new List<int>();
Random random = new Random();
int randomNo;
do {
randomNo = random.Next(0, 10);
if (!placeHolder.Contains(randomNo) && placeHolder.Count != 3)
{
placeHolder.Add(randomNo);
randomNo = random.Next(0, 10);
}
} while (placeHolder.Count < 3);
return placeHolder;
[edit]: This thread moved fast... and Animal's solution is much better than mine :(

Categories