从列表中选择随机字符串 排除和非重复选择

问题描述 投票:3回答:3

我试图写一个程序,让用户:

  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; }
c# string random enumerable
3个回答
1
投票

解决这个问题的一种方法是使用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();

}

0
投票

此代码将始终为您提供符合您标准的输出:

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));

        }
    }
}

0
投票

在更简单的版本中,项目可以被洗牌:

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

或者,可以选择改组源,并且每个人都可以选择列表中下一个人。

© www.soinside.com 2019 - 2024. All rights reserved.