Randomly selects an item from one of the two lists - c#

I am working with generating Random items from either one or another list. I am kind of struggling how to do that.
Basically I have two lists:
List<string> names = new List<string>();
List<string> surnames = new List<string>();
I know how to get an item from one list randomly, but I am struggling how to do so there will be a possibility of taking an item from either names or surnames.
I know there is possibly an easy solution for that but couldn't find it.
Any help would be appreciated.

I know how to get an item from one list randomly
Leverage the technique for taking a random item from a single list to build a simple approach that works with two lists.
Imagine that you have a list of length N = names.Count + surnames.Count
Pick a random position p between 0, inclusive, and N, exclusive
If the position p is less than names.Count, use names[p]
Otherwise, use surnames[p - names.Length]
Effectively, the above approach picks an item form a merged list without performing an actual merge.
Edit: It turns out that you wanted a random combination of names[] and surnames[]. This is a simpler task, which is achieved by picking a random element from an array twice - once from names[], and then separately from surnames[].

This should do the job:
Random r = new Random();
Int32 nameIdx = r.Next(names.Count);
Int32 surnameIdx = r.Next(surnames.Count);
String randFullname = names[nameIdx] + " " + surnames[surnameIdx];
This is just an example to show you how to work with random array accesses. If you need to select only one name or one surname (the question was not really clear on that point "but I am struggling how to do so there will be a possibility of taking an item from either names or surnames"), just throw another random [0 1] and pick the first or the second list basing your choice on the output value:
List<String> currentList;
String result;
Random r = new Random();
if (rand.Next(0, 2) == 0)
currentList = names;
else
currentList = surnames;
Int32 idx = r.Next(currentList.Count);
String result = currentList[idx];
Otherwise, just pick a single random entry from a concatenation:
List<String> con = names.Concat(surnames).ToList();

You can merge two lists and access random element as follows,
var newList = names.Concat(surnames).ToList();
Random r = new Random();
string rand = newList[r.Next(newList.Count)];

If you want to do it in a one-liner you could try the following:
var r = new Random();
var randomName = names.Concat(surnames).OrderBy(n => r.Next()).First();
It's not very efficient memory wise, but it should work.

Related

How can I always have a different random phrase selected when using a random? [duplicate]

This question already has answers here:
Randomize a List<T>
(28 answers)
Closed 5 years ago.
I have this code:
public async Task ShowTimedCard()
{
phrase = phrases[(int)AS.rand.Next(phrases.Count)];
It gets a random number and then selects a phrase from phrases which is a List of phrase rows. The List has items added and removed from it by a background process.
Each phrase has an Id field. I cannot remove phrases that I have used from the List for other reasons.
Sometimes ShowTimedCard picks the same phrase twice in a row so I decided to store the lastPhrase information in a static variable like this:
AS.phrase.id = phrase.id
How can I make it so if there are more than 1 items in the phrases List then it will not pick the same phrase twice in a row? I was thinking of some kind of while loop or until loop but not sure how to implement that. I guess it needs to compare the phrase.Id with the AS.phrase.id
Show where you create rand
You should create it just once and reuse it
I bet you are creating new and not enough tick to get a new seed
OK I may have missed the question
Just shuffle the List using Yates shuffle
This is byte but you get the idea
for (byte i = (byte)(count - 1); i >= 1; i--)
{
byte k = (byte)rand.Next(i + 1);
if (k != i)
{ // exchange the values
curVal = deck[i];
deck[i] = deck[k];
deck[k] = curVal;
}
}
Or store the used id in a hashset
Random rand = new Random();
HashSet<int> hs = new HashSet<int>();
int next;
while (hs.Contains(next = rand.Next(12))) { }
hs.Add(next)
Moving target
while (next = rand.Next(12) == lastNext) { }
lastNext = next;
It sounds like what you really want to do is sort the list randomly and then you can just take consecutive items. This will ensure that you don't see any item before all the others have been displayed, but they won't be displayed in the order they were added:
// Shuffle our list of phrases, so they're in a random order
var rnd = new Random();
phrases = phrases.OrderBy(p => rnd.NextDouble()).ToList();
You can then re-shuffle the list anytime you want, like if you add new items or when you've reached the end of the list.
Here's a quick demo using integers:
// Create a list of consecutive integers and display it
var numbers = Enumerable.Range(1, 30).ToList();
Console.WriteLine("Original list:");
Console.WriteLine(string.Join(", ", numbers));
// Shuffle the list and display it again
var rnd = new Random();
numbers = numbers.OrderBy(n => rnd.NextDouble()).ToList();
Console.WriteLine("\nShuffled list:");
Console.WriteLine(string.Join(", ", numbers));
Output

Efficient way to pair a limited number of random elements from two separate collections

I have two lists (lista and listb), each containing an unknown number of points (two ints in a struct).
I want to create a new list containing unique random pairings from lista and listb. So an example entry might be [12,14] where 12 is an index for lista and 14 is an index for listb.
I also want to set a maximum number of pairings when calling this function. So instead of pairing every element in lista with every element in listb, I could limit it to 200 random pairings as an example.
My first attempt at this was to simply generate every possible pairing. Shuffle that list and knock off any elements past my max. This solution isn't nearly efficient enough.
My next attempt was to make an array per original list of every possible index, shuffle those separately, and then just iterate over them both until I had the max number of pairings (or all of them). This has several problems I'm not certain how to solve. One of which, lista could have 10 million elements for all I know. Creating a new array of 10 million elements (the indices list) and shuffling that when my max pairs might only be 200? Seems silly to go that far.
I've considered just choosing random elements from both lista/listb and seeing if I've already paired them before adding them to the new list. This is also quite a silly option as a lot of time can be spent picking duplicate pairings over and over.
So, what's a good option here or is there one? I don't want to iterate over every possible combination, pairings need to be unique, removing options from a list is quite slow due to the array re-sizing when they are quite large, distribution needs to be pretty uniform in the selection process for each list, etc.
Thanks for any and all help.
Edit - I meant the unique aspect regarding the pairs themselves. So element 10 in lista could be used over and over as long as the element in listb is different each time. The only catch there is I don't want to limit lista and listb right off as I need fairly even distribution across both lists for every pairing.
To avoid duplicates completely, you could try doing a sparse Fisher-Yates shuffle.
Create a Dictionary<int, int> dict that will map "indices in the Fisher-Yates array that do not hold their own index" to "the value at that index".
For the nth item, pick a random number x from n (inclusive) to "size of ListA * size of ListB" (exclusive)
dict[x] ?? x is your selected item.
Store dict[n] ?? n in dict[x].
Map the selected item back to a pair of indices (divide by size of ListA for the ListB index, modulus by the size of ListA for the ListA index).
A math or statistics buff might give you a formula for evaluating this but I just wrote some test code.
The code simply picks random pairs, and every time it sees a duplicate it tries again. Then for each such "pick a random pair until unique" cycle it counts how many retries it did and tracks this. Then finally this is summed up into a global array to track the relative frequency of these things.
Here's the results after about 1 minute of execution:
84382319 81 0 0 0 0 0 0 0 0
The numbers mean this:
Out of 421912 cycles [(84382319+81)/200]:
81 duplicates were found but retrying did not find a duplicate (3rd number and up is 0)
84382319 unique pairs could be found on the first try without duplicates
So, obviously this will start to rise if you increase the number of pairs you want generated or lower the numbers to choose wrong, but I'm not sure this will pose a problem in practice.
Here's the LINQPad program I used:
static Random R = new Random();
void Main()
{
var a = 10000;
var b = 10000;
var n = 200;
int[] counts = new int[10];
var dc = new DumpContainer().Dump();
while (true)
{
var once = Test(a, b, n);
for (int i = 0; i < once.Length; i++)
counts[i] += once[i];
dc.Content = Util.HorizontalRun(true, counts);
}
}
public static int[] Test(int a, int b, int n)
{
var seen = new HashSet<Tuple<int, int>>();
var result = new int[10];
for (int index = 0; index < n; index++)
{
int tries = 0;
while (true)
{
var av = R.Next(a);
var bv = R.Next(a);
var t = Tuple.Create(av, bv);
if (seen.Contains(t))
tries++;
else
{
seen.Add(t);
break;
}
}
result[tries]++;
}
return result;
}

picking random name from text file in c# windows forms non repeatable

I was wondering how get on with this code, currently working on a tournament bracket system.
Currently I have created a comboBox that fetches all the lines from "log.txt" there are 16 lines in the txt file; then I created a assign button that is supposed to assign all the names into 16 textboxes called User1 --> User16, however the same name cant be repeated.
I looked at "Array of list" & "Array of string", but I seem to be stuck since I cant really figure out what to put in the code.
my random button looks like this at the moment:
private void assign_Click(object sender, EventArgs e)
{
int x;
Random rnd = new Random();
x = rnd.Next(0, 16);
User1.Text = comboBox2.Items[x].ToString();
x = rnd.Next(0, 16);
User2.Text = comboBox2.Items[x].ToString();
x = rnd.Next(0, 16);
User3.Text = comboBox2.Items[x].ToString();
x = rnd.Next(0, 16);
User4.Text = comboBox2.Items[x].ToString();
and so on untill i hit
x = rnd.Next(0, 16);
User16.Text = comboBox2.Items[x].ToString();
}
One of the simplest, but not necessarily most efficient, way to do this is to put all your strings into a List<string> and remove them randomly one-by-one. This would work a lot better if you put all your textboxes into a collection as well. For example, given a list of strings called myStrings and a collection of textboxes called myTextboxes, you could:
for (var i=0; i < myStrings.Count; i++)
{
var idx = rnd.Next(0, myStrings.Count);
myTextboxes[i].Text = myStrings[idx]; // Note: we are assuming the two collections have
// the same length
myStrings.RemoveAt(idx);
}
This is very easy to implement and very easy to get right, but it's not terribly efficient (for 16 items, it probably doesn't matter) because your collection is repeatedly resized. For a more efficient approach, first shuffle your strings using the Fisher-Yates shuffle and then just assign the first entry from your shuffled strings to the first textbox, the second to the second, and so on.
You could use a List, and after each assignment you could remove the assigned item from the list. This would prevent duplicates.
http://msdn.microsoft.com/en-us/library/cd666k3e(v=vs.110).aspx
How about removing each item after selecting it?
Try something like
comboBox1.Items.RemoveAt(x);
After adding it and each time your
x = rnd.Next(0, 16);
code will reduce to
x = rnd.Next(0, 15);
until it reaches zero.
A different approach would be after selecting one randomly loop through all the filled ones (or all in general for simpler code) and check if it is already selected. If already selected get a new one until it's different.
For that you could use an array of textboxes (store what you have in an array) and loop through them like so
for(int i=0;i<16;i++)
if(textBoxArray[i].Text==comboBox2.Items[x].toString()){
chosen=true;
}
But removing them from the combobox is much simpler and much faster as code. If you want them to still appear in your combobox you could simultaneously in a List, get your items from that List and remove it from there.
The user will not see anything.
To accomplish this, is fairly simple.
First, you know there are 16 items in total. You don't need to randomize the list but rather, randomize the index that you use to access the item of the list. This part you know.
In order to avoid repeating items, you need to keep a list of indexes that have already been used. Once you have determined an unused index, that's when you need to access your list.
Example:
class Sample
{
List<int> _usedIndexes;
public Sample()
{
_usedIndexes = new List<int>();
}
public int GetRandomIndex(int s, e)
{
Random rnd = new Random();
//Initialize with a random number
int x = rnd.Next(s, e);
//While the index exists in the list of used indexes, get another random number.
while(_usedIndexes.Exists(index => index == x))
{
x = rnd.Next(s, e);
}
//Add the number to the list of used indexes
_usedIndexes.Add(x);
return x;
}
}
Then you simply access the List of names you have with the index you have acquired as follows:
int unusedIndex = GetRandomIndex(0, 16);
User1.Text = comboBox2.Items[unusedIndex].ToString();

