我正在尝试编写一个程序,让用户:
- 加载一组字符串。
- 遍历集合,并从同一集合中选择另一个字符串。
- 避免再次拾取已拾取的字符串。
- 有特定的字符串不能选择特定的字符串。
示例如下表:
下表是一个示例场景:
我应该如何以简单的方式做到这一点?
我有下面的代码,但是生成一个有效的集合需要很长时间,因为如果没有什么可以选择的话它会重新启动所有的东西,而不是消除这种可能性。
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
Participant
类由以下属性组成:
public string Name { get; set; }
public string Number { get; set; }
public string Exclusions { get; set; }
ParticipantPick
类由以下属性组成:
public string Participant { get; set; }
public string PickedParticipant { get; set; }
public string Number { get; set; }
最佳答案
解决此问题的一种方法是使用 dictionary
, 使用 tuple
的复合键和数据类型的匹配值 bool
.
Dictionary<Tuple<string, string>, bool>
复合键Tuple<sring,string>
将包含参与者的每一个排列并将他们匹配到他们合适的 bool
值(value)。
例如,字典中填充的值如下:
Dictionary<Tuple<"Judith","James">, true>
...将表明 Judith 选择 James 是有效的。
因此让我们创建一个字典,其中包含每个可能的参与者组合,并将它们的值设置为 true
因为它们在程序开始时有效。
这可以通过 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);
在获得所有可能选择的排列并将它们设置为 true
之后,我们可以遍历“不允许选择”列表并将该复合键的字典值更改为 false。
dictionary[new Tuple<string, string>(personNotAllowing, notAllowedPerson)] = false;
您可以通过以下方式使用循环来移除参与者自己的选择:
for(int abc=0;abc<participants.Length;abc++)
{
//remove clone set
Tuple<string, string> clonePair = Tuple.Create(participants[abc], participants[abc]);
dictionary.Remove(clonePair);
}
或者只需将克隆对的值更改为 false。
for(int abc=0;abc<participants.Length;abc++)
{
dictionary[Tuple.Create(participants[abc],participants[abc])] = false;
}
在这个示例程序中,我创建了一个 string[]
参与者,和一个string[]
对于他们不允许的相应人员列表。然后我执行笛卡尔连接,参与者数组自身。这导致每个排列,初始 true
bool 值。
我将参与者不允许为false的词典更改,并显示示例词典。
之后,我创建了 10 个随机参与者的实例,这些参与者正在挑选其他随机参与者并测试它是否有效。
每次参与者选择另一个参与者时,我都会检查该组合键以查看其值是否为 true
.
如果它确实产生了有效的选择,则结果参与者的每个组合被选择都将设置为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
{
}
}
运行代码可以更好地说明这个概念。
演示:
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();
}
关于c# - 从 List<string> 中选择带有排除项和非重复选择的随机字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47728758/