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

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

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

вот пример:

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

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

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

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

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

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

4 ответов


то, что другие сказали верно:

  1. эта проблема NP-завершена. Простое сокращение-это обычная сумма подмножеств. Вы можете показать это, отметив, что подмножество a суммируется с подмножеством B (не оба пустые), только если непустое подмножество объединения (- B) суммируется с нулем.

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

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

Итак, что я вношу в эту дискуссию? Ну, код (в 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)

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

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


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

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

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


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

решение динамического программирования на самом деле не отличается от исчерпывающего подхода, который у нас есть прямо сейчас, и я думаю, что если нам нужно оптимальное решение, нам нужно рассмотреть каждую возможную комбинацию, но время, необходимое для создания этого исчерпывающего списка сумм, слишком длинное.. Сделал быстрый тест и время, необходимое для генерации всех возможных сумм для x количество элементов очень быстро перейти на 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

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

все равно спасибо!


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