Алгоритм для поиска подмножества в двух наборах целых чисел, суммы которых соответствуют

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

Проблема похожа на проблема с подмножеством подмножества, за исключением того, что я ищу подмножества с обеих сторон.

Вот пример:

Список A {4, 5, 9, 10, 1}

Список B {21, 7, -4, 180}

Итак, единственное совпадение: {10, 1, 4, 9} <= > {21, 7, -4}

Кто-нибудь знает, существуют ли существующие алгоритмы для подобных проблем?

До сих пор единственное решение, которое у меня есть, - это подход с грубой силой, который пробует каждую комбинацию, но он выполняет в Экспоненциальном времени, и мне пришлось наложить жесткий предел на количество элементов, которые следует учитывать, чтобы избежать слишком длительного.

Единственное другое решение, о котором я могу думать, - запустить факториал в обоих списках и искать там равенства, но это все еще не очень эффективно и занимает экспоненциально больше, поскольку списки становятся больше.

Ответ 1

То, что говорили другие, верно:

  • Эта проблема NP-полная. Легкое сокращение - обычная подмножество. Вы можете показать это, заметив, что подмножество A сумм к подмножеству B (не оба пустых), только если непустое подмножество объединения A (-B) суммируется с нулем.

  • Эта проблема только слабо NP-полная, в том, что она является полиномом по размеру числа, но предполагается, что она экспоненциальна по своим логарифмам. Это означает, что проблема проще, чем может показаться прозвище "NP-complete".

  • Вы должны использовать динамическое программирование.

Итак, что я могу внести в эту дискуссию? Ну, код (в Perl):

@a = qw(4 5 9 10 1);
@b = qw(21 7 -4 180);
%a = sums( @a );
%b = sums( @b );
for $m ( keys %a ) {
    next unless exists $b{$m};
    next if $m == 0 and (@{$a{0}} == 0 or @{$b{0}} == 0);
    print "sum(@{$a{$m}}) = sum(@{$b{$m}})\n";
}

sub sums {
    my( @a ) = @_;
    my( $a, %a, %b );
    %a = ( 0 => [] );
    while( @a ) {
        %b = %a;
        $a = shift @a;
        for my $m ( keys %a ) {
            $b{$m+$a} = [@{$a{$m}},$a];
        }
    %a = %b;
    }
    return %a;
}

Он печатает

sum(4 5 9 10) = sum(21 7)
sum(4 9 10 1) = sum(21 7 -4)

поэтому, в частности, существует более одного решения, которое работает в вашей первоначальной проблеме!

Изменить. Пользователь-ити правильно указал, что это решение было неправильным и, что еще хуже, несколькими способами! Я очень сожалею об этом, и я, надеюсь, рассмотрел эти проблемы в новом коде выше. Тем не менее, есть еще одна проблема, а именно, что для любой конкретной подмножества-суммы она печатает только одно из возможных решений. В отличие от предыдущих проблем, которые были правильными ошибками, я бы классифицировал это как намеренное ограничение. Удачи и остерегайтесь ошибок!

Ответ 2

Как и проблема суммирования подмножеств, эта проблема слабо NP-полная, поэтому у нее есть решение, которое выполняется во временном многочлене (M), где M - сумма всех чисел, входящих в задачу пример. Вы можете добиться этого с помощью динамического программирования. Для каждого набора вы можете сгенерировать все возможные суммы, заполнив двумерную двоичную таблицу, где "true" at (k, m) означает, что подмножество sum m может быть достигнуто путем выбора некоторых элементов из первых k элементов множества.

Вы заполняете его итеративно - вы устанавливаете (k, m) на "true", если (k-1, m) установлено значение "true" (очевидно, если вы можете получить m из k-1 элементов, вы можете получить он из k элементов, не выбирая k-й), или если (k-1, md) установлено значение "true", где d - значение k-го элемента в наборе (случай, когда вы выбираете k-й элемент).

Заполнение таблицы дает вам все возможные суммы в последнем столбце (тот, который представляет весь набор). Сделайте это для обоих наборов и найдите общие суммы. Вы можете отменить фактические подмножества, представляющие решения, изменив процесс, который вы использовали для заполнения таблиц.

Ответ 3

Большое спасибо за все быстрые ответы!

Решение динамического программирования на самом деле не отличается от исчерпывающего утверждения, которое у нас есть сейчас, и я думаю, если нам нужно оптимальное решение, нам нужно рассмотреть все возможные комбинации, но время, необходимое для создания этого исчерпывающего списка сумм, тоже длинный.. Был ли быстрый тест и время, затрачиваемое на создание всех возможных сумм для х элементов, очень быстро перешло через 1 мин:

11 elements took - 0.015625 seconds
12 elements took - 0.015625 seconds
13 elements took - 0.046875 seconds
14 elements took - 0.109375 seconds
15 elements took - 0.171875 seconds
16 elements took - 0.359375 seconds
17 elements took - 0.765625 seconds
18 elements took - 1.609375 seconds
19 elements took - 3.40625 seconds
20 elements took - 7.15625 seconds
21 elements took - 14.96875 seconds
22 elements took - 31.40625 seconds
23 elements took - 65.875 seconds
24 elements took - 135.953125 seconds
25 elements took - 282.015625 seconds
26 elements took - 586.140625 seconds
27 elements took - 1250.421875 seconds
28 elements took - 2552.53125 seconds
29 elements took - 5264.34375 seconds

который для бизнес-задачи, которую мы пытаемся решить, на самом деле не приемлем. Я вернусь к чертежной доске и посмотрю, действительно ли нам нужно знать все решения или мы можем просто сделать это с одним ( наименьшее/наибольшее подмножество, например), и, надеюсь, это может помочь просто решить проблему и сделать мой алгоритм ожидаемым.

Спасибо всем тем же!

Ответ 4

сумма подмножества Np-полна, и вы можете полиномиально уменьшить свою проблему, поэтому ваша проблема тоже NP-complete.