Как разделить всех участников на отдельные честные команды на основе их рейтинга?

Я хочу разделить игроков на отдельные честные команды на основе их рейтинга. Например, у меня есть такой список игроков:

players = [{
    name: "Qasim",
    rating: 1
  }, {
    name: "Mahsam",
    rating: 7
  }, {
    name: "Aj",
    rating: 3
  }, {
    name: "Osman",
    rating: 6
  }, {
    name: "Usama",
    rating: 2
  }, {
    name: "Bilal",
    rating: 8
  }, {
    name: "Kaka",
    rating: 20
  }, {
    name: "Owen",
    rating: 15
  }
]

Я хочу разделить их на 4 команды с лучшими равными общими баллами, а также поровну по участникам следующим образом:

Team A       Team B       Team C     Team D
=======      =======      =======    =======
Kaka: 20     Owen: 15     Bilal: 8   Mahsam: 7
Qasim: 1     Usama: 2     Aj: 3      Osman: 6

Я нашел способ решить эту проблему, но его трудно превратить в рубиновый код. Предположим, что у нас может быть более 8 игроков, а количество команд может варьироваться от 2 до 4 команд.

1. Sort all players by their ratings descendingly.
2. Assign team A the best player.
3. Assign team B the next best player.
4. Assign team C the next best player.
5. Assign team D the next best player.
6. Assign team D the next best player.
7. Assign team C the next best player.
8. Assign team B the next best player.
9. Assign team A the next best player.
10. Go to 2
11. End when we're out of players.

На самом деле в команде может быть от 2 до 4 команд, все игроки в каждой команде должны быть равными, и суммарные рейтинги каждой команды тоже должны быть равными или как можно ближе к равным.

Количество игроков может быть любым, и они должны делиться на команды.

Например, если 2 команды, общее количество игроков должно быть четным. Если 3 команды, общее количество игроков должно делиться на 3, а если 4 команды, общее количество игроков должно делиться на 4.

Я обновил вопрос. "... Вы имеете в виду, что хотите свести к минимуму разницу между наибольшим и наименьшим? Если да, то вы должны сказать, что..." ==> Да! Это именно то, что я хочу.

Peter Nguyen 10.04.2019 06:57

Мне кажется, что это NP-полная проблема. Я не уверен, что это просто вариант проблема с рюкзаком. Или если он достаточно особенный, чтобы иметь собственное имя. Возможно, вы захотите просмотреть одну из известных задач комбинаторной оптимизации.

spickermann 10.04.2019 06:58

@CarySwoveland Я уже написал это в вопросе «... Я нашел способ решить эту проблему ...».

Peter Nguyen 10.04.2019 07:07
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
628
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Мы можем решить эту проблему, отсортировав хэш по рейтингу ключа.

players.sort_by { |k| k[:rating] }

Теперь, когда вы отсортировали массив.

Вы можете выполнить итерацию до половины длины массива и нажать элемент i и элемент length-i в одной команде, в этом случае у вас есть 4 команды.

def divide_teams players
   players = players.sort_by { |k| k[:rating] } # sorted
   len = players.length
   teams = Hash.new(0)
   (len/2).times do |i|
      teams["team#{i+1}"] = [players[i], players[len-i-1]]
   end
   teams
end

divide_teams players

=> {"team1"=>[{:name=>"Qasim", :rating=>1}, {:name=>"Kaka", :rating=>20}],
 "team2"=>[{:name=>"Usama", :rating=>2}, {:name=>"Owen", :rating=>15}],
 "team3"=>[{:name=>"Aj", :rating=>3}, {:name=>"Bilal", :rating=>8}],
 "team4"=>[{:name=>"Osman", :rating=>6}, {:name=>"Mahsam", :rating=>7}]}

Сейчас, как я предположил, 4 команды и в каждой по 2 человека.

вы можете изменить функцию в соответствии с вашими потребностями, если функция team является динамической переменной.

Спасибо.

Ответ принят как подходящий

Вы можете реализовать описанный вами алгоритм назначения следующим образом.

def assign(nbr_teams, players)
  flip = [true, false].cycle
  players.
    sort_by { |p| p[:rating] }.
    each_slice(nbr_teams).
    map { |a| flip.next ? a.reverse : a }.
    transpose
end

assign(4, players)
  #=> [[{:name=>"Osman",  :rating=>6}, {:name=>"Mahsam", :rating=>7}],
  #    [{:name=>"Aj",     :rating=>3}, {:name=>"Bilal",  :rating=>8}],
  #    [{:name=>"Usama",  :rating=>2}, {:name=>"Owen",   :rating=>15}],
  #    [{:name=>"Qasim",  :rating=>1}, {:name=>"Kaka",   :rating=>20}]] 

Задания были бы такими, если бы было 2 команды.

assign(2, players)
  #=> [[{:name=>"Usama",  :rating=>2}, {:name=>"Aj",     :rating=>3},
  #     {:name=>"Bilal",  :rating=>8}, {:name=>"Owen",   :rating=>15}],
  #    [{:name=>"Qasim",  :rating=>1}, {:name=>"Osman",  :rating=>6},
  #     {:name=>"Mahsam", :rating=>7}, {:name=>"Kaka",   :rating=>20}]]

Шаги следующие.

nbr_teams = 4
flip = [true, false].cycle
  #=> #<Enumerator: [true, false]:cycle>

Массив#цикл работает так: flip.next #=> true, flip.next #=> false, flip.next #=> true и так далее. Продолжая,

a = players.sort_by { |p| p[:rating] }
  #=> [{:name=>"Qasim",  :rating=>1},  {:name=>"Usama", :rating=>2},
  #    {:name=>"Aj",     :rating=>3},  {:name=>"Osman", :rating=>6},
  #    {:name=>"Mahsam", :rating=>7},  {:name=>"Bilal", :rating=>8},
  #    {:name=>"Owen",   :rating=>15}, {:name=>"Kaka", :rating=>20}] 
b = a.each_slice(nbr_teams)
  #=> #<Enumerator:
  #     [{:name=>"Qasim",  :rating=>1},  {:name=>"Usama", :rating=>2},
  #      {:name=>"Aj",     :rating=>3},  {:name=>"Osman", :rating=>6},
  #      {:name=>"Mahsam", :rating=>7},  {:name=>"Bilal", :rating=>8},
  #      {:name=>"Owen",   :rating=>15}, {:name=>"Kaka",  :rating=>20}]
  #     :each_slice(4)> 

Мы можем преобразовать этот перечислитель в массив, чтобы увидеть, какие объекты он сгенерирует и передаст в map.

b.to_a
  #=> [[{:name=>"Qasim",  :rating=>1},  {:name=>"Usama", :rating=>2},
  #     {:name=>"Aj",     :rating=>3},  {:name=>"Osman", :rating=>6}], 
  #    [{:name=>"Mahsam", :rating=>7},  {:name=>"Bilal", :rating=>8}, 
  #     {:name=>"Owen",   :rating=>15}, {:name=>"Kaka",  :rating=>20}]] 

Продолжая,

c = b.map { |a| flip.next ? a.reverse : a }
  #=> [[{:name=>"Osman",  :rating=>6},  {:name=>"Aj",    :rating=>3},
  #     {:name=>"Usama",  :rating=>2},  {:name=>"Qasim", :rating=>1}],
  #    [{:name=>"Mahsam", :rating=>7},  {:name=>"Bilal", :rating=>8},
  #     {:name=>"Owen",   :rating=>15}, {:name=>"Kaka",  :rating=>20}]] 
c.transpose
  #=> [[{:name=>"Osman", :rating=>6}, {:name=>"Mahsam", :rating=>7}],
  #    [{:name=>"Aj",    :rating=>3}, {:name=>"Bilal",  :rating=>8}],
  #    [{:name=>"Usama", :rating=>2}, {:name=>"Owen",   :rating=>15}],
  #    [{:name=>"Qasim", :rating=>1}, {:name=>"Kaka",   :rating=>20}]] 

Может оказаться желательным преобразовать результаты в массив хэшей.

assign(4, players).map { |a| a.map { |h| [h[:name], h[:rating]] }.to_h }
  #=> [{"Osman"=>6, "Mahsam"=>7},
  #    {"Aj"   =>3, "Bilal" =>8},
  #    {"Usama"=>2, "Owen"  =>15},
  #    {"Qasim"=>1, "Kaka"  =>20}] 

Так как алгоритм (который я нашел) не идеален, но результат все же приемлемый. В любом случае, ваш ответ является наиболее точным из того, что мне нужно. Спасибо и за подробное объяснение ;-)

Peter Nguyen 10.04.2019 09:26

Другие вопросы по теме