[英]Pick Random string from List<string> with exclusions and non-repetitive pick
im trying to write a program that would let a user: 我试图写一个程序,让用户:
Example is table below: 示例如下表:
And below table is a sample scenario: 下表是一个示例场景:
How am i supposed to do this the easy way? 我怎么这么简单呢?
i have below code, but it is taking like forever to generate a valid set since it restarts everything if there are nothing left to pick, while not eliminating the possibility. 我有下面的代码,但它生成一个有效的集合是永远的,因为它重新启动所有东西,如果没有什么可以选择,而不是消除可能性。
private List<Participant> _participants;
AllOverAgain:
var pickedParticipants = new List<Participant>();
var participantPicks = new List<ParticipantPick>();
foreach(var participant in _participants)
{
var pickedParticipantNames = from rp in participantPicks select rp.PickedParticipant;
var picks = (from p in _participants where p.Name != participant.Name & !Utilities.IsInList(p.Name, pickedParticipantNames) select p).ToList();
var pick = picks[new Random().Next(0, picks.Count())];
if(pick == null)
{
UpdateStatus($"No Available Picks left for {participant.Name}, Restarting...");
goto AllOverAgain;
}
var exclusions = participant.Exclusions.Split(',').Select(p => p.Trim()).ToList();
if(exclusions.Contains(pick.Name))
{
UpdateStatus($"No Available Picks left for {participant.Name}, Restarting...");
goto AllOverAgain;
}
participantPicks.Add(new ParticipantPick(participant.Name, pick.Name, participant.Number));
}
return participantPicks; // Returns the final output result
The Participant
Class consists of these Properties: Participant
类包含以下属性:
public string Name { get; set; }
public string Number { get; set; }
public string Exclusions { get; set; }
The ParticipantPick
Class consists of these Properties: ParticipantPick
类包含以下属性:
public string Participant { get; set; }
public string PickedParticipant { get; set; }
public string Number { get; set; }
One way you can solve this is by using a dictionary
, using a composite key of a tuple
and the matching value of a datatype bool
. 解决此问题的一种方法是使用
dictionary
,使用tuple
的复合键和数据类型bool
的匹配值。
Dictionary<Tuple<string, string>, bool>
The composite key Tuple<sring,string>
will contain every permutation of participants and match them to their appropriate bool
value. 复合键
Tuple<sring,string>
将包含参与者的每个排列,并将它们与适当的bool
值匹配。
For example, the dictionary filled with values such as: 例如,字典中填充了以下值:
Dictionary<Tuple<"Judith","James">, true>
...would be indicating that Judith picking James is valid. ......这表明朱迪思选择詹姆斯是有效的。
So lets create a dictionary with every single possible combination of participants, and set the value of them to true
for them being valid at the start of the program. 因此,让我们为每个可能的参与者组合创建一个字典,并将它们的值设置为
true
,以使它们在程序开始时有效。
This can be accomplished by a cartesian join using an array with itself . 这可以通过使用数组本身的笛卡尔联接来实现。
Dictionary<Tuple<string, string>, bool> dictionary = participants.SelectMany(left => participants, (left, right) => new Tuple<string, string>(left, right)).ToDictionary(item=> item, item=>true);
After getting every permutation of possible picks and setting them to true
, we can go through the "not allowed to pick" lists and change the dictionary value for that composite key to false. 在获得可能的选择的每个排列并将它们设置为
true
,我们可以通过“不允许选择”列表并将该组合键的字典值更改为false。
dictionary[new Tuple<string, string>(personNotAllowing, notAllowedPerson)] = false;
You can remove a participant from picking itself by using a loop in the following way: 您可以通过以下方式使用循环来删除参与者自行选择:
for(int abc=0;abc<participants.Length;abc++)
{
//remove clone set
Tuple<string, string> clonePair = Tuple.Create(participants[abc], participants[abc]);
dictionary.Remove(clonePair);
}
Or by simply changing the value of the clone pair to false. 或者只是将克隆对的值更改为false。
for(int abc=0;abc<participants.Length;abc++)
{
dictionary[Tuple.Create(participants[abc],participants[abc])] = false;
}
In this example program, I create a string[]
of participants, and a string[]
for the respective list of people they do not allow. 在这个示例程序中,我创建了一个参与者的
string[]
,以及一个string[]
用于他们不允许的相应人员列表。 I then perform a cartesian join, the participants array with itself . 然后我执行一个笛卡尔连接,参与者数组与自己 。 This leads to every permutation, with an initial
true
boolean value. 这导致每个排列,具有初始的
true
布尔值。
I change the dictionary where the participants are not allowed to false, and display the example dictionary. 我更改了不允许参与者为false的字典,并显示示例字典。
Afterward, I create 10 instances of random participants who are picking other random participants and test if it would be valid. 之后,我创建了10个随机参与者实例,他们正在挑选其他随机参与者并测试它是否有效。
Every time a participant picks another participant, I check that composite key to see if it has a value of true
. 每次参与者选择另一个参与者时,我都会检查该复合键以查看其值是否为
true
。
If it does result in a valid pick, then every combination of the resulting participant who was picked gets set to false
. 如果它确实产生了有效的选择,那么被挑选的结果参与者的每个组合都将设置为
false
。
for(int j=0; j<participants.Length;j++)
{
//Make the partner never be able to be picked again
Tuple<string, string> currentPair2 = Tuple.Create(partner, participants[j]);
try
{
dictionary[currentPair2] = false;
}
catch
{
}
}
This concept is better illustrated with running the code. 运行代码可以更好地说明这个概念。
The demo: 演示:
static void Main(string[] args)
{
//Create participants set
string[] participants = {"James","John","Tyrone","Rebecca","Tiffany","Judith"};
//Create not allowed lists
string[] jamesNotAllowedList = {"Tiffany", "Tyrone"};
string[] johnNotAllowedList = {};
string[] tyroneNotAllowedList = {};
string[] rebeccaNotAllowedList ={"James", "Tiffany"};
string[] judithNotAllowedList = {};
//Create list of not allowed lists
string[][] notAllowedLists = { jamesNotAllowedList, johnNotAllowedList, tyroneNotAllowedList, rebeccaNotAllowedList, judithNotAllowedList};
//Create dictionary<Tuple<string,string>, bool> from participants array by using cartesian join on itself
Dictionary<Tuple<string, string>, bool> dictionary = participants.SelectMany(left => participants, (left, right) => new Tuple<string, string>(left, right)).ToDictionary(item=> item, item=>true);
//Loop through each person who owns a notAllowedList
for (int list = 0; list < notAllowedLists.Length; list++)
{
//Loop through each name on the not allowed list
for (int person = 0; person<notAllowedLists[list].Length; person++)
{
string personNotAllowing = participants[list];
string notAllowedPerson = notAllowedLists[list][person];
//Change the boolean value matched to the composite key
dictionary[new Tuple<string, string>(personNotAllowing, notAllowedPerson)] = false;
Console.WriteLine(personNotAllowing + " did not allow " + notAllowedPerson);
}
}
//Then since a participant cant pick itself
for(int abc=0;abc<participants.Length;abc++)
{
//remove clone set
Tuple<string, string> clonePair = Tuple.Create(participants[abc], participants[abc]);
dictionary.Remove(clonePair);
}
//Display whats going on with this Dictionary<Tuple<string,string>, bool>
Console.WriteLine("--------Allowed?--Dictionary------------\n");
Console.WriteLine(string.Join(" \n", dictionary));
Console.WriteLine("----------------------------------------\n\n");
//Create Random Object
Random rand = new Random();
//Now that the data is organized in a dictionary..
//..Let's have random participants pick random participants
//For this demonstration lets try it 10 times
for (int i=0;i<20;i++)
{
//Create a new random participant
int rNum = rand.Next(participants.Length);
string randomParticipant = participants[rNum];
//Random participant picks a random participant
string partner = participants[rand.Next(participants.Length)];
//Create composite key for the current pair
Tuple<string, string> currentPair = Tuple.Create(partner,randomParticipant);
//Check if it's a valid choice
try
{
if (dictionary[currentPair])
{
Console.WriteLine(randomParticipant + " tries to pick " + partner);
Console.WriteLine("Valid.\n");
//add to dictionary
for(int j=0; j<participants.Length;j++)
{
//Make the partner never be able to be picked again
Tuple<string, string> currentPair2 = Tuple.Create(partner, participants[j]);
try
{
dictionary[currentPair2] = false;
}
catch
{
}
}
}
else
{
Console.WriteLine(randomParticipant + " tries to pick " + partner);
Console.WriteLine(">>>>>>>>Invalid.\n");
}
}
catch
{
//otherwise exception happens because the random participant
//And its partner participant are the same person
//You can also handle the random participant picking itself differently
//In this catch block
//Make sure the loop continues as many times as necessary
//by acting like this instance never existed
i = i - 1;
}
}
Console.ReadLine();
}
This code will always give you output that adheres to your criteria: 此代码将始终为您提供符合您标准的输出:
public static class Program
{
public static void Main(string[] args)
{
var gathering = new Gathering();
gathering.MakeSelections();
foreach (var item in gathering.participants)
{
Console.WriteLine(item.name + ":" + item.selectedParticipant);
}
}
public class Participant
{
public string name;
public List<string> exclusions;
public string selectedParticipant;
}
public class Gathering
{
public List<Participant> participants;
public List<string> availableParticipants;
public List<string> usedNames;
public Dictionary<string, string> result;
public Gathering()
{
//initialize participants
participants = new List<Participant>();
participants.Add(new Participant
{
name = "James",
exclusions = new List<string> { "Tiffany", "Tyrone" }
});
participants.Add(new Participant
{
name = "John",
exclusions = new List<string> { }
});
participants.Add(new Participant
{
name = "Judith",
exclusions = new List<string> { }
});
participants.Add(new Participant
{
name = "Rebecca",
exclusions = new List<string> { "James", "Tiffany" }
});
participants.Add(new Participant
{
name = "Tiffany",
exclusions = new List<string> { }
});
participants.Add(new Participant
{
name = "Tyrone",
exclusions = new List<string> { }
});
//prevent participants from selecting themselves
foreach (Participant p in participants)
{
p.exclusions.Add(p.name);
}
//create list of all the names (all available participants at the beginning)
availableParticipants = participants.Select(x => x.name).ToList();
}
public void MakeSelections()
{
Participant currentParticipant;
Random randy = new Random();
//Sort Participants by the length of their exclusion lists, in descending order.
participants.Sort((p1, p2) => p2.exclusions.Count.CompareTo(p1.exclusions.Count));
//Get the first participant in the list which hasn't selected someone yet
currentParticipant = participants.FirstOrDefault(p => p.selectedParticipant == null);
while (currentParticipant != null)
{
//of the available participants, create a list to choose from for the current participant
List<string> listToChooseFrom = availableParticipants.Where(x => !currentParticipant.exclusions.Contains(x)).ToList();
//select a random participant from the list of eligible ones to be matched with the current participant
string assignee = listToChooseFrom[randy.Next(listToChooseFrom.Count)];
currentParticipant.selectedParticipant = assignee;
//remove the selected participant from the list of available participants
availableParticipants.RemoveAt(availableParticipants.IndexOf(assignee));
//remove the selected participant from everyone's exclusion lists
foreach (Participant p in participants)
if (p.exclusions.Contains(assignee))
p.exclusions.RemoveAt(p.exclusions.IndexOf(assignee));
//Resort Participants by the length of their exclusion lists, in descending order.
participants.Sort((p1, p2) => p2.exclusions.Count.CompareTo(p1.exclusions.Count));
//Get the first participant in the list which hasn't selected someone yet
currentParticipant = participants.FirstOrDefault(p => p.selectedParticipant == null);
}
//finally, sort by alphabetical order
participants.Sort((p1, p2) => p1.name.CompareTo(p2.name));
}
}
}
In the simpler version, the items can just be shuffled: 在更简单的版本中,项目可以被洗牌:
string[] source = { "A", "B", "C", "D", "E", "F" };
string[] picked = source.ToArray(); // copy
var rand = new Random();
for (int i = source.Length - 1, r; i > 0; --i)
{
var pick = picked[r = rand.Next(i)]; // pick random item less than the current one
picked[r] = picked[i]; // and swap with the current one
picked[i] = pick;
Console.WriteLine(i + " swapped with " + r);
}
Console.WriteLine("\nsource: " + string.Join(", ", source) +
"\npicked: " + string.Join(", ", picked));
sample result: 样本结果:
5 swapped with 4
4 swapped with 2
3 swapped with 0
2 swapped with 1
1 swapped with 0
source: A, B, C, D, E, F
picked: F, D, B, A, C, E
or, the source can be optionally shuffled, and each person can pick the person that is next in the list. 或者,可以选择改组源,并且每个人都可以选择列表中下一个人。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.