プレイヤーの評価のリストを考えると、プレイヤー(つまり、評価)をできるだけ2つのグループに分割する必要があります。目標は、チームの累積評価の差を最小限に抑えることです。プレーヤーをチームに分割する方法に制約はありません(1つのチームには2人のプレーヤーがいて、もう1つのチームには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)
pdate:インストラクターに連絡し、関数内に別の「ヘルパー」関数を定義してすべての異なる組み合わせをチェックする必要があると言われました。次に、最小の違いをチェックする必要があります。
私はすべての可能なリストを生成する必要があることを知っているので、すべての可能性を生成するのに役立つ「ヘルパー」関数を作成する必要があります。それを行った後、私は真の違いをチェックすることに真実であり、その最小の違いを持つリストの組み合わせが望ましい解決策です。
ヘルパー関数は再帰的であり、リストの組み合わせのすべての可能性をチェックします。
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)
例: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
。
次のアルゴリズムがこれを行います。
a
に偶数のメンバーを入れ、リストb
に奇数のメンバーを入れますa
とb
の間でアイテムをランダムに移動および交換します例のリストの進行状況を示すために、printステートメントを追加しました。
# -*- 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')
出力:
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])
これは、パフォーマンスよりも教育を目的とした、かなり複雑な例です。それはいくつかの興味深いPython=リスト内包表記やジェネレータなどの概念、ならびにフリンジケースを適切にチェックする必要がある再帰の良い例を紹介します。例えば、等しい数のチームのみの拡張プレーヤーは有効であり、適切な個々の関数で実装するのは簡単です。
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)
出力:
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]
****************************************
あなたがチームでさえ欲しいなら、あなたは各チームのレーティングの目標スコアを知っています。これは、評価の合計を2で割ったものです。
したがって、次のコードはあなたが望むことをするはずです。
_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)
_
出力
_first_strong_ratings
[6, 10]
weak_rating
[5, 2, 2, 3, 4]
_
同じfairness
のスプリットが他にもあります。これらはすべてstrong_ratingsタプル内で見つけることができます。渡した評価リストには常に存在するため、最初のスプリットを確認することを選択します(ただし、len(ratings) > 1
)。
貪欲なソリューションは、次善のソリューションをもたらす可能性があります。これはかなり単純な貪欲な解決策です。アイデアは、バケットへの評価の追加の影響を減らすために、リストを降順に並べ替えることです。レーティングの合計が少ないバケットにレーティングが追加されます
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))
出力:
Bucket 1 : [10, 4, 2]
Bucket 2 : [6, 5, 3, 2]
編集:
別のアプローチは、リストのすべての可能なサブセットを生成することです。リストのサブセットの1つであるl1があるとすると、l2 = list(original)-l1のようなリストl2を簡単に取得できます。サイズnのリストのすべての可能なサブセットの数は2 ^ nです。それらを0から2 ^ n -1の整数のシーケンスとして表すことができます。例として、リスト= [1、3、5]とすると、可能な組み合わせは2 ^ 3、つまり8ではありません。すべての組み合わせを次のように記述できます。
解決:
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))
出力:
Bucket 1 : [5, 2, 2, 3, 4]
Bucket 2 : [6, 10]