#c# #random #duplicates
Вопрос:
Краткое описание: Случайным образом выберите два элемента из массива, а затем удалите их из массива, а затем случайным образом выберите один элемент из двух уже случайно выбранных элементов.
Я знаю, что об этом, вероятно, спрашивали раньше, но все ответы, которые я мог найти, я не знал, как применить к своему коду.
Вот мой код:
string[] teams = { "Team 1", "Team 2", "Team 3", "Team 4", "Team 5", "Team 6",
"Team 7", "Team 8" };
Console.WriteLine("The available teams are: ");
for (int i = 0; i < teams.Length; i )
{
Console.WriteLine(teams[i]);
}
Random rnd1 = new Random();
int r1 = rnd1.Next(teams.Length);
Random rnd2 = new Random();
int r2 = rnd2.Next(teams.Length);
Console.WriteLine("Round 1: " teams[r1] " vs " teams[r2]);
Как я могу сделать так, чтобы две команды всегда были разными, и чтобы я мог провести 2-й раунд, в котором будут еще две разные команды?
Еще одна вещь, которую мне нужно сделать, — это сделать так, чтобы, когда две команды будут выбраны для раунда (допустим, это команда 3 и команда 7), случайным образом выбрать одну из этих двух команд, чтобы я мог определить победителя. Я перепробовал кучу вещей, и ни одна из них не сработала, в основном мне нужна помощь с первой проблемой, но если кто-нибудь может помочь и со второй, я был бы признателен.
Комментарии:
1. В качестве примечания, не должно быть необходимости в двух
Random
экземплярах. ОдногоRandom
должно быть достаточно для всех случайных чисел, которые вы хотите сгенерировать. Что касается основной проблемы, не могли бы вы включить в вопрос пример желаемого результата программы?2. Простой выход-просто чтобы сравнить результат с предыдущими результатами, если он находится слишком близко от предыдущего результата, то просто попробуйте это опять же классический не получите следующий результат}в то время как(результат != предыдущее); в противном случае, можно исключить одну или обе команды от массив до рандомизации, это может быть достигнуто с помощью простых запросов LINQ.
3. Если вы случайным образом выбираете два элемента из одного массива (при условии равномерного распределения), нет необходимости затем рандомизировать порядок этих двух элементов. Вы с такой же вероятностью выбрали бы
b
тогда,a
какa
и тогдаb
.
Ответ №1:
Для того, чтобы реализовать его, нам действительно нужно отслеживать раунд, в который продвинулась команда.
С 8 командами максимальное количество раундов, которые можно сыграть, составляет 3.
Прежде чем что-либо делать, мы можем перетасовать список наших команд, а затем соединить их в пару, чтобы играть в их игру. Команда-победитель выйдет в следующий раунд.
Первое, что нужно сделать, это определить нашу классную команду:
public class Team
{
public string Name { get; set; }
public int Round { get; set; }
}
Затем нам нужно создать два вложенных цикла.
Внешний цикл имитирует разыгрываемый раунд, в то время как внутренний цикл имитирует текущую игру между двумя командами, определяющими победителя.
Собрав все это вместе, мы имеем:
public class Team
{
public string Name { get; set; }
public int Round { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Team> teams = new List<Team>
{
new Team
{
Name = "Team 1",
Round = 0
},
new Team
{
Name = "Team 2",
Round = 0
},
new Team
{
Name = "Team 3",
Round = 0
},
new Team
{
Name = "Team 4",
Round = 0
},
new Team
{
Name = "Team 5",
Round = 0
},
new Team
{
Name = "Team 6",
Round = 0
},
new Team
{
Name = "Team 7",
Round = 0
},
new Team
{
Name = "Team 8",
Round = 0
}
};
for (int j = 0; j < teams.Count / (teams.Count / 4); j )
{
//select current round and shuffles the teams
Random rng = new Random();
var currentPlayingTeams = teams.Where(x => x.Round == j).Select(c => c).ToList().OrderBy(x => rng.Next()).ToList();
if (currentPlayingTeams.Count > 1)
{
Console.WriteLine($"---------------- Round {j 1}----------------------");
for (int i = 0; i < currentPlayingTeams.Count; i = 2)
{
var currentMatch = currentPlayingTeams.GetRange(i, 2);
Console.WriteLine($"{currentMatch[0].Name} VS {currentMatch[1].Name}");
System.Threading.Thread.Sleep(1000);
Random ran = new Random();
var matchWinner = ran.Next(0, 2);
Console.WriteLine($"{currentMatch[matchWinner].Name} Won");
System.Threading.Thread.Sleep(1000);
teams.FirstOrDefault(x => x.Name == currentMatch[matchWinner].Name).Round ;
}
}
}
var winner = teams.OrderByDescending(v => v.Round).First();
Console.WriteLine("---------------- Result----------------------");
Console.WriteLine($"The winning team is {winner.Name}");
Console.ReadLine();
}
}
Поскольку мы перетасовали — мы можем выбрать первые 2 и заставить их играть.
Реализация требует, чтобы количество команд составляло 2n (4, 8,16,32 и т. Д.).
Ответ №2:
Один из методов состоит в том, чтобы создать копию массива, думайте об этом так, как если бы вы написали каждое из имен на листе бумаги и положили их в шляпу, мы назовем это pool
. Теперь, когда вы случайным образом выбираете команду из pool
списка, удалите эту команду из pool
списка, прежде чем выбирать снова:
Random rand = new Random();
string[] teams = { "Team 1", "Team 2", "Team 3", "Team 4", "Team 5", "Team 6", "Team 7", "Team 8" };
Console.WriteLine("The available teams are: ");
for (int i = 0; i < teams.Length; i )
{
Console.WriteLine(teams[i]);
}
List<int> pool = new List<int>(Enumerable.Range(0, teams.Length));
int round = 0;
while(pool.Any())
{
round ;
int index = rand.Next(0, pool.Count);
int r1 = pool[index];
pool.RemoveAt(index);
index = rand.Next(0, pool.Count);
int r2 = pool[index];
pool.RemoveAt(index);
Console.Write($"Round {round}: {teams[r1]} vs {teams[r2]}");
int winner = rand.Next(0, 2) == 1 ? r1 : r2;
Console.WriteLine($" - Winner: {teams[winner]}");
}
Как упоминалось в комментариях, нет необходимости создавать новый экземпляр
Random
, просто повторно используйте существующий.
Я не большой поклонник этого синтаксиса, подобную логику часто легче понять, когда ссылки на объекты используются вместо того, чтобы хранить массив индексов в другом массиве, однако это близко приближает проблему OPs.
Одно выполнение этого имеет следующий результат:
The available teams are:
Team 1
Team 2
Team 3
Team 4
Team 5
Team 6
Team 7
Team 8
Round 1: Team 5 vs Team 4 - Winner: Team 4
Round 2: Team 6 vs Team 1 - Winner: Team 6
Round 3: Team 3 vs Team 2 - Winner: Team 3
Round 4: Team 8 vs Team 7 - Winner: Team 8
В сценарии извлечения имен из шляпы часто то, что мы делаем, — это помещаем имена в шляпу, перемешиваем их, а затем последовательно вытаскиваем. Таким образом, мы действительно можем несколько упростить логику, сортируя pool
, а не удаляя элементы по случайному индексу, как только коллекция будет отсортирована один раз, результат уже будет достаточно случайным.
List<int> pool = new List<int>(Enumerable.Range(0, teams.Length).OrderBy(x => rand.Next()));
int round = 0;
while (pool.Any())
{
round ;
int r1 = pool[0];
int r2 = pool[1];
// remove the entries before the next round
pool.RemoveAt(0);
pool.RemoveAt(0); // previous remove has shifted the array
Console.Write($"Round {round}: {teams[r1]} vs {teams[r2]}");
// still randomly select a winner from the two selected
int winner = rand.Next(0, 2) == 1 ? r1 : r2;
Console.WriteLine($" - Winner: {teams[winner]}");
}
Это тонкая разница, но случайная сортировка массива позволяет писать более детерминированный код, а более предсказуемый код намного проще отлаживать, результат все равно случайный, фактически теперь, когда нам не нужно удалять элементы из пула, мы можем просто перечислить массив пула:
int [] pool = Enumerable.Range(0, teams.Length).OrderBy(x => rand.Next()).ToArray();
for (int index = 0, round = 1; index < pool.Length; index = 2, round )
{
int r1 = pool[index];
int r2 = pool[index 1];
Console.Write($"Round {round}: {teams[r1]} vs {teams[r2]}");
// still randomly select a winner from the two selected
int winner = rand.Next(0, 2) == 1 ? r1 : r2;
Console.WriteLine($" - Winner: {teams[winner]}");
}
Если входные команды могут быть нечетным числом, что может произойти, просто поставьте там чек и попрощайтесь с последней командой.
var t = teams.ToList();
t.Add("Team 9");
teams = t.ToArray();
// now that there is an odd number of teams...
int[] p = Enumerable.Range(0, teams.Length).OrderBy(x => rand.Next()).ToArray();
for (int index = 0, round = 1; index < p.Length; index = 2, round )
{
if (p.Length > index 1)
{
int r1 = p[index];
int r2 = p[index 1];
Console.Write($"Round {round}: {teams[r1]} vs {teams[r2]}");
// still randomly select a winner from the two selected
int winner = rand.Next(0, 2) == 1 ? r1 : r2;
Console.WriteLine($" - Winner: {teams[winner]}");
}
else
{
int rb = p[index];
Console.Write($"Round {round}: {teams[rb]} *** bye ***");
}
}
Результатом для вышесказанного является:
Round 1: Team 4 vs Team 2 - Winner: Team 4
Round 2: Team 5 vs Team 3 - Winner: Team 3
Round 3: Team 8 vs Team 1 - Winner: Team 1
Round 4: team 9 vs Team 6 - Winner: team 9
Round 5: Team 7 *** bye ***
Для предыдущего выполнения pool
имеет следующее содержимое:
[4,2,5,3,8,1,9,6,7]
Ни в коем случае нам не нужно было манипулировать исходной коллекцией teams
, кроме как добавлять 9-ю 😉
Таким образом, просто рандомизируя список адресов исходных ссылок, мы можем получить рандомизированный вывод с использованием детерминированной логики.