C# - Returning a Unique Random Number [duplicate] - c#

This question already has answers here:
Random number generator only generating one random number
(15 answers)
Closed 10 years ago.
I have searched this for hours and I'm not getting it. I don't seem to know how to return values using Fisher-Yates and many ways listed. I'm dying here.
I can get a RandomNumber, but this is reused over and over. I need it to be unique everytime when returned (or so I tend to think is possible).
I need help understanding what I should do, why each part does, and stuff for dummies. This is what works:
private int RandomNumber(int min, int max)
{
Random random = new Random();
return random.Next(min, max);
}
And this is what I'm putting it into and it working (but not unique random numbers are used)... I only included what I felt needed to be looked at and where it is positioned:
private void ComputersTurn()
{
Control.ControlCollection coll = this.Controls;
foreach (Control c in coll)
{
if (...)
{
if (...)
{
if (...)
{
if ((c.Name == "btn" + Convert.ToString(RandomNumber(1,9)) && (c.Enabled != false) ))
{
if (...)
{
//code here
}
}
}
}
}
}
}
Again, RandomNumber works...but it's not unique. I wish to learn how to return a unique number (if possible).

Are you simply trying to return all the integers from min to max with their order permuted? This is the only way it makes sense to me to want a sequence of random integers in a given range such that each random is guaranteed unique...
Assuming I'm correct, you should be able to easily find code for random permutation of an array.

The only way to generate unique numbers by Random is to define it in your class like this:
public static class RandomGenerator
{
private static readonly Random _random = new Random();
public static int GenRand(int x, int y)
{
return _random.Next(x, y);
}
}
In your case try to use this code this way:
private void ComputersTurn()
{
Control.ControlCollection coll = this.Controls;
foreach (Control c in coll)
{
if (...)
{
if (...)
{
if (...)
{
if ((c.Name == "btn" + Convert.ToString(RandomGenerator.GenRand(1, 9)) && (c.Enabled != false) ))
{
if (...)
{
// code here
}
}
}
}
}
}
}

Instantiate the Random class only once.
private Random random = new Random();
private int RandomNumber(int min, int max)
{
var result = this.random.Next(min, max);
return result;
}

try to put declaration of your instance of Random class outside the function so that the you can get every time different number if you want to make sure that random numbers are not going to be duplicated you can use and List to store every generated number
class Classname
{
private Random random = new Random();
//your code
}

Related

C# Returning A Random String From A List

