The faster way to get random numbers [closed] - c#

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I have a problem
I need to get random numbers, but except numbers that I already have.
My Code:
List<int> current_numbers = repository.GetCurrentNumbers();
Random rnd = new Random(42);
var new_values = Enumerable.Range(10000000,99999999)
.Except(current_numbers)
.OrderBy(o=> rnd.Next())
.Take(amount)
.ToList();
But this code is VERY SLOWLY
When I tried to use select instead OrderBy - I got DUPLICATES.
In my case, its must be without duplicates.
UPDATED:
With OrderBy -- I have problem with memory :)
Range must be like this 1M - 99M
Thank you.

Since you want numbers from such a large range, you probably want to use a HashSet to eliminate duplicates.
HashSet<int> current_numbers = new HashSet<int>(repository.GetCurrentNumbers());
HashSet<int> newValues = new HashSet<int>();
while (newValues.Count < amount)
{
var next = rnd.Next(10000000,99999999);
if (!current_numbers.Contains(next))
newValues.Add(next);
}
Converting current_numbers to a HashSet will speed up the process, because a call to Contains would take O(n) time if current_numbers is not stored as a HashSet.

Use a HashSet<T> instead of a List and then test using Contains - if you take a peek at Reference Source, you'll note that Except will build those existing numbers into a Set.
Since OrderBy tries to sort the whole collection, you're losing the benefits of lazily executed enumerables by using OrderBy at all - instead, using a regular loop and generate random numbers
var random = new Random(); // Default constructor or you'll get the same sequence because of a constant seed
var result = new HashSet<int>();
var currentNumbers = new HashSet<int>(repository.GetCurrentNumbers());
while(result.Count < amount)
{
var next = random.Next(1000000,99000000);
if(currentNumbers.Contains(next)) continue;
result.Add(next);
}
Or write you own generator
public static IEnumerable<int> GenerateRandom()
{
var random = new Random();
while(true) { yield return random.Next(1000000,99000000); }
}
// Later...
var newValues = MyClass.GenerateRandom()
.Where(next => !currentNumbers.Contains(next))
.Distinct()
.Take(amount)
.ToList();

To avoid creating that huge list of numbers you can instead keep track of the numbers you have and randomly pick where the next number should come from. First you need an ordered list of the used number. Then add the lower and upper bounds to it. Then keep track of the index of the lower and upper bounds. Then iterate until you have enough number and each time randomly pick an index between the lower and upper bound indexes. Check that the difference between the number at that index and the next is 1 and if so increment the index until it isn't or you hit the upper bound. If the upper bound was hit then just walk the upper bound down and try again. When you do find a gap in the used numbers randomly pick a number in the gap and add it to your return list and to the used list at the proper index. Then make sure to walk the lower bound index up if needed.
var used = repository.GetCurrentNumbers().OrderBy(x => x).ToList();
used.InsertAt(0, 999999) // This is the lower bounds;
used.Add(100000000); // This is the upper bounds;
var random = new Random();
var values = new List<int>();
int lowerIndex = 0;
int upperIndex = used.Length - 1;
while(values.Count < amount) {
int ind = random.Next(lowerIndex, upperIndex);
while(ind < upperIndex && used[ind+1] - used[ind] == 1) ind++;
if(ind == upperIndex){
while(used[upperIndex] - used[upperIndex-1] == 1) upperIndex--;
continue;
}
int val = random.Next(used[ind]+1, used[ind+1]);
values.Add(val);
used.InsertAt(ind+1, val);
while(used[lowerIndx+1] - used[lowerIndex] == 1) lowerIndex++;
}
This works best if amount isn't a very large number and your overall range is large and the initial used numbers is also sparse.

Related

Fill an array with unique random integers [duplicate]

This question already has answers here:
Randomize a List<T>
(28 answers)
Closed 5 years ago.
I want to fill a small array with unique values from a bigger array. How do I check the uniqueness?
Here's what I have:
int[] numbers45 = new int[45];
for (int i = 0; i <= 44; i++) // I create a big array
{
numbers45[i] = i + 1;
}
Random r = new Random();
int[] draw5 = new int[5]; //new small array
Console.WriteLine("The 5 draws are:");
for (int i = 1; i <= 4; i++)
{
draw5[i] = numbers45[r.Next(numbers45.Length)]; //I fill the small array with values from the big one. BUT the values might not be unique.
Console.WriteLine(draw5[i]);
}
There are multiple ways to do what you are asking.
First off, though, I would recommend to use one of the classes which wraps the array type and adds some extra functionality you could use (in this case a List would probably be a perfect structure to use)!
One way to handle this is to check if the value is already in the draw5 array. This can be done with (for example) the List<T>.Contains(T) function, and if it exists, try another.
Personally though, I would probably have randomized the first array with the OrderBy linq method and just return a random number, like:
numbers45.OrderBy(o => random.Next());
That way the numbers are already random and unique when it is supposed to be added to the second list.
And a side note: Remember that arrays indexes starts on index 0. In your second loop, you start at 1 and go to 4, that is, you wont set a value to the first index.
You could just run for (int i=0;i<5;i++) to get it right.
Inspired by Jite's answer, I changed to use Guid to randomize the numbers
var randomized = numbers45.OrderBy(o => Guid.NewGuid().ToString());
Then you could take the draws by:
var draws = randomized.Take(5).ToArray;
HashSet<int> hs = new HashSet<int>();
int next = random.next(45);
while(hs.Length <=5)
{
if(!hs.Contains(array[next]))
hs.Add(array[next]);
next = random next(45);
}

