Предложение на алгоритме для распределения объектов другого значения

У меня есть следующая проблема:

Данные объекты N (N <30) различных значений несколько из "k" константы т.е. k, 2k, 3k, 4k, 6k, 8k, 12k, 16k, 24k и 32k, мне нужен алгоритм, который распределит все объекты плеерам M (M <= 6) таким способом, которым итоговое значение объектов, которые получает каждый игрок, как, как раз когда возможный (другими словами, я хочу распределить все объекты всем плеерам самым справедливым возможным способом).

Править: Самым справедливым распределением я подразумеваю, что различие между значением объектов, которые получают любые два игрока, минимально. Другой подобный случай был бы: у Меня есть монеты N различных значений, и я должен разделить их одинаково между плеерами M; иногда они не делятся точно, и я должен найти следующий лучший случай распределения (где никакой плеер не сердит, потому что другой получил слишком много денег).

Мне не нужен (псевдо) код для решения этого (также, это не домашняя работа :)), но я буду ценить любые идеи или ссылки на алгоритмы, которые могли решить это.

Спасибо!

6
задан Unknown 13 July 2015 в 06:25
поделиться

7 ответов

Задача является строго NP-полной. Это означает, что невозможно обеспечить правильное решение в разумные сроки. (См. Проблема с 3 разделами , спасибо, Пол).

Вместо этого вы захотите воспользоваться хорошим генератором приближенных решений. Часто они могут быть очень близки к оптимальному ответу за очень короткое время. Я могу порекомендовать метод Simulated Annealing , который вы также сможете использовать для множества других NP-полных задач.

Идея такова:

  1. Распределите предметы в случайном порядке.
  2. Постоянно производите случайные обмены между двумя случайными игроками, пока это делает систему более справедливой или только немного менее справедливой (подробности см. В вики).
  3. Прекратите, когда у вас есть что-то достаточно справедливое или у вас не хватает времени.

Это решение намного сильнее, чем многие предлагают «жадные» алгоритмы. Жадный алгоритм - это алгоритм, при котором вы постоянно добавляете самый крупный предмет «самому бедному» игроку.Пример тестового случая, когда жадный не работает: [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 в течение секунды. Однако я полагаю, что вы можете поиграться с методом имитации отжига точно так же.

9
ответ дан 8 December 2019 в 17:19
поделиться

как насчет этого:

упорядочить значения k. упорядочить игроков.

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

1
ответ дан 8 December 2019 в 17:19
поделиться

Жадное решение, предложенное несколькими людьми, кажется лучшим вариантом, я запускал его несколько раз с некоторыми случайными значениями, и, кажется, каждый раз все получается правильно.
Если это не оптимально, то, по крайней мере, очень близко, и оно выполняется за O (нм) или около того (я не могу сейчас потрудиться с математикой)
Реализация C #:

 статический список > Dist (значения int n, IList )
{
var result = new List > ();
для (int i = 1; i <= n; i ++)
result.Add (новый список  ());
var sortedValues ​​= values.OrderByDescending (val => val);
foreach (int val в sortedValues)
 {
var low = result.OrderBy (a => a.Sum ()). First ();
самый низкий.Добавить (val);
 }
вернуть результат;
}
2
ответ дан 8 December 2019 в 17:19
поделиться

РЕДАКТИРОВАТЬ:

Целью было использовать жадное решение с небольшим улучшением реализации, которое может быть прозрачным в C #:

static List<List<int>> Dist(int n, IList<int> values) 
{ 
    var result = new List<List<int>>(); 
    for (int i = 1; i <= n; i++) 
        result.Add(new List<int>()); 
    var sortedValues = values.OrderByDescending(val => val);//Assume the most efficient sorting algorithm - O(N log(N))
    foreach (int val in sortedValues) 
    { 
        var lowest = result.OrderBy(a => a.Sum()).First();//This can be done in O(M * log(n)) [M - size of sortedValues, n - size of result]
        lowest.Add(val); 
    } 
    return result; 
} 

Относительно этого этапа:

var lowest = result.OrderBy(a => a.Sum()).First();//This can be done in O(M * log(n)) [M - size of sortedValues, n - size of result]

Идея состоит в том, что список всегда отсортирован (в этот код делает OrderBy). В конце концов, эта сортировка не займет больше, чем O (log (n)) - потому что нам просто нужно ВСТАВИТЬ не более одного элемента в отсортированный список - что должно принимать то же самое, что и двоичный поиск. Поскольку нам нужно повторить эту фазу для sortedValues.Length раз, весь алгоритм выполняется за O (M * log (n)).

Таким образом, это можно перефразировать так: Повторяйте шаги ниже, пока не закончите значения Значения : 1. Добавьте наибольшую ценность к наименьшему игроку. 2. Проверьте, есть ли у этого игрока наименьшая сумма. 3. Если да, переходите к шагу 1. 4. Вставьте последнего полученного игрока в отсортированный список игроков

Шаг 4 - это шаг O (log (n)), поскольку список всегда отсортирован.

0
ответ дан 8 December 2019 в 17:19
поделиться

Несколько раз отдавайте доступный объект с наибольшим значением игроку, у которого наименьшее общее значение назначенных ему объектов.

1
ответ дан 8 December 2019 в 17:19
поделиться

30 ^ 6 не так уж и много (это меньше 1 миллиарда). Переберите все возможные варианты распределения и выберите тот, который является самым справедливым по любой мерке, которую вы определите.

0
ответ дан 8 December 2019 в 17:19
поделиться

Это прямая реализация ответа Джастина Пила:

M = 3
players = [[] for i in xrange(M)]

values = [10,4,3,1,1,1]
values.sort()
values.reverse()
for v in values:
    lowest=sorted(players, key=lambda x: sum(x))[0]
    lowest.append(v)

print players
print [sum(p) for p in players]

Я новичок в Python, но, кажется, все работает нормально. Этот пример выведет

[[10], [4, 1], [3, 1, 1]]
[10, 5, 5]
1
ответ дан 8 December 2019 в 17:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: