Проблема сильно NP-полная. Это означает, что невозможно найти правильное решение в разумные сроки. (См. Проблема с 3 разделами , спасибо, Пол).
Вместо этого вы захотите найти хороший примерный генератор решений. Они часто могут быть очень близки к оптимальному ответу за очень короткое время. Я могу порекомендовать метод Имитация отжига , который вы также сможете использовать для множества других NP-полных задач.
Идея такова:
- Распределите предметы случайным образом.
- Постоянно производите случайные обмены между двумя случайными игроками, если это делает систему более честной или чуть менее справедливой (см. Вики в деталях).
- Остановитесь, когда у вас будет что-то достаточно справедливое или у вас закончится время.
Это решение намного сильнее, чем многие жадные алгоритмы. Жадный алгоритм - это тот, в котором вы постоянно добавляете самый крупный предмет в «самого бедного» игрока. Пример тестового примера, где жадный сбой - [10,9,8,7,7,5,5]
.
Я сделал реализацию SA для вас. Это следует за статьей вики строго, в образовательных целях. Если вы оптимизируете его, я бы сказал, что улучшение в 100 раз не будет нереальным.
from __future__ import division
import random, math
values = [10,9,8,7,7,5,5]
M = 3
kmax = 1000
emax = 0
def s0():
s = [[] for i in xrange(M)]
for v in values:
random.choice(s).append(v)
return s
def E(s):
avg = sum(values)/M
return sum(abs(avg-sum(p))**2 for p in s)
def neighbour(s):
snew = [p[:] for p in s]
while True:
p1, p2 = random.sample(xrange(M),2)
if s[p1]: break
item = random.randrange(len(s[p1]))
snew[p2].append(snew[p1].pop(item))
return snew
def P(e, enew, T):
if enew < e: return 1
return math.exp((e - enew) / T)
def temp(r):
return (1-r)*100
s = s0()
e = E(s)
sbest = s
ebest = e
k = 0
while k < kmax and e > emax:
snew = neighbour(s)
enew = E(snew)
if enew < ebest:
sbest = snew; ebest = enew
if P(e, enew, temp(k/kmax)) > random.random():
s = snew; e = enew
k += 1
print sbest
Обновление: После игры с Branch'n'Bound я теперь считаю, что этот метод лучше, так как он дает отличные результаты для случая N = 30, M = 6 в течение секунды. Тем не менее, я думаю, вы могли бы так же поиграть с имитацией отжига.