how to create a random int[] [duplicate]

This question already has answers here:
Most efficient way to randomly "sort" (Shuffle) a list of integers in C#
(13 answers)
Closed 5 years ago.
I need to create a random array of int with certain parameters.
int[] test = new int[80];
Random random = new Random();
I need to assign 1's and 0's, randomly to the array. I know how to do that.
test[position] = Random.Next(0,2);//obviously with a for loop
But I need to have exactly 20 1's, but they need to be randomly positioned in the array. I don't know how to make sure that the 1's are randomly positioned, AND that there are exactly 20 1's. The rest of the positions in the array would be assigned 0.
I think you need to turn your thinking around.
Consider:
var cnt = 20;
while (cnt > 0) {
var r = Random.Next(0, 80);
if (test[r] != 1) {
test[r] = 1;
cnt--;
}
}
Expanding explanation based on comments from CodeCaster. Rather than generate a random value to place in the array, this code generates and index to set. Since C# automatically initializes the test array to 0 these values are already set. So all you need is to add your 1 values. The code generates a random index, tests it to see if it isn't 1, if so it sets the array element and decrements a count (cnt). Once count reaches zero the loop terminates.
This won't properly function if you need more values than 0 and 1 that is true. Of course the questions explicitly stated that these were the needed values.
"This causes horrible runtime performance". What!? Can you produce any prove of that? There is a chance that the index generated will collide with an existing entry. This chance increases as more 1's are added. Worst case is there is a 19/80 (~23%) chance of collision.
If you know you need exactly 20 of one value, a better way to go about this is to pre-populate the array with the required values and then shuffle it.
Something like this should work:
int[] array = new int[80];
for (int i = 0; i < 80; i++)
{
int val = 0;
if (i < 20)
{
val = 1;
}
array[i] = val;
}
Random rnd = new Random();
int[] shuffledArray = array.OrderBy(x => rnd.Next()).ToArray();
You can do
for (int i = 0; i < 20; i++)
{
var rand = random.Next(0,80);
if (test[rand] == 1)
{
i--;
continue;
}
test[rand] = 1;
}
Remaining are automatically zero.

Generate random numbers from 1 to 6 and stop at the last one

I have the following code :
int GetRandNumber()
{
Random r = new Random();
return r.Next() % 6 + 1;
}
I am developing a windows store app. I have a textbox in which a single number from 1 to 6 is generated. For every number I have made a specific event. I need to make sure that every number is generated only once so that the events do not repeat.
Looks like you just need to shuffle numbers. I'd suggest to create an array of numbers from 1 to 6 and just shuffle the array. You can see some code here for how to shuffle an array/list.
First of all you need to be careful with this implementation, if you call GetRandomNumber() multiple times very close together it will give you the same result. A better function would be
int GetRandNumber(Random r)
{
return r.Next(1, 7); //This does the same as "r.Next() % 6 + 1" but removes bias.
}
//used like
Random rand = new Random();
foreach(var foo in bar)
{
//The same instance of rand is used for each call instead of a new one each time.
foo.SomeProp = GetRandNumber(rand);
}
However that is completely separate from what you need to do, you should not be generating random numbers between 1 though 6. What you need to do is make a list of 1 though 6 then shuffle the list, not use random numbers at all (well you will likely use them during the shuffle but that is a implementation detail)
List<int> numbers = new List<int>();
for(int i = 1; i <= 6; i++)
{
numbers.Add(i);
}
MyShuffleListFunction(numbers);
There are plenty of examples on this site on how to make a shuffle function.
Maybe I'm wrong, but as I understand you want something like this as an output:
344213266125
To achieve this you should keep track which numbers are already generated, and stop if all has been "rolled" at least once.
So let's have a bool array initialized to 6 false values, and after each random number generation (after each roll) set the array's corresponing element to true. Check if there are any false values in the array, if not then "roll again".
Also, you might consider generating the numbers according to the other answers, as they are of course right: this is not an ideal way.
Update
Okay, based on your question's update I also update my pseudocode (or maybe I should call this prose):
Keep track of the already "rolled" numbers (as above). When generating a new number check if it has been already generated or not. If it's been rolled before, then roll again, until you roll a new one: then update the array accordingly. Also be sure to check if there is a valid number or all has been rolled before at the beginning of the roll...
But now it seems for me that what you really are looking for is simply a shuffle, which has been proposed in other answers.
Perhaps this will work for you. The approach is to record the random results in a List and to skip the output if the results already exists in the list.
class Program
{
static int tmp;
static void Main(string[] args)
{
List<int> alreadyFired = new List<int>();
while (alreadyFired.Count != 6)
{
Random rng = new Random();
int diceresult = rng.Next(1,7);
foreach (var item in alreadyFired)
{
if (item == diceresult)
{
tmp = item;
}
}
if (!(tmp == diceresult))
{
alreadyFired.Add(diceresult);
Console.WriteLine(diceresult);
}
}
Console.WriteLine("no events left");
Console.ReadKey();
}
}