getting a random number as long as its not in a list

var list = new List<int>(){1,17,18,21,30};
Random rnd = new Random(DateTime.Now.Second);
int r;
do
{
r = rnd.Next(1, 30);
}
while (list.Contains(r));
but i think that's a stupid solution, can anyone give me a more optimized approach?
even better if there is a way to prevent the Random instance from returning a number that it has already returned.
in case anyone wonders why do i need this its the first step in shuffling 3 byte arrays and combining them into one byte array and producing 3 byte arrays that hold the indices original order as it was in the original arrays.
Yes, one thing to make it much more efficient is use a HashSet<int> instead of a List<int> lookups for a HashSet are MUCH faster than a List (however the cost of the constructor will be slightly more for a HashSet).
Also if the input list is always the same numbers move it out of the function to help reduce the cost overhead of generating the HashSet the first time.
Due to order now mattering, in my personal experience (please test and profile for your own situation), after about 14 items in the list it is faster to convert a list to a HashSet and do the lookup than doing the lookup in the list itself.
var list = new List<int>(){1,17,18,21,30};
Random rnd = new Random(DateTime.Now.Second);
int r;
//In this example with 5 items in the list the HashSet will be slower do to the cost
// of creating it, but if we knew that the 5 items where fixed I would make this
// outside of the function so I would only have to pay the cost once per program
// start-up and it would be considered faster again due to amortized start-up cost.
var checkHashSet = new HashSet<int>(list);
do
{
r = rnd.Next(1, 30);
}
while (checkHashSet.Contains(rnd.Next(1, 30))); //Shouldent this be "r" not "rnd.Next(1,30)"?
You're right that looping isn't particularly efficient. You can use some handy extensions to select a number if you consider the constraint of the list of valid numbers, as opposed to the list of invalid ones.
So you have your list of invalid numbers:
var list = new List<int>(){1,17,18,21,30};
Which means that your list of valid numbers is the range from 1-30 except for these. Something like:
var validList = Enumerable.Range(1, 30).Except(list);
So we can use these extensions from the linked answer:
public static T RandomElement(this IEnumerable<T> enumerable)
{
return enumerable.RandomElementUsing(new Random());
}
public static T RandomElementUsing(this IEnumerable<T> enumerable, Random rand)
{
int index = rand.Next(0, enumerable.Count());
return enumerable.ElementAt(index);
}
And select a random element from the list of known valid numbers:
var kindOfRandomNumber = Enumerable.Range(1, 30).Except(list).RandomElement();