I wrote out a list filled with answers for a question. I want to use my GetAnswer() method to return the current answer as a string.
The issue that I'm having is that I can't get the random answer that I selected to print out.
namespace Magic8Ball_Logic
{
public class Magic8Ball
{
private List<string> _answers;
private int _currentIndex;
public Magic8Ball()
{
_answers = new List<string>();
_answers.Add("It is certain.");
_answers.Add("It is decidedly so.");
_answers.Add("Without a doubt.");
}
public Magic8Ball(List<string> answers)
{
//I won't use the default. Use the ones passed in.
_answers = answers;
}
public void Shake()
{
//picking the index of the answer to show the user
Random r = new Random();
int index = r.Next(_answers.Count);
string randomString = _answers[index];
}
public string GetAnswer()
{
//using the index picked by shake to return the answer
return randomString;
}
Tell me,if you don't understand something.
public Form1()
{
InitializeComponent();
_answers.Add("It is certain.");
_answers.Add("It is decidedly so.");
_answers.Add("Without a doubt.");
}
List<string> _answers = new List<string>();
private void BtnRandom_Click(object sender, EventArgs e)
{
MessageBox.Show(GetAnswer());
}
string GetAnswer()
{
Random rnd = new Random();
int i = 0;
int _rnd = rnd.Next(_answers.Count);
foreach (string answer in _answers)
{
if (i == _rnd)
{
return answer;
}
i++;
}
return "";
}
}
Using your original code:
public void Shake()
{
//picking the index of the answer to show the user
Random r = new Random();
_currentIndex = r.Next(_answers.Count);
}
public string GetAnswer()
{
//using the index picked by shake to return the answer
return _answers[_currentIndex];
}
You might need to make your Random static. There are other threads that you can refer to for this.
you can do this by using the Random class that comes with the system.
Make sure you have using System; at the top of your code. If you want to avoid adding this line at the top of your code, you can just add System. before every time you see the word "Random".
Random rnd = new Random();
return _asnswers[rnd.Next(0, _answers.Count);
By the way, try to avoid using variables with '_' at the beginning because it is used for variables from another type, such as:
using _StartWith = System.File;
Name the variables "answers" (preferred) or "Answers".
The code will work 100% but this is just for when other people look at your code they will know what variable is what type.
Also, I assume you want to shuffle the answers so I will help you with it too:
Random rnd = new Random();
for (int i = _answers.Count - 1; i >= 0; i++)
{
_answers.Add( // Add the random removed answer
_answers.Remove( // Removes the random answer
rnd.Next(0, i)));// Randomizes a random answer in a range of 0 to the number of answers that didn't get picked before.
}
Good luck!
Tell me if I helped :)

Generate 10 unique integers in C# for Unity

I want to generate 10 'random' numbers, but they have to be unique. I have tried something, but is there someone who can help me out with something better?
My code:
List<int> ran = new List<int>();
Random rnd = new Random();
public static int randomValue;
int tempRandom;
public int randomNum()
{
if(ran.Count == 0)
{
ran.Add(0);
ran.Add(1);
ran.Add(2);
ran.Add(3);
ran.Add(4);
ran.Add(5);
ran.Add(6);
ran.Add(7);
}
tempRandom = rnd.Next(0, ran.Count);
randomValue = ran[randomValue];
ran.RemoveAt(tempRandom);
return randomValue;
}
Is this what you're trying to say? If not, please specify how you mean further. This code should give you a number between 1-10 that hasn't been already used. This code will only work 10 times.
Random rnd = new Random();
List<int> usedNumbers = new List<int>();
public int RandomNum(){
int number;
do {
number = rnd.Next(1, 10);
} while(usedNumbers.IndexOf(number) == -1);
usedNumbers.Add(number);
return number;
}
Straight answer to your question (not regarding if you actually want what you are asking for):
Random.Range( int.MinValue, int.MaxValue );
This simply produces a random int in the range of all integers. For 10 numbers, the probability of duplicates is so little that every number will be unique.

C# return does not exit the function

So, I'm programming a function that asks a random question from the dictionary until all the questions have been asked and I ran into an issue. When the list of randomly generated numbers already contains the new generated number, the program calls the Ask function, as it should. But then, when it gets to the final return line, it calls the function Ask again, instead of returning the list of numbers.
static List<int> Ask(Dictionary<string,string> Questions, List<int> random, int count)
{
bool contains = false;
var rando = new Random();
int num = Convert.ToInt32(rando.Next(0, count));
if (random.Count >= count)
{
Console.WriteLine("No more questions! Quitting");
System.Environment.Exit(1);
}
foreach (int number in random)
{
if (number == num)
{
contains = true;
}
}
if (contains == true)
{
Ask(Questions, random, count);
}
random.Add(num);
var randomEntry = Questions.ElementAt(num);
String randomKey = randomEntry.Key;
String randomValue = randomEntry.Value;
Console.WriteLine(randomKey);
if (Console.ReadLine() == randomValue)
{
Console.WriteLine("Correct!");
}
else
{
Console.WriteLine("Wrong!");
}
return random;
}
I think the problem is in your recursive call to the function Ask. When you exit from the recursive call you are in the previous call still inside the Ask method and there is no way to avoid the remainder of the code following the Ask call.
I don't think yor really need a recursive method for this. Just check if the random number generated is already in your list of asked questions and repeat the random generation until you find a question not asked before....
Here the refactored code with variable names less confusing
List<int> Ask(Dictionary<string,string> Questions, int count)
{
List<int> askedList = new List<int>();
var rnd = new Random();
int questionIndex = rnd.Next(0, count);
while(askedList.Count < count)
{
if(!askedList.Any(number => number == questionIndex))
{
askedList.Add(questionIndex);
var questionEntry = Questions.ElementAt(questionIndex);
string questionText = questionEntry.Key;
string questionAnswer = questionEntry.Value;
Console.WriteLine(questionText);
if (Console.ReadLine() == questionAnswer)
{
Console.WriteLine("Correct!");
}
else
{
Console.WriteLine("Wrong!");
}
}
questionIndex = rnd.Next(0, count);
}
return askedList;
}
Your code is a little bit messy. But random var isn't updated after recursively calling the function. Try this:
random = Ask(Questions, random, count);

Best way to distribute different outcomes?

I have a method called "GetValue()" which is supposed to return the value "A", "B", "C" or "D" on each method call.
I want this method to return the value "A" in 30% of the method calls and the value "B" in 14% of the method calls, the value "C" 31%.. and so on...
Wich is the best way to distribute theese values smoothly, I do not want the method to return the value "A" xxx times in a row becouse the value "A" are farest from it's requested outcome percentage.
Please, all answeres are appreciated.
You can use the Random class to achieve this:
private static Random Generator = new Random();
public string GetValue()
{
var next = Generator.Next(100);
if (next < 30) return "A";
if (next < 44) return "B";
if (next < 75) return "C";
return "D";
}
Update
For a more generic random weighted value store, the following may be a good starting point:
public class WeightedValueStore<T> : IDisposable
{
private static readonly Random Generator = new Random();
private readonly List<Tuple<int, T>> _values = new List<Tuple<int, T>>();
private readonly ReaderWriterLockSlim _valueLock = new ReaderWriterLockSlim();
public void AddValue(int weight, T value)
{
_valueLock.EnterWriteLock();
try
{
_values.Add(Tuple.Create(weight, value));
}
finally
{
_valueLock.ExitWriteLock();
}
}
public T GetValue()
{
_valueLock.EnterReadLock();
try
{
var totalWeight = _values.Sum(t => t.Item1);
var next = Random.Next(totalWeight);
foreach (var tuple in _values)
{
next -= tuple.Item1;
if (next < 0) return tuple.Item2;
}
return default(T); // Or throw exception here - only reachable if _values has no elements.
}
finally
{
_valueLock.ExitReadLock();
}
}
public void Dispose()
{
_valueLock.Dispose();
}
}
Which would then be useable like so:
public string GetValue()
{
using (var valueStore = new WeightedValueStore<string>())
{
valueStore.AddValue(30, "A");
valueStore.AddValue(14, "B");
valueStore.AddValue(31, "C");
valueStore.AddValue(25, "D");
return valueStore.GetValue();
}
}
Use Random.
Take care of the seed. See this link.
Example:
// You can provide a seed as a parameter of the Random() class.
private static Random RandomGenerator = new Random();
private static string Generate()
{
int value = RandomGenerator.Next(100);
if (value < 30)
{
return "A";
}
else if (value < 44)
{
return "B";
}
else
{
return "C";
}
}
If you want that distribution by average, you can just pick a random number and check it.
Random rnd = new Random();
int value = rnd.Next(100); // get a number in the range 0 - 99
if (value < 30) return "A";
if (value < 30+14) return "B";
if (value < 30+14+31) return "C";
return "D";
Note that you should create the random generator once, and reuse it for subsequent calls. If you create a new one each time, they will be initialised with the same random sequence if two method calls come too close in time.
If you want exactly that distribution for 100 items, then you would create an array with 100 items, where 30 are "A", 14 are "B", and so on. Shuffle the array (look up Fisher-Yates), and return one item from the array for each method call.
Let's say you have the arrays
String[] possibleOutcomes = new String[] { "A", "B", "C", "D" }
and
int[] possibleOutcomeProbabilities = new int[] { 30, 14, 31, 25 }
You can use the following strategy whenever you are required to output one of the outcomes:
Find the sum of all elements in possibleOutcomeProbabilities. Lets call this sum totalProbability.
Generate a random number between 1 and totalProbability. Lets call this randomly generated number outcomeBucket.
Iterate over possibleOutcomeProbabilities to determine which outcome outcomeBucket corresponds to. You then pick the corresponding outcome from possibleOutcomes.
This strategy will certainly not give you first 30% outcomes as A, next 14% as B, etc. However, as probability works, over a sufficiently large number of outcomes, this strategy will ensure that your possible outcomes are distributed as per their expected probabilities. This strategy gives you the advantage that outcome probabilities are not required to add up to 100%. You can even specify relative probabilities, such as, 1:2:3:4, etc.
If you are really worried about the fastest possible implementation for the strategy, you can tweak it as follows:
a. Calculate totalProbability only once, or when the probablities are changed.
b. Before calculating totalProbability, see if the elements in possibleOutcomeProbabilities have any common divisors and eliminate those. This will give you a smaller probability space to traverse each time.
try this:
Random r = new Random();
private string GetValue()
{
double d = r.Next();
if(d < 0.3)
return "A";
else if(d < 0.5)
return "B";
...etc.
}
EDIT: just make sure that the Random variable is created outside the function or you'll get the same value each time.
I would not recommend any hard-coded approach (it is hard to maintain and it's bad practice). I'd prefer a more generic solution instead.
enum PossibleOutcome { A, B, C, D, Undefined }
// sample data: possible outcome vs its probability
static readonly Dictionary<PossibleOutcome, double> probabilities = new Dictionary<PossibleOutcome, double>()
{
{PossibleOutcome.A, 0.31},
{PossibleOutcome.B, 0.14},
{PossibleOutcome.C, 0.30},
{PossibleOutcome.D, 0.25}
};
static Random random = new Random();
static PossibleOutcome GetValue()
{
var result = random.NextDouble();
var sum = 0.0;
foreach (var probability in probabilities)
{
sum += probability.Value;
if (result <= sum)
{
return probability.Key;
}
}
return PossibleOutcome.Undefined; // it shouldn't happen
}
static void Main(string[] args)
{
if (probabilities.Sum(pair => pair.Value) != 1.0)
{
throw new ApplicationException("Probabilities must add up to 100%!");
}
for (var i = 0; i < 100; i++)
{
Console.WriteLine(GetValue().ToString());
}
Console.ReadLine();
}

How can I get true randomness in this class without Thread.Sleep(300)?

I've made a class (code below) that handles the creation of a "matching" quiz item on a test, this is the output:
It works fine.
However, in order to get it completely random, I have to put the thread to sleep for at least 300 counts between the random shuffling of the two columns, anything lower than 300 returns both columns sorted in the same order, as if it is using the same seed for randomness:
LeftDisplayIndexes.Shuffle();
Thread.Sleep(300);
RightDisplayIndexes.Shuffle();
What do I have to do to make the shuffling of the two columns completely random without this time wait?
full code:
using System.Collections.Generic;
using System;
using System.Threading;
namespace TestSort727272
{
class Program
{
static void Main(string[] args)
{
MatchingItems matchingItems = new MatchingItems();
matchingItems.Add("one", "111");
matchingItems.Add("two", "222");
matchingItems.Add("three", "333");
matchingItems.Add("four", "444");
matchingItems.Setup();
matchingItems.DisplayTest();
matchingItems.DisplayAnswers();
Console.ReadLine();
}
}
public class MatchingItems
{
public List<MatchingItem> Collection { get; set; }
public List<int> LeftDisplayIndexes { get; set; }
public List<int> RightDisplayIndexes { get; set; }
private char[] _numbers = { '1', '2', '3', '4', '5', '6', '7', '8' };
private char[] _letters = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
public MatchingItems()
{
Collection = new List<MatchingItem>();
LeftDisplayIndexes = new List<int>();
RightDisplayIndexes = new List<int>();
}
public void Add(string leftText, string rightText)
{
MatchingItem matchingItem = new MatchingItem(leftText, rightText);
Collection.Add(matchingItem);
LeftDisplayIndexes.Add(Collection.Count - 1);
RightDisplayIndexes.Add(Collection.Count - 1);
}
public void DisplayTest()
{
Console.WriteLine("");
Console.WriteLine("--TEST:-------------------------");
for (int i = 0; i < Collection.Count; i++)
{
int leftIndex = LeftDisplayIndexes[i];
int rightIndex = RightDisplayIndexes[i];
Console.WriteLine("{0}. {1,-12}{2}. {3}", _numbers[i], Collection[leftIndex].LeftText, _letters[i], Collection[rightIndex].RightText);
}
}
public void DisplayAnswers()
{
Console.WriteLine("");
Console.WriteLine("--ANSWERS:-------------------------");
for (int i = 0; i < Collection.Count; i++)
{
string leftLabel = _numbers[i].ToString();
int leftIndex = LeftDisplayIndexes[i];
int rightIndex = RightDisplayIndexes.IndexOf(leftIndex);
string answerLabel = _letters[rightIndex].ToString();
Console.WriteLine("{0}. {1}", leftLabel, answerLabel);
}
}
public void Setup()
{
do
{
LeftDisplayIndexes.Shuffle();
Thread.Sleep(300);
RightDisplayIndexes.Shuffle();
} while (SomeLinesAreMatched());
}
private bool SomeLinesAreMatched()
{
for (int i = 0; i < LeftDisplayIndexes.Count; i++)
{
int leftIndex = LeftDisplayIndexes[i];
int rightIndex = RightDisplayIndexes[i];
if (leftIndex == rightIndex)
return true;
}
return false;
}
public void DisplayAsAnswer(int numberedIndex)
{
Console.WriteLine("");
Console.WriteLine("--ANSWER TO {0}:-------------------------", _numbers[numberedIndex]);
for (int i = 0; i < Collection.Count; i++)
{
int leftIndex = LeftDisplayIndexes[i];
int rightIndex = RightDisplayIndexes[i];
Console.WriteLine("{0}. {1,-12}{2}. {3}", _numbers[i], Collection[leftIndex].LeftText, _letters[i], Collection[rightIndex].RightText);
}
}
}
public class MatchingItem
{
public string LeftText { get; set; }
public string RightText { get; set; }
public MatchingItem(string leftText, string rightText)
{
LeftText = leftText;
RightText = rightText;
}
}
public static class Helpers
{
public static void 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;
}
}
}
}
Move Random rng = new Random(); to a static variable.
MSDN says "The default seed value is derived from the system clock and has finite resolution". When you create many Random objects within a small time range they all get the same seed and the first value will be equal to all Random objects.
By reusing the same Random object you will advance to the next random value from a given seed.
Only make one instance of the Random class. When you call it without a constructor it grabs a random seed from the computer clock, so you could get the same one twice.
public static class Helpers
{
static Random rng = new Random();
public static void Shuffle<T>(this IList<T> list)
{
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;
}
}
}
I have to put the thread to sleep for
at least 300 counts between the random
shuffling of the two columns, anything
lower than 300 returns both columns
sorted in the same order, as if it is
using the same seed for randomness
You've answered your own question here. It is "as if it is using the same seed" because it is using the same seed! Due to the relatively coarse granularity of the Windows system clock, multiple Random instances constructed at nearly the same time will have the same seed value.
As Albin suggests, you should just have one Random object and use that. This way instead of a bunch of pseudorandom sequences that all start at the same seed and are therefore identical, your Shuffle method will be based on a single pseudorandom sequence.
Considering that you have it as an extension method, you may desire for it to be reusable. In this case, consider having an overload that accepts a Random and one that doesn't:
static void Shuffle<T>(this IList<T> list, Random random)
{
// Your code goes here.
}
static void Shuffle<T>(this IList<T> list)
{
list.Shuffle(new Random());
}
This allows the caller to provide a static Random object if he/she's going to be calling Shuffle many times consecutively; on the other hand, if it's just a one-time thing, Shuffle can take care of the Random instantiation itself.
One last thing I want to point out is that since the solution involves using a single shared Random object, you should be aware that the Random class is not thread-safe. If there's a chance you might be calling Shuffle from multiple threads concurrently, you'll need to lock your Next call (or: what I prefer to do is have a [ThreadStatic] Random object for each thread, each one seeded on a random value provided by a "core" Random -- but that's a bit more involved).
Otherwise you could end up with Next suddenly just retuning an endless sequence of zeroes.
The problem is that you are creating your Random objects too close to each other in time. When you do that, their internal pseudo-random generators are seeded with the same system time, and the sequence of numbers they produce will be identical.
The simplest solution is to reuse a single Random object, either by passing it as an argument to your shuffle algorithm or storing it as a member-variable of the class in which the shuffle is implemented.
The way random generators work, roughly, is that they have a seed from which the random values are derived. When you create a new Random object, this seed is set to be the current system time, in seconds or milliseconds.
Let's say when you create the first Random object, the seed is 10000. After calling it three times, the seeds were 20000, 40000, 80000, generating whatever numbers form the seeds (let's say 5, 6, 2). If you create a new Random object very quickly, the same seed will be used, 10000. So, if you call it three times, you'll get the same seeds, 20000, 40000, and 80000, and the same numbers from them.
However, if you re-use the same object, the latest seed was 80000, so instead you'll generate three new seeds, 160000, 320000, and 640000, which are very likely to give you new values.
That's why you have to use one random generator, without creating a new one every time.
Try to use Random() just one time. You'll get the idea.

Categories

Resources