How to get max 10 random items from list?

I have a list that can have a variable amount of items (but no more than ~30 so no performance issue here), I want to get maximum 10 random items and I wrote a piece of code for that which works fine if there are 10+ items in the list. The issue with the code below is that it obviously doesn't randomize the result if there are less than 10 items in the list and if there are less than 10 items I want to display them all but in random order.
var keyPersons = people.ToList();
Random rnd = new Random();
while (keyPersons.Count() > 10)
{
int j = rnd.Next(0, keyPersons.Count());
keyPersons.RemoveAt(j);
}
rptKeyPersons.DataSource = keyPersons;
How can I randomize the result if there are let's say only five items in the person list?
Thanks in advance.
Given the clarification in the comments that you need a randomized ordering, you can perform a shuffle of the list and then simply take n items in order. Note that even when your list had more than 10 items, your method still not cover randomizing those results beyond eliminating elements at random points. The elements left were still in their original order otherwise. This will address both issues.
Given
public static void Shuffle<T>(IList<T> list)
{
var random = new Random();
for (int index = list.Count - 1; index >= 1; index--)
{
int other = random.Next(0, index + 1);
T temp = list[index];
list[index] = list[other];
list[other] = temp;
}
}
You could have
var keyPersons = people.ToList();
Shuffle(keyPersons);
rptKeyPersons.DataSource = keyPersons.Take(10);
Take would select the first 10 items in the freshly shuffled sequence. If less than 10 exist, it simply takes as many as are available.
I like Anthonies solution but it can be as simple as this.
var keyPersons = people.OrderBy(x => Guid.NewId()).ToList()
rptKeyPersons.DataSource = keyPersons.Take(10);

C# creating a list of random unique integers

I need to create a list with one billion integers and they must all be unique. I also need this to be done extremely fast.
Creating a list and adding random numbers one by one and checking to see if each one is a duplicate is extremely slow.
It seems to be quite fast if I just populate a list with random numbers without checking if they are duplicates and then using distinct().toList(). I repeat this until there are no more duplicates. However the extra memory used by creating a new list is not optimal. Is there a way to get the performance of distinct() but instead of creating a new list it just modifies the source list?
Do the integers need to be in a certain range? If so, you could create an array or list with all numbers in that range (for example from 1 to 1000000000) and shuffle that list.
I found this the fastest while maintaining randomness:
Random rand = new Random();
var ints = Enumerable.Range(0, numOfInts)
.Select(i => new Tuple<int, int>(rand.Next(numOfInts), i))
.OrderBy(i => i.Item1)
.Select(i => i.Item2);
...basically assigning a random id to each int and then sorting by that id and selecting the resulting list of ints.
You can track duplicates in a separate HashSet<int>:
var set = new HashSet<int>();
var nums = new List<int>();
while(nums.Count < 1000000000) {
int num;
do {
num = rand.NextInt();
} while (!set.Contains(num));
set.Add(num);
list.Add(num);
}
You need a separate List<int> to store the numbers because a hashset will not preserve your random ordering.
Taking the question literally (a list with one billion integers and they must all be unique):
Enumerable<int>.Range(0, 1000000000)
But along the lines of CodeCaster's answer, you can create the list and shuffle it at the same time:
var count = 1000000000;
var list = new List<int>(count);
var random = new Random();
list.Add(0);
for (var i = 1; i < count; i++)
{
var swap = random.Next(i - 1);
list.Add(list[swap]);
list[swap] = i;
}
If the amount of possible integers from which you draw is significantly larger (say factor 2) than the amount of integers you want you can simply use a HashSet<T> to check for duplicates.
List<int> GetUniqueRandoms(Random random, int count)
{
List<int> result = new List<int>(count);
HashSet<int> set = new HashSet<int>(count);
for(int i = 0; i < count; i++)
{
int num;
do
{
num = random.NextInt();
while(!set.Add(num));
result.Add(num);
}
return result;
}
This allocates the collections with the correct capacity to avoid reallocation during growth. Since your collections are large this should be a large improvement.
You can also use Distinct a single time:
IEnumerable<int> RandomSequence(Random random)
{
while(true)
{
yield return random.NextInt();
}
}
RandomSequence(rand).Distinct().Take(1000000000).ToList();
But with both solutions you need enough memory for a HashSet<int> and a List<int>.
If the amount of possible integers from which you draw is about as large as the amount of integers you want, you can create an array that contains all of them, shuffle them and finally cut off those you're not interested in.
You can use Jon Skeet's shuffle implementation.
What if you created the list in a sorted but still random fashion (such as adding a random number to the last element of the list as the next element), and then shuffled the list with a Fisher-Yates-Durstenfeld? That would execute in linear time overall, which is pretty much as good as it gets for list generation. However, it might have some significant bias that would affect the distribution.

Categories