Разбиение списка целых чисел для минимизации разности их сумм
Задан список целых чисел l
, как я могу разбить его на 2 списка a
и b
таким образом, что d(a,b) = abs(sum(a) - sum(b))
является минимальным. Я знаю, что задача NP-полная, поэтому я ищу алгоритм псевдополиномиального времени, т. е. O(c*n)
, где c = sum(l map abs)
. Я посмотрел наWikipedia , но алгоритм там состоит в том, чтобы разбить его на точные половины, что является частным случаем того, что я ищу...
Править:
Чтобы уточнить, я ищу точные разделы a
и b
, а не только результирующий минимальная разница d(a, b)
Чтобы обобщить, что такое алгоритм псевдополиномиального времени для разбиения списка n
чисел на k
группы g1, g2 ...gk
такие, что (max(S) - min(S)).abs
как можно меньше, где S = [sum(g1), sum(g2), ... sum(gk)]
3 ответа:
Наивным, тривиальным и все еще псевдополиномиальным решением было бы использовать существующее решение для подмножества-суммы и повторить для
sum(array)/2
до 0 (и вернуть первое найденное).Сложность этого решения будет
O(W^2*n)
, гдеW
- сумма массива.Псевдокод:
for cand from sum(array)/2 to 0 descending: subset <- subsetSumSolver(array,cand) if subset != null: return subset
Выше будет возвращено максимальное подмножество, которое меньше / равно
sum(array)/2
, а другая часть является дополнением для возвращаемого подмножества.
Однако , динамическая программирования для подмножества-суммы должно быть достаточно.
Напомним, что формула такова:
f(0,i) = true f(x,0) = false | x != 0 f(x,i) = f(x-arr[i],i-1) OR f(x,i-1)
При построении матрицы вышеописанное фактически создает вам каждую строку со значением ниже начального
После того, как вы сгенерируете матрицу DP, просто найдите максимальное значениеx
, Если вы вводите сумму(массив)/2 - это в основном все значения.x
такое, чтоf(x,n)=true
, и это лучшее разбиение, которое вы можете получить.Сложность в этом случае равна
O(Wn)
Можно сформулировать это как задачу оптимизации целочисленного линейного программирования 0/1. Пусть wi-это I-е число, а xi-переменная 0/1, указывающая, входит ли wi в первый набор или нет. Затем вы хотите минимизировать сумму(Си Висконсин) - сумма((1 - ХІ) Висконсин) при условии
Сумма(ХІ Висконсин) >= сумма((1 - ХІ) Висконсин)
, а также при условии, что все xi равны 0 или 1. Было проведено много исследований по оптимизации решателей линейного программирования 0/1. Для большой общей суммы W это может быть улучшением по сравнению с O(W Н) псевдо-полиномиальное время представлен алгоритм, потому что W фактор-это страшно.
Моя первая мысль:
- сортировка списка целых чисел
- Создайте два пустых списка A и B
- при итерации от наибольшего целого числа к наименьшему целому числу...добавьте следующее целое число в список с наименьшей текущей суммой.
Это, конечно, не гарантирует вам наилучший результат, но вы можете ограничить результат, который он даст вам, размером самого большого целого числа в вашем списке