Меню
Главная
Случайная статья
Настройки
|
Задача разбиения множества чисел — это задача определения, можно ли данное мультимножество S положительных целых чисел разбить на два подмножества S1 и S2, таких, что сумма чисел из S1 равна сумме чисел из S2. Хотя задача разбиения чисел является NP-полной, существует решение псевдополиномиального времени методом динамического программирования и существуют эвристические алгоритмы решения для многих конкрентных задач либо оптимально, либо приближённо. По этой причине задачу называют "простейшей NP-трудной задачей"[1].
Существует оптимизационная версия задачи разбиения, в которой требуется разбить мультимножество S на два подмножества S1 и S2, таких, что разность между суммой элементов S1 и суммой элементов S2 минимальна. Оптимизационная версия является NP-трудной задачей, но на практике может быть решена эффективно[2].
Содержание
Примеры
Если дано множество S={3,1,1,2,2,1}, допустимым решением задачи разбиения являются два множества S1={1,1,1,2} и S2={2,3}. Оба множества имеют сумму 5, и они являются разбиением множества S. Заметим, что это решение не уникально. S1={3,1,1} и S2={2,2,1} является другим решением.
Не любое мультимножество положительных целых чисел имеет разбиение на две части с равными суммами. Пример такого множества — S={2,5}.
Алгоритм псевдополиномиального времени
Задача может быть решена с помощью динамического программирования, если размер множества и размер суммы целых чисел в множестве не слишком велики, чтобы требования в памяти не стали невыполнимыми.
Представим вход алгоритма как список вида:
- S=x1, ..., xn
Пусть N — число элементов в множестве S.
Пусть K — сумма всех элементов из множества S. То есть K=x1 + ... + xn. Мы построим алгоритм, который определяет, существует ли подмножество S, сумма элементов которого равна . Если подмножество существует, то:
- если K чётно, то остаток множества S также даст
- если K нечётно, остаток множества S даст сумму . Это лучшее возможное решение.
Рекуррентные соотношения
Мы хотим определить, существует ли подмножество S, сумма элементов которого равна . Пусть:
- p(i, j) принимает значение True, если среди { x1, ..., xj } существует такое подмножество, элементы которого в сумме дают i и False в противном случае.
Тогда p(, n) принимает значение True тогда и только тогда, когда существует подмножество S, сумма которого равна . Цель нашего алгоритма — вычислить p(, n). Для достижения этого мы имеем следующие рекуррентные формулы:
- p(i, j) принимает значение True, если либо p(i, j 1) принимает значение True, либо p(i xj, j 1) принимает значение True
- p(i, j) принимает значение False в противном случае
Причина этому следующая: существует некоторое подмножество S, сумма которого равна i для чисел
- x1, ..., xj
тогда и только тогда, когда одно из двух верно:
- существует подмножество { x1, ..., xj-1 }, дающее сумму i;
- существует подмножество { x1, ..., xj-1 }, дающее сумму i xj, поскольку xj + сумма этого подмножества=i.
Псевдополиномиальный алгоритм
Алгоритм строит таблицу размера на n, содержащую значения рекурсии, где — сумма значений, а — число элементов. Когда вся таблица заполнена, возвращаем . Ниже приведена таблица P. На рисунке синяя стрелка из одного блока в другой, если значение конечного блока может зависеть от значения исходного блока. Эта зависимость является свойством рекуррентного отношения.
INPUT: Список целых чисел S
OUTPUT: True, если S может быть разбито на два подмножества, имеющих одинаковые суммы
1 функция найти_разбиение(S):
2 n |S|
3 K sum(S)
4 P пустая булева таблица размера ( + 1) by (n + 1)
5 инициализируем верхнюю строку (P(0,x)) таблицы P значениями True
6 инициализируем крайний левый столбец (P(x, 0)) таблицы P, за исключением ячейки P(0, 0) значениями False
7 для i от 1 до
8 для j от 1 до n
9 если (i-S[j-1]) >= 0
10 P(i, j) P(i, j-1) или P(i-S[j-1], j-1)
11 иначе
12 P(i, j) P(i, j-1)
13 вернуть значение P(, n)
Пример
Ниже приведена таблица P для использованного выше множества S={3, 1, 1, 2, 2, 1}:
Анализ
Этот алгоритм работает за время O(K N), где
Алгоритм может быть распространён на задачу мультиразбиения на
Специальный случай задачи о сумме подмножеств
Задача о разбиении можно рассматривать как частный случай задачи о сумме подмножеств и решение методом псевдополиномиального времени динамического программирования, данное выше обобщается до решения задачи о сумме подмножеств.
Аппроксимирующие алгоритмы
Существуют некоторые эвристические алгоритмы для аппроксимации оптимизационной задачи разбиения. Они могут быть расширены до точных алгоритмов линейного времени[2].
Жадный алгоритм
Один из подходов к задаче, имитирующий способ ребёнка партнёра для игры, жадный алгоритм, который перебирает числа в порядке убывания и добавляет каждое число к меньшей сумме. Этот подход имеет время работы
Известно, что этот жадный подход даёт 76-аппроксимацию относительно оптимального решения оптимизационной версии. То есть, если вывод жадного алгоритма даёт два множестваs def find_partition(int_list):
"Разбиваем `int_list` на два множества с одинаковыми суммами "
A=set()
B=set()
for n in sorted(int_list, reverse=True):
if sum(A) < sum(B):
A.add(n)
else:
B.add(n)
return (A, B)
Алгоритм может быть распространён на случаи
Разностный алгоритм
Другой эвристический алгоритм — метод наибольшей разности (МНР, en:Largest Differencing Method, LDM)[6], который называется эвристическим методом Кармаркара–Карпа[2], по имени учёных, опубликовавших метод в 1982[7]. МНР имеет две фазы. Первая фаза алгоритма берёт два наибольших числа из входа и заменяет их разностью. Повторяем операцию, пока не останется одно число. Замена представляет решение разместить два числа в разные подмножества, но в какие множества эти числа размещаются, решение не принимается. В конце первой фазы оставшееся единственное число является разностью двух сумм подмножеств. На втором этапе строится актуальное решение[1].
Разностный эвристический алгоритм работает лучше, чем жадный алгоритм, но остаётся плохим для задач, в которых числа экспоненционально зависят от размера множества[1].
Следующий код на Java представляет первую фазу алгоритма Кармаркара – Карпа. Алгоритм использует кучу для эффективного поиска пары наибольших чисел среди оставшихся.
int karmarkarKarpPartition(int[] baseArr) {
// create max heap
PriorityQueue<Integer> heap=new PriorityQueue<Integer>(baseArr.length, REVERSE_INT_CMP);
for (int value : baseArr) {
heap.add(value);
}
while(heap.size() > 1) {
int val1=heap.poll();
int val2=heap.poll();
heap.add(val1 - val2);
}
return heap.poll();
}
Другие подходы
Существуют также алгоритмы с отсечением по времени[англ.], основанные на разностной эвристической схеме, которые сначала находят решение, полученное разностной эвристической схемой, затем находится поступательно лучшие решения, если время позволяет (возможен экспоненциальный рост времени работы для получения оптимального решения в худшем случае)[8][9].
Сложные случаи
Множества с только одним или отсутствием разбиений чаще всего наиболее сложны (или наиболее расточительны) для получения решения относительно размера входа. Если значения малы по сравнению с размером множества, хорошие разбиения наиболее вероятны. Известно, что задача претерпевает «фазовый переход», когда хорошие разбиения наиболее вероятны для одних множеств и маловероятны для других. Если m — число бит, необходимых для выражения любого числа из множества, а n — размер множества, то при задача чаще имеет много решений, а при задача чаще имеет одно решение, либо не имеет решения вообще. Когда n и m становятся больше, вероятность хорошего разбиения стремится к 1, а плохого к 0 соответственно. Этот факт первоначально основывался на эмпирических результатах Гента и Уолша[10], затем на методах статистической физики (Мертенс[11][12]) и, наконец, факт доказали Боргс, Чайес[англ.] и Питтель[13].
Варианты и обобщения
Существует задача о 3-разбиении множества чисел[англ.], которая ищет разбиение множества S на |S|/3 тройки, каждая тройка с той же суммой. Эта задача совершенно отличается от задачи разбиения и не имеет алгоритма с псевдополиномиальным временем работы, если только не P=NP[14].
Задача разбиения на несколько множеств обобщает оптимизационную версию задачи разбиения. Здесь целью является разбиение множества или мультимножества n целых чисел на заданное число k подмножеств, минимизируя разность между наименьшей и наибольшей суммой чисел в подмножествах[2].
Дальнейшие обобщения задачи разбиения можно найти в статье «Задача об упаковке в контейнеры».
Вероятностная версия
Связанная задача, в чём-то похожая на парадокс дней рождения, заключается в поиске размера входного множества, для которого вероятность существования решения равна 0,5 при условии, что каждый элемент множества случайно выбран между 1 и некоторым заданным значением.
Решение этой задачи может быть парадоксальным. Например, при выборе случайно целых чисел между 1 и одним миллионом, многие люди считают, что ответом будет тысячи, десятки тысяч, или даже сотни тысяч, хотя, на самом деле, правильным ответом будет примерно 23 (подробности — в статье Задача разбиения).
Примечания
- 1 2 3 Hayes, 2002.
- 1 2 3 4 5 Korf, 2009.
- 1 2 Graham, 1969, с. 416–429.
- Kellerer, Pferschy, Pisinger, 2004, с. 94.
- Martello, Toth, 1990, с. 105–136.
- Michiels, Korst, Aarts, 2003, с. 71–75.
- Karmarkar, Karp, 1982.
- Korf, 1998.
- Mertens, 1999.
- Gent, Walsh, 1996.
- Mertens, 1998.
- Mertens, 2001.
- Borgs, Chayes, Pittel, 2001.
- Garey, Johnson, 1979, с. 96–105.
Литература- Silvano Martello, Paolo Toth. 4 Subset-sum problem // Knapsack problems: Algorithms and computer interpretations. — Wiley-Interscience, 1990. — ISBN 0-471-92420-2.
- Ron L. Graham. Bounds on multiprocessor timing anomalies // SIAM J. Appl. Math.. — 1969. — Т. 17, вып. 2.
- Richard E. Korf. Multi-Way Number Partitioning // IJCAI. — 2009.
- Wil Michiels, Jan Korst, Emile Aarts. Performance ratios for the Karmarkar–Karp differencing method // Electronic Notes in Discrete Mathematics. — 2003. — Т. 13.
- Michael Garey, David Johnson. Computers and Intractability; A Guide to the Theory of NP-Completeness. — 1979. — ISBN 0-7167-1045-5.
- Hans Kellerer, Ulrich Pferschy, David Pisinger. Knapsack problems. — Springer, 2004. — С. 97. — ISBN 9783540402862.
- Brian Hayes. The Easiest NP Hard Problem // American Scientist. — 2002. — May–June.
- Narenda Karmarkar, Richard M. Karp. The Differencing Method of Set Partitioning. — Technical Report UCB/CSD 82/113. — University of California at Berkeley: Computer Science Division (EECS), 1982.
- Ian Gent, Toby Walsh. Phase Transitions and Annealed Theories: Number Partitioning as a Case Study. — John Wiley and Sons, 1996. — С. 170–174.
- Ian Gent, Toby Walsh. Analysis of Heuristics for Number Partitioning // Computational Intelligence. — 1998. — Т. 14, вып. 3. — С. 430–451. — doi:10.1111/0824-7935.00069.
- Stephan Mertens. Phase Transition in the Number Partitioning Problem // Physical Review Letters. — 1998. — Ноябрь (т. 81, вып. 20). — С. 4281. — doi:10.1103/PhysRevLett.81.4281. — . — arXiv:cond-mat/9807077.
- Stephan Mertens. A physicist's approach to number partitioning // Theoretical Computer Science. — 2001. — Т. 265, вып. 1-2. — С. 79–108. — doi:10.1016/S0304-3975(01)00153-0.
- Stephan Mertens. The Easiest Hard Problem: Number Partitioning // Computational complexity and statistical physics / Allon Percus, Gabriel Istrate, Cristopher Moore. — Oxford University Press US, 2006. — С. 125. — ISBN 9780195177374. — . — arXiv:cond-mat/0310317.
- Christian Borgs, Jennifer Chayes, Boris Pittel. Phase transition and finite-size scaling for the integer partitioning problem // Random Structures and Algorithms. — 2001. — Т. 19, вып. 3-4. — С. 247–288. — doi:10.1002/rsa.10004.
- Richard E. Korf. A complete anytime algorithm for number partitioning // Artificial Intelligence. — 1998. — Т. 106, вып. 2. — С. 181–203. — ISSN 0004-3702. — doi:10.1016/S0004-3702(98)00086-1.
- Stephan Mertens. A complete anytime algorithm for balanced number partitioning. — 1999. — . — arXiv:cs/9903011.
|
|