Учитывая список рейтингов игроков, я должен разделить игроков (то есть рейтинги) на две группы как можно более справедливо. Цель состоит в том, чтобы минимизировать разницу между совокупным рейтингом команд. Нет никаких ограничений относительно того, как я могу разделить игроков на команды (одна команда может иметь 2 игрока, а другая команда может иметь 10 игроков).
Например: [5, 6, 2, 10, 2, 3, 4]
должен вернуться([6, 5, 3, 2], [10, 4, 2])
Я хотел бы знать алгоритм для решения этой проблемы. Пожалуйста, обратите внимание, что я прохожу вводный курс по программированию в Интернете, поэтому были бы полезны простые алгоритмы.
Я использую следующий код, но по какой-то причине он-лайн проверка кода говорит, что он неправильный.
def partition(ratings):
set1 = []
set2 =[]
sum_1 = 0
sum_2 = 0
for n in sorted(ratings, reverse=True):
if sum_1 < sum_2:
set1.append(n)
sum_1 = sum_1 + n
else:
set2.append(n)
sum_2 = sum_2 + n
return(set1, set2)
Обновление: я связался с инструкторами, и мне сказали, что я должен определить другую «вспомогательную» функцию внутри функции, чтобы проверить все различные комбинации, а затем мне нужно проверить минимальную разницу.
Ответы:
Примечание: отредактировано для лучшей обработки случая, когда сумма всех чисел нечетна.
Возврат является возможным для этой проблемы.
Это позволяет рекурсивно исследовать все возможности без необходимости большого объема памяти.
Он останавливается, как только найдено оптимальное решение:
sum = 0
гдеsum
разница между суммой элементов множества A и суммой элементов множества B. РЕДАКТИРОВАТЬ: останавливается сразу жеsum < 2
, чтобы обработать случай, когда сумма всех чисел является нечетным, то есть соответствует минимальной разнице 1. Если эта глобальная сумма четна, минимальная разница не может быть равна 1.Это позволяет реализовать простую процедуру преждевременного отказа :
в данное время, если
sum
оно больше, чем сумма всех оставшихся элементов (т.е. не помещенных в A или B) плюс абсолютное значение полученного текущего минимума, тогда мы можем отказаться от проверки текущий путь, без изучения оставшихся элементов. Эта процедура оптимизирована с помощью:Вот псевдокод
Инициализация:
a[]
sum_back[i] = sum_back[i+1] + a[i];
min_diff = sum_back[0];
a[0]
в A -> индексi
исследуемого элемента установлен в 1up_down = true;
: это логическое значение указывает, движемся ли мы в данный момент вперед (true) или назад (false)Пока цикл:
Если (up_down): вперед
sum_back
sum
соответствии с этим выборомif (i == n-1)
: LEAF -> проверить, улучшено ли оптимальное значение, и вернуть, если новое значение равно 0 (РЕДАКТИРОВАТЬ:if (... < 2)
; идти назадЕсли (! Updown): назад
i == 0
: возвратsum
значениеВот код на C ++ (извините, не знаю Python)
источник
if I == 0
. Я проверил это, заменив 10 на 11 в вашем примереЯ думаю, что вы должны выполнить следующее упражнение самостоятельно, иначе вы многому не научитесь. Что касается этого, вот решение, которое пытается реализовать рекомендации вашего инструктора:
Вывод:
Обратите внимание, что этот вывод отличается от желаемого, но оба они верны.
Этот алгоритм основан на том факте, что для выбора всех возможных подмножеств данного набора с N элементами вы можете сгенерировать все целые числа с N битами и выбрать I-й элемент в зависимости от значения I-го бита. Я оставляю вам, чтобы добавить пару строк, чтобы остановить, как только
best_distance
ноль (потому что, конечно, не может быть лучше).Бит по битам (обратите внимание, что
0b
это префикс для двоичного числа в Python):Двоичное число:
0b0111001 == 0·2⁶+1·2⁵+1·2⁴+1·2³+0·2²+0·2¹+1·2⁰ == 57
Сдвиг вправо на 1:
0b0111001 >> 1 == 0b011100 == 28
Сдвиг влево на 1:
0b0111001 << 1 == 0b01110010 == 114
Сдвиг вправо на 4:
0b0111001 >> 4 == 0b011 == 3
Побитовый
&
(и):0b00110 & 0b10101 == 0b00100
Чтобы проверить, равен ли 5-й бит (индекс 4) 1:
(0b0111001 >> 4) & 1 == 0b011 & 1 == 1
Один, за которым следуют 7 нулей:
1 << 7 == 0b10000000
7 из них:
(1 << 7) - 1 == 0b10000000 - 1 == 0b1111111
Все 3-битовые комбинации:
0b000==0
,0b001==1
,0b010==2
,0b011==3
,0b100==4
,0b101==5
,0b110==6
,0b111==7
(заметим , что0b111 + 1 == 0b1000 == 1 << 3
)источник
Следующий алгоритм делает это:
a
, нечетные в списке,b
чтобы начатьa
иb
если изменение к лучшемуЯ добавил операторы печати, чтобы показать прогресс в вашем списке примеров:
Вывод:
источник
Поскольку я знаю, что должен сгенерировать все возможные списки, мне нужно создать вспомогательную функцию, чтобы помочь сгенерировать все возможности. После этого я проверяю минимальную разницу, и комбинация списков с этой минимальной разницей является желаемым решением.
Вспомогательная функция является рекурсивной и проверяет все возможности комбинаций списков.
Примеры:,
r = [1, 2, 2, 3, 5, 4, 2, 4, 5, 5, 2]
оптимальный раздел будет:([1, 2, 2, 3, 5, 4], [2, 4, 5, 5, 2])
с разницей1
.r = [73, 7, 44, 21, 43, 42, 92, 88, 82, 70]
оптимальным разделом будет:([73, 7, 21, 92, 88], [44, 43, 42, 82, 70])
с разницей0
.источник
Вот довольно сложный пример, предназначенный для образовательных целей, а не для производительности. В нем представлены некоторые интересные концепции Python, такие как списки и генераторы, а также хороший пример рекурсии, в которой необходимо надлежащим образом проверять дополнительные случаи. Расширения, например, действительны только команды с равным количеством игроков, легко реализуются в соответствующих индивидуальных функциях.
Вывод:
источник
Учитывая, что вам нужны даже команды, вы знаете целевой рейтинг рейтингов каждой команды. Это сумма рейтингов, деленная на 2.
Поэтому следующий код должен делать то, что вы хотите.
Вывод
Есть другие сплиты, которые имеют то же самое
fairness
, что все они доступны для поиска в кортеже strong_ratings, я просто выберу первый, так как он всегда будет существовать для любого списка рейтингов, который вы передаете (при условииlen(ratings) > 1
).источник
Жадное решение может привести к неоптимальному решению. Вот довольно простое жадное решение, идея состоит в том, чтобы отсортировать список по убыванию, чтобы уменьшить эффект добавления рейтингов в корзину. Рейтинг будет добавлен в тот сегмент, чья общая сумма рейтинга меньше
Вывод :
Редактировать:
Другой подход заключается в создании всех возможных подмножеств списка. Допустим, у вас есть l1, который является одним из подмножеств списка, тогда вы можете легко получить список l2 такой, что l2 = list (original) - l1. Количество всех возможных подмножеств списка размера n равно 2 ^ n. Мы можем обозначить их как последовательность целого числа от 0 до 2 ^ n -1. Возьмем пример, скажем, у вас есть список = [1, 3, 5], тогда ни одна из возможных комбинаций не равна 2 ^ 3, то есть 8. Теперь мы можем записать все комбинации следующим образом:
Решение:
Вывод :
источник