Étant donné une liste de notes des joueurs, je dois diviser les joueurs (c.-à-d. Les notes) en deux groupes aussi équitablement que possible. L'objectif est de minimiser la différence entre la note cumulée des équipes. Il n'y a aucune contrainte quant à la façon dont je peux diviser les joueurs en équipes (une équipe peut avoir 2 joueurs et l'autre équipe peut avoir 10 joueurs).
Par exemple: [5, 6, 2, 10, 2, 3, 4]
devrait renvoyer ([6, 5, 3, 2], [10, 4, 2])
Je voudrais connaître l'algorithme pour résoudre ce problème. Veuillez noter que je prends un cours d'introduction à la programmation en ligne, donc des algorithmes simples seraient appréciés.
J'utilise le code suivant, mais pour une raison quelconque, le vérificateur de code en ligne dit qu'il est incorrect.
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)
Mise à jour: J'ai contacté les instructeurs et on m'a dit que je devrais définir une autre fonction "d'aide" à l'intérieur de la fonction pour vérifier toutes les différentes combinaisons, alors je dois vérifier la différence minimale.
Comme je sais que je dois générer toutes les listes possibles, je dois créer une fonction "d'aide" pour aider à générer toutes les possibilités. Après cela, je vérifie la différence minimale et la combinaison de listes avec cette différence minimale est la solution souhaitée.
La fonction d'assistance est récursive et vérifie toutes les possibilités de combinaisons de listes.
def partition(ratings):
def helper(ratings, left, right, aux_list, current_index):
if current_index >= len(ratings):
aux_list.append((left, right))
return
first = ratings[current_index]
helper(ratings, left + [first], right, aux_list, current_index + 1)
helper(ratings, left, right + [first], aux_list, current_index + 1)
#l contains all possible sublists
l = []
helper(ratings, [], [], l, 0)
set1 = []
set2 = []
#set mindiff to a large number
mindiff = 1000
for sets in l:
diff = abs(sum(sets[0]) - sum(sets[1]))
if diff < mindiff:
mindiff = diff
set1 = sets[0]
set2 = sets[1]
return (set1, set2)
Exemples: r = [1, 2, 2, 3, 5, 4, 2, 4, 5, 5, 2]
, la partition optimale serait: ([1, 2, 2, 3, 5, 4], [2, 4, 5, 5, 2])
avec une différence de 1
.
r = [73, 7, 44, 21, 43, 42, 92, 88, 82, 70]
, la partition optimale serait: ([73, 7, 21, 92, 88], [44, 43, 42, 82, 70])
avec une différence de 0
.
L'algorithme suivant fait cela:
a
, impair dans la liste b
pour commencera
et b
si le changement est pour le mieuxJ'ai ajouté des instructions d'impression pour montrer les progrès de votre liste d'exemples:
# -*- coding: utf-8 -*-
"""
Created on Fri Dec 6 18:10:07 2019
@author: Paddy3118
"""
from random import shuffle, random, randint
#%%
items = [5, 6, 2, 10, 2, 3, 4]
def eq(a, b):
"Equal enough"
return int(abs(a - b)) == 0
def fair_partition(items, jiggles=100):
target = sum(items) / 2
print(f" Target sum: {target}")
srt = sorted(items)
a = srt[::2] # every even
b = srt[1::2] # every odd
asum = sum(a)
bsum = sum(b)
n = 0
while n < jiggles and not eq(asum, target):
n += 1
if random() <0.5:
# move from a to b?
if random() <0.5:
a, b, asum, bsum = b, a, bsum, asum # Switch
shuffle(a)
trial = a[0]
if abs(target - (bsum + trial)) < abs(target - bsum): # closer
b.append(a.pop(0))
asum -= trial
bsum += trial
print(f" Jiggle {n:2}: Delta after Move: {abs(target - asum)}")
else:
# swap between a and b?
apos = randint(0, len(a) - 1)
bpos = randint(0, len(b) - 1)
trya, tryb = a[apos], b[bpos]
if abs(target - (bsum + trya - tryb)) < abs(target - bsum): # closer
b.append(trya) # adds to end
b.pop(bpos) # remove what is swapped
a.append(tryb)
a.pop(apos)
asum += tryb - trya
bsum += trya - tryb
print(f" Jiggle {n:2}: Delta after Swap: {abs(target - asum)}")
return sorted(a), sorted(b)
if __name__ == '__main__':
for _ in range(5):
print('\nFinal:', fair_partition(items), '\n')
Sortie:
Target sum: 16.0
Jiggle 1: Delta after Swap: 2.0
Jiggle 7: Delta after Swap: 0.0
Final: ([2, 3, 5, 6], [2, 4, 10])
Target sum: 16.0
Jiggle 4: Delta after Swap: 0.0
Final: ([2, 4, 10], [2, 3, 5, 6])
Target sum: 16.0
Jiggle 9: Delta after Swap: 3.0
Jiggle 13: Delta after Move: 2.0
Jiggle 14: Delta after Swap: 1.0
Jiggle 21: Delta after Swap: 0.0
Final: ([2, 3, 5, 6], [2, 4, 10])
Target sum: 16.0
Jiggle 7: Delta after Swap: 3.0
Jiggle 8: Delta after Move: 1.0
Jiggle 13: Delta after Swap: 0.0
Final: ([2, 3, 5, 6], [2, 4, 10])
Target sum: 16.0
Jiggle 5: Delta after Swap: 0.0
Final: ([2, 4, 10], [2, 3, 5, 6])
Voici un exemple assez élaboré, destiné à des fins éducatives plutôt qu'à des performances. Il présente des concepts intéressants de Python tels que les compréhensions de listes et les générateurs, ainsi qu'un bon exemple de récursivité dans lequel les cas marginaux doivent être vérifiés de manière appropriée. Extensions, par exemple uniquement des équipes avec un nombre égal de les joueurs sont valides, sont faciles à mettre en œuvre dans les fonctions individuelles appropriées.
def listFairestWeakTeams(ratings):
current_best_weak_team_rating = -1
fairest_weak_teams = []
for weak_team in recursiveWeakTeamGenerator(ratings):
weak_team_rating = teamRating(weak_team, ratings)
if weak_team_rating > current_best_weak_team_rating:
fairest_weak_teams = []
current_best_weak_team_rating = weak_team_rating
if weak_team_rating == current_best_weak_team_rating:
fairest_weak_teams.append(weak_team)
return fairest_weak_teams
def recursiveWeakTeamGenerator(
ratings,
weak_team=[],
current_applicant_index=0
):
if not isValidWeakTeam(weak_team, ratings):
return
if current_applicant_index == len(ratings):
yield weak_team
return
for new_team in recursiveWeakTeamGenerator(
ratings,
weak_team + [current_applicant_index],
current_applicant_index + 1
):
yield new_team
for new_team in recursiveWeakTeamGenerator(
ratings,
weak_team,
current_applicant_index + 1
):
yield new_team
def isValidWeakTeam(weak_team, ratings):
total_rating = sum(ratings)
weak_team_rating = teamRating(weak_team, ratings)
optimal_weak_team_rating = total_rating // 2
if weak_team_rating > optimal_weak_team_rating:
return False
Elif weak_team_rating * 2 == total_rating:
# In case of equal strengths, player 0 is assumed
# to be in the "weak" team
return 0 in weak_team
else:
return True
def teamRating(team_members, ratings):
return sum(memberRatings(team_members, ratings))
def memberRatings(team_members, ratings):
return [ratings[i] for i in team_members]
def getOpposingTeam(team, ratings):
return [i for i in range(len(ratings)) if i not in team]
ratings = [5, 6, 2, 10, 2, 3, 4]
print("Player ratings: ", ratings)
print("*" * 40)
for option, weak_team in enumerate(listFairestWeakTeams(ratings)):
strong_team = getOpposingTeam(weak_team, ratings)
print("Possible partition", option + 1)
print("Weak team members: ", weak_team)
print("Weak team ratings: ", memberRatings(weak_team, ratings))
print("Strong team members:", strong_team)
print("Strong team ratings:", memberRatings(strong_team, ratings))
print("*" * 40)
Production:
Player ratings: [5, 6, 2, 10, 2, 3, 4]
****************************************
Possible partition 1
Weak team members: [0, 1, 2, 5]
Weak team ratings: [5, 6, 2, 3]
Strong team members: [3, 4, 6]
Strong team ratings: [10, 2, 4]
****************************************
Possible partition 2
Weak team members: [0, 1, 4, 5]
Weak team ratings: [5, 6, 2, 3]
Strong team members: [2, 3, 6]
Strong team ratings: [2, 10, 4]
****************************************
Possible partition 3
Weak team members: [0, 2, 4, 5, 6]
Weak team ratings: [5, 2, 2, 3, 4]
Strong team members: [1, 3]
Strong team ratings: [6, 10]
****************************************
Étant donné que vous voulez même des équipes, vous connaissez le score cible des notes de chaque équipe. Il s'agit de la somme des notes divisée par 2.
Le code suivant devrait donc faire ce que vous voulez.
from itertools import combinations
ratings = [5, 6, 2, 10, 2, 3, 4]
target = sum(ratings)/2
difference_dictionary = {}
for i in range(1, len(ratings)):
for combination in combinations(ratings, i):
diff = sum(combination) - target
if diff >= 0:
difference_dictionary[diff] = difference_dictionary.get(diff, []) + [combination]
# get min difference to target score
min_difference_to_target = min(difference_dictionary.keys())
strong_ratings = difference_dictionary[min_difference_to_target]
first_strong_ratings = [x for x in strong_ratings[0]]
weak_ratings = ratings.copy()
for strong_rating in first_strong_ratings:
weak_ratings.remove(strong_rating)
Sortie
first_strong_ratings
[6, 10]
weak_rating
[5, 2, 2, 3, 4]
Il y a d'autres divisions qui ont le même fairness
elles sont toutes disponibles pour trouver à l'intérieur du tuple strong_ratings, je choisis simplement de regarder la première car cela existera toujours pour toute liste de notes que vous transmettez (à condition que len(ratings) > 1
).
Une solution gourmande pourrait donner une solution sous-optimale. Voici une solution assez simple et gourmande, l'idée est de trier la liste par ordre décroissant afin de diminuer l'effet de l'ajout de notes dans le bucket. La note sera ajoutée à ce compartiment dont la somme totale des notes est inférieure
lis = [5, 6, 2, 10, 2, 3, 4]
lis.sort()
lis.reverse()
bucket_1 = []
bucket_2 = []
for item in lis:
if sum(bucket_1) <= sum(bucket_2):
bucket_1.append(item)
else:
bucket_2.append(item)
print("Bucket 1 : {}".format(bucket_1))
print("Bucket 2 : {}".format(bucket_2))
Production :
Bucket 1 : [10, 4, 2]
Bucket 2 : [6, 5, 3, 2]
Modifier:
Une autre approche consistera à générer tous les sous-ensembles possibles de la liste. Disons que vous avez l1 qui est l'un des sous-ensembles de la liste, alors vous pouvez facilement obtenir la liste l2 telle que l2 = list (original) - l1. Le nombre de tous les sous-ensembles possibles de la liste de taille n est 2 ^ n. On peut les désigner comme seq d'un entier de 0 à 2 ^ n -1. Prenons un exemple, disons que vous avez list = [1, 3, 5] alors aucune combinaison possible n'est 2 ^ 3 soit 8. Maintenant, nous pouvons écrire toutes les combinaisons comme suit:
Solution:
def sum_list(lis, n, X):
"""
This function will return sum of all elemenst whose bit is set to 1 in X
"""
sum_ = 0
# print(X)
for i in range(n):
if (X & 1<<i ) !=0:
# print( lis[i], end=" ")
sum_ += lis[i]
# print()
return sum_
def return_list(lis, n, X):
"""
This function will return list of all element whose bit is set to 1 in X
"""
new_lis = []
for i in range(n):
if (X & 1<<i) != 0:
new_lis.append(lis[i])
return new_lis
lis = [5, 6, 2, 10, 2, 3, 4]
n = len(lis)
total = 2**n -1
result_1 = 0
result_2 = total
result_1_sum = 0
result_2_sum = sum_list(lis,n, result_2)
ans = total
for i in range(total):
x = (total ^ i)
sum_x = sum_list(lis, n, x)
sum_y = sum_list(lis, n, i)
if abs(sum_x-sum_y) < ans:
result_1 = x
result_2 = i
result_1_sum = sum_x
result_2_sum = sum_y
ans = abs(result_1_sum-result_2_sum)
"""
Produce resultant list
"""
bucket_1 = return_list(lis,n,result_1)
bucket_2 = return_list(lis, n, result_2)
print("Bucket 1 : {}".format(bucket_1))
print("Bucket 2 : {}".format(bucket_2))
Production :
Bucket 1 : [5, 2, 2, 3, 4]
Bucket 2 : [6, 10]