c# - 从 List<string> 中选择带有排除项和非重复选择的随机字符串

标签 c# string random enumerable

我正在尝试编写一个程序,让用户:

  1. 加载一组字符串。
  2. 遍历集合,并从同一集合中选择另一个字符串。
  3. 避免再次拾取已拾取的字符串。
  4. 有特定的字符串不能选择特定的字符串。

示例如下表:

enter image description here

下表是一个示例场景:

enter image description here

我应该如何以简单的方式做到这一点?

我有下面的代码,但是生成一个有效的集合需要很长时间,因为如果没有什么可以选择的话它会重新启动所有的东西,而不是消除这种可能性。

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/

相关文章:

c# - 你能找到我的 SQL 语法中的错误吗?

c# - 犀牛模拟中的 AssertWasCalled

java - java中如何查找用户输入的字符串是否为基本字符串文字?

c++ - 使物体随机移动

c# - 如何在多线程中从TargetInitationException中优雅地恢复?

c# - 网络API : How to get a route parameter when controller action does not have corresponding argument

c++ - C++字符串中的ROS cv_bridge ImagePtr转换

javascript - javascript indexOf 对字符串和数组的工作方式有何不同?

c - rand() 函数行为

c# - 如何确定文件名是否随机?