简体   繁体   中英

Randomize two List<string> in C# in same order

I have two string lists en and en1

List<string> en = new List<string>(new string[] { "horse", "cat", "dog", "milk", "honey"});
List<string> en1 = new List<string>(new string[] { "horse1", "cat2", "dog3", "milk4", "honey5" });

And I want radomize their content "Shuffle them" and put this radomized content to new two lists. But I also want them randomize same way so lists after randomization will still be

en[0] == en1[0]

random content after randomization

{ "cat", "horse", "honey", "milk", "dog"}
{ "cat2", "horse1", "honey5", "milk4", "dog3"}

Two obvious ways:

  • Take a normal shuffle method, and change it to modify both lists at the same time
  • Transform the two lists into a single joint list, shuffle that, then split them again

The second sounds cleaner to me. You'd use something like:

var joined = en.Zip(en1, (x, y) => new { x, y }).ToList();
var shuffled = joined.Shuffle(); // Assume this exists
en = shuffled.Select(pair => pair.x).ToList();
en1 = shuffled.Select(pair => pair.y).ToList();

Third obvious way:

Shuffle a list of integers 0, 1, ... , Count-1 and use this list as indexes into the original lists.


Edit

This goes along this lines (for user38...):

 List<int> shuffeledIndex = new List<int>(); for(int i = 0; i < en.Count; i++) shuffeledIndex.Add(i); shuffeledIndex.Shuffle(); // Assume this exists enshuffeled = en[shuffeledIndex[i]]; // instead en[i] en1shuffeled = en1[shuffeledIndex[i]]; 

Adding to Jon Skeet's answer, a nice way to do the shuffling is OrderBy(x => Guid.NewGuid()) so the code would look like

var joined = en.Zip(en1, (x, y) => new { x, y });
var shuffled = joined.OrderBy(x => Guid.NewGuid()).ToList(); 
en = shuffled.Select(pair => pair.x).ToList();
en1 = shuffled.Select(pair => pair.y).ToList();

Well, I think @Jon Skeet's answer is better than mine (since I only know the basics about C# and probably for some other reasons... :P), however you could shuffle your lists manually with something like this:

for(int i=0;i<en.Count;i++) {
    int remainingCount = en.Count - 1;
    int exchangeIndex = i + (new Random()).nextInt(0, remainingCount);
    swap(en, i, exchangeIndex);
    swap(en1, i, exchangeIndex);
}

Of course you would need to write a swap function like swap(List<string> list, int indexA, int indexB) :

string tmp = list[indexA];
list[indexA] = list[indexB];
list[indexB] = tmp;

You could first combine the two lists into one using the Zip function:

var zipped = en.Zip(en1, (first, second) => new { first, second }).ToList();

You'll then need a shuffling function to shuffle them. You could use the Fisher–Yates shuffle in an extension method:

public static void FisherYatesShuffle<T>(this IList<T> list)  
{  
    var rnd = new Random();  
    var x = list.Count;  
    while (x > 1) {  
        x--;  
        var y = rnd.Next(x + 1);  
        T value = list[y];  
        list[y] = list[x];  
        list[x] = value;  
    }  
}

To use the extension method:

var shuffled = zipped.FisherYatesShuffle();

Now you can split them back out again into two separate lists:

en = shuffled.Select(combined => combined.x).ToList();
en1 = shuffled.Select(combined => combined.y).ToList();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM