我试图写一个程序,让用户:
示例如下表:
下表是一个示例场景:
我怎么这么简单呢?
我有下面的代码,但它生成一个有效的集合是永远的,因为它重新启动所有东西,如果没有什么可以选择,而不是消除可能性。
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
值匹配。
例如,字典中填充了以下值:
Dictionary<Tuple<"Judith","James">, true>
......这表明朱迪思选择詹姆斯是有效的。
因此,让我们为每个可能的参与者组合创建一个字典,并将它们的值设置为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
布尔值。
我更改了不允许参与者为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();
}
此代码将始终为您提供符合您标准的输出:
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));
}
}
}
在更简单的版本中,项目可以被洗牌:
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));
样本结果:
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
或者,可以选择改组源,并且每个人都可以选择列表中下一个人。