Building a Matrix of Combinations

I'm sure this has been asked a million times, but when I searched all the examples didn't quite fit, so I thought I should ask it anyway.
I have two arrays which will always contain 6 items each. For example:
string[] Colors=
new string[] { "red", "orange", "yellow", "green", "blue", "purple" };
string[] Foods=
new string[] { "fruit", "grain", "dairy", "meat", "sweet", "vegetable" };
Between these two arrays, there are 36 possible combinations(e.g. "red fruit", "red grain").
Now I need to further group these into sets of six unique values.
For example:
meal[0]=
new Pair[] {
new Pair { One="red", Two="fruit" },
new Pair { One="orange", Two="grain" },
new Pair { One="yellow", Two="dairy" },
new Pair { One="green", Two="meat" },
new Pair { One="blue", Two="sweet" },
new Pair { One="purple", Two="vegetable" }
};
where meal is
Pair[][] meal;
No element can be repeated in my list of "meals". So there is only ever a single "Red" item, and a single "meat" item, etc.
I can easily create the pairs based on the first two arrays, but I am drawing a blank on how best to then group them into unique combinations.
OK, you want a sequence containing all 720 possible sequences. This is a bit trickier but it can be done.
The basic idea is the same as in my previous answer. In that answer we:
generated a permutation at random
zipped the permuted second array with the unpermuted first array
produced an array from the query
Now we'll do the same thing except instead of producing a permutation at random, we'll produce all the permutations.
Start by getting this library:
http://www.codeproject.com/Articles/26050/Permutations-Combinations-and-Variations-using-C-G
OK, we need to make all the permutations of six items:
Permutations<string> permutations = new Permutations<string>(foods);
What do we want to do with each permutation? We already know that. We want to first zip it with the colors array, turning it into a sequence of pairs, which we then turn into an array. Instead, let's turn it into a List<Pair> because, well, trust me, it will be easier.
IEnumerable<List<Pair>> query =
from permutation in permutations
select colors.Zip(permutation, (color, food)=>new Pair(color, food)).ToList();
And now we can turn that query into a list of results;
List<List<Pair>> results = query.ToList();
And we're done. We have a list with 720 items in it. Each item is a list with 6 pairs in it.
The heavy lifting is done by the library code, obviously; the query laid on top of it is straightforward.
('ve been meaning to write a blog article for some time on ways to generate permutations in LINQ; I might use this as an example!)
There are 720 possible combinations that meet your needs. It is not clear from your question whether you want to enumerate all 720 or choose one at random or what. I'm going to assume the latter.
UPDATE: Based on comments, this assumption was incorrect. I'll start a new answer.
First, produce a permutation of the second array. You can do it in-place with the Fischer-Yates-Knuth shuffle; there are many examples of how to do so on StackOverflow. Alternatively, you could produce a permutation with LINQ by sorting with a random key.
The former technique is fast even if the number of items is large, but mutates an existing array. The second technique is slower, particularly if the number of items is extremely large, which it isn't.
The most common mistake people make with the second technique is sorting on a guid. Guids are guaranteed to be unique, not guaranteed to be random.
Anyway, produce a query which, when executed, permutes the second array:
Random random = new Random();
IEnumerable<string> shuffled = from food in foods
orderby random.NextDouble()
select food;
A few other caveats:
Remember, the result of a query expression is a query, not a set of results. The permutation doesn't happen until you actually turn the thing into an array at the other end.
if you make two instances of Random within the same millisecond, you get the same sequence out of them both.
Random is pseudo-random, not truly random.
Random is not threadsafe.
Now you can zip-join your permuted sequence to the first array:
IEnumerable<Pair> results = colors.Zip(shuffled, (color, food)=>new Pair(color, food));
Again, this is still a query representing the action of zipping the two sequences together. Nothing has happened yet except building some queries.
Finally, turn it into an array. This actually executes the queries.
Pair[] finalResults = results.ToArray();
Easy peasy.
Upon request, I will be specific about how I view the problem in regards to sorting. I know that since C# is a higher level language there are tons of quick and easy libraries and objects that can be used to reduce this to minimal code. This answer is actually attempting the solve the question by implementing sorting logic.
When initially reading this question I was reminded of sorting a deck of cards. The two arrays are very similar to an array for suit and an array for face value. Since one way to solve a shuffle is to randomize the arrays and then pick a card combined of both, you could apply the same logic here.
Sorting as a possible solution
The Fisher-Yates sorting algorithm essentially loops through all the indices of the array swapping the current index with a random index. This creates a fairly efficient sorting method. So then how does this apply to the problem at hand? One possible implementation could be...
static Random rdm = new Random();
public string[] Shuffle(string[] c)
{
var random = rdm;
for (int i = c.Length; i > 1; i--)
{
int iRdm = rdm.Next(i);
string cTemp = c[iRdm];
c[iRdm] = c[i - 1];
c[i - 1] = cTemp;
}
return c;
}
Source: Fisher-Yates Shuffle
The code above randomizes the positions of values within the string array. If you passed the Colors and Food arrays into this function, you would get unique pairings for your Pairs by referencing a specific index of both.
Since the array is shuffled, the pairing of the two arrays at index 0,1,2,etc are unique. The problem however asks for Pairs to be created. A Pair class should then be created that takes in a value at a specific index for both Colors and Foods. ie...Colors[3] and Foods[3]
public class Pair
{
public string One;
public string Two;
public Pair(string m1, string m2)
{
One = m1;
Two = m2;
}
}
Since we have sorted arrays and a class to contain the unique parings, we simply create the meal array and populate it with Pairs.
If we wanted to create a new pair we would have...
Pair temp = new Pair(Colors[0],Foods[0]);
With this information we can finally populate the meal array.
Pair[] meal = new Pair[Colors.Length - 1];
for (int i = 0; i < Colors.Length - 1; i++)
{
meal[i] = new Pair(Colors[i],Foods[i]);
}
This section of code creates the meal array and defines its number of indices by the length of Colors. The code then loops through the total number of Color values while creating new pair combos and dropping them in meal. This method assumes the length of the arrays are identical, a check could easily be made for the smallest array.
Full Code
private void Form1_Load(object sender, EventArgs e)
{
string[] Colors = new string[] { "red", "orange", "yellow", "green", "blue", "purple" };
string[] Foods = new string[] { "fruit", "grain", "dairy", "meat", "sweet", "vegetable" };
Colors = Shuffle(Colors);
Foods = Shuffle(Foods);
Pair[] meal = new Pair[Colors.Length - 1];
for (int i = 0; i < Colors.Length - 1; i++)
{
meal[i] = new Pair(Colors[i],Foods[i]);
}
}
static Random rdm = new Random();
public string[] Shuffle(string[] c)
{
var random = rdm;
for (int i = c.Length; i > 1; i--)
{
int iRdm = rdm.Next(i);
string cTemp = c[iRdm];
c[iRdm] = c[i - 1];
c[i - 1] = cTemp;
}
return c;
}
}
public class Pair
{
public string One;
public string Two;
public Pair(string m1, string m2)
{
One = m1;
Two = m2;
}
}
-Original Post-
You can simply shuffle the array. This will allow for the same method to populate meal, but with different results. There is a post on Fisher-Yates shuffle Here

Categories

Resources