Menemukan semua kemungkinan kombinasi angka untuk mencapai jumlah tertentu

232

Bagaimana Anda akan menguji semua kemungkinan kombinasi penambahan dari satu set Nangka sehingga mereka menambahkan hingga angka akhir yang diberikan?

Contoh singkat:

  • Set angka untuk ditambahkan: N = {1,5,22,15,0,...}
  • Hasil yang diinginkan: 12345
James P.
sumber
@ James - Saya pikir masalah Anda perlu diklarifikasi. Apa aturannya? Bisakah Anda memilih nomor apa saja? Angka apa yang ada di set? Apa kendala Anda?
jmort253
9
Artikel wikipedia ( en.wikipedia.org/wiki/Subset_sum_problem ) bahkan menyebutkan bahwa masalah ini merupakan pengantar yang bagus untuk kelas masalah NP-complete.
user57368
1
@ jmort253: Saya tidak berpikir ada kendala selain memiliki satu set bilangan bulat yang positif dan lebih rendah dari angka yang diberikan sebagai target. Kombinasi angka apa pun dapat digunakan. Itu bukan pekerjaan rumah tetapi jenis masalah yang akan Anda berikan untuk dipecahkan jika Anda melamar beberapa pekerjaan. Saya biasanya bisa memikirkan algoritma ketika diperlukan tetapi saya tidak yakin bagaimana cara melihat sesuatu seperti ini. Itu perlu diurai entah bagaimana (rekursif?).
James P.
3
@ James, Apakah Anda memerlukan kombinasi atau hanya sejumlah subset yang menambahkan hingga jumlah?
st0le
6
Bisakah kita menggunakan elemen yang sama dari set asli lebih dari sekali? Misalnya jika inputnya {1,2,3,5} dan target 10, apakah 5 + 5 = 10 solusi yang dapat diterima?
alampada

Jawaban:

248

Masalah ini dapat diselesaikan dengan kombinasi rekursif dari semua jumlah yang mungkin menyaring orang-orang yang mencapai target. Berikut adalah algoritma dalam Python:

def subset_sum(numbers, target, partial=[]):
    s = sum(partial)

    # check if the partial sum is equals to target
    if s == target: 
        print "sum(%s)=%s" % (partial, target)
    if s >= target:
        return  # if we reach the number why bother to continue

    for i in range(len(numbers)):
        n = numbers[i]
        remaining = numbers[i+1:]
        subset_sum(remaining, target, partial + [n]) 


if __name__ == "__main__":
    subset_sum([3,9,8,4,5,7,10],15)

    #Outputs:
    #sum([3, 8, 4])=15
    #sum([3, 5, 7])=15
    #sum([8, 7])=15
    #sum([5, 10])=15

Jenis algoritme ini dijelaskan dengan sangat baik dalam kuliah Abstrak Pemrograman Standford berikut - video ini sangat direkomendasikan untuk memahami bagaimana rekursi bekerja untuk menghasilkan permutasi solusi.

Edit

Di atas sebagai fungsi generator, membuatnya sedikit lebih bermanfaat. Membutuhkan Python 3.3+ karena yield from.

def subset_sum(numbers, target, partial=[], partial_sum=0):
    if partial_sum == target:
        yield partial
    if partial_sum >= target:
        return
    for i, n in enumerate(numbers):
        remaining = numbers[i + 1:]
        yield from subset_sum(remaining, target, partial + [n], partial_sum + n)

Berikut ini adalah versi Java dari algoritma yang sama:

package tmp;

import java.util.ArrayList;
import java.util.Arrays;

class SumSet {
    static void sum_up_recursive(ArrayList<Integer> numbers, int target, ArrayList<Integer> partial) {
       int s = 0;
       for (int x: partial) s += x;
       if (s == target)
            System.out.println("sum("+Arrays.toString(partial.toArray())+")="+target);
       if (s >= target)
            return;
       for(int i=0;i<numbers.size();i++) {
             ArrayList<Integer> remaining = new ArrayList<Integer>();
             int n = numbers.get(i);
             for (int j=i+1; j<numbers.size();j++) remaining.add(numbers.get(j));
             ArrayList<Integer> partial_rec = new ArrayList<Integer>(partial);
             partial_rec.add(n);
             sum_up_recursive(remaining,target,partial_rec);
       }
    }
    static void sum_up(ArrayList<Integer> numbers, int target) {
        sum_up_recursive(numbers,target,new ArrayList<Integer>());
    }
    public static void main(String args[]) {
        Integer[] numbers = {3,9,8,4,5,7,10};
        int target = 15;
        sum_up(new ArrayList<Integer>(Arrays.asList(numbers)),target);
    }
}

Itu persis heuristik yang sama. Java saya agak berkarat tapi saya pikir mudah dimengerti.

C # konversi solusi Java: (oleh @JeremyThompson)

public static void Main(string[] args)
{
    List<int> numbers = new List<int>() { 3, 9, 8, 4, 5, 7, 10 };
    int target = 15;
    sum_up(numbers, target);
}

private static void sum_up(List<int> numbers, int target)
{
    sum_up_recursive(numbers, target, new List<int>());
}

private static void sum_up_recursive(List<int> numbers, int target, List<int> partial)
{
    int s = 0;
    foreach (int x in partial) s += x;

    if (s == target)
        Console.WriteLine("sum(" + string.Join(",", partial.ToArray()) + ")=" + target);

    if (s >= target)
        return;

    for (int i = 0; i < numbers.Count; i++)
    {
        List<int> remaining = new List<int>();
        int n = numbers[i];
        for (int j = i + 1; j < numbers.Count; j++) remaining.Add(numbers[j]);

        List<int> partial_rec = new List<int>(partial);
        partial_rec.Add(n);
        sum_up_recursive(remaining, target, partial_rec);
    }
}

Solusi Ruby: (oleh @emaillenin)

def subset_sum(numbers, target, partial=[])
  s = partial.inject 0, :+
# check if the partial sum is equals to target

  puts "sum(#{partial})=#{target}" if s == target

  return if s >= target # if we reach the number why bother to continue

  (0..(numbers.length - 1)).each do |i|
    n = numbers[i]
    remaining = numbers.drop(i+1)
    subset_sum(remaining, target, partial + [n])
  end
end

subset_sum([3,9,8,4,5,7,10],15)

Sunting: diskusi kompleksitas

Seperti orang lain menyebutkan ini adalah masalah NP-hard . Ini dapat diselesaikan dalam waktu eksponensial O (2 ^ n), misalnya untuk n = 10 akan ada 1024 kemungkinan solusi. Jika target yang Anda coba jangkau berada dalam kisaran rendah maka algoritma ini berfungsi. Jadi misalnya:

subset_sum([1,2,3,4,5,6,7,8,9,10],100000) menghasilkan 1024 cabang karena target tidak pernah bisa menyaring solusi yang mungkin.

Di sisi lain subset_sum([1,2,3,4,5,6,7,8,9,10],10)hanya menghasilkan 175 cabang, karena target yang akan dicapai 10dapat menyaring banyak kombinasi.

Jika Ndan Targetmerupakan angka besar, seseorang harus pindah ke versi perkiraan solusi.

Manuel Salvadores
sumber
1
Optimasi Java: ArrayList <Integer> partial_rec = ArrayList baru <Integer> (sebagian); partial_rec.add (n); ini salinan sebagian. dan dengan demikian menambahkan O (N). Cara yang lebih baik adalah dengan hanya "partial.add (n)" melakukan rekursi dan kemudian "partial.remove (partial.size -1). Saya memutar ulang kode Anda untuk memastikan. Ini berfungsi dengan baik
Christian Bongiorno
4
Solusi ini tidak berfungsi untuk semua kasus. Pertimbangkan [1, 2, 0, 6, -3, 3], 3- ini hanya menghasilkan [1,2], [0,3], [3]sementara kasus yang hilang seperti[6, -3, 3]
LiraNuna
11
Ini juga tidak berfungsi untuk setiap kombinasi, misalnya [1, 2, 5], 5hanya keluaran [5], kapan [1, 1, 1, 1, 1], [2, 2, 1]dan [2, 1, 1, 1]solusi.
cbrad
3
@cbrad itu karena i+1di remaining = numbers[i+1:]. Sepertinya algoritma itu tidak memungkinkan duplikat.
Leonid Vasilev
1
@cbrad Untuk mendapatkan juga solusi termasuk duplikat seperti [1, 1, 3]lihat di stackoverflow.com/a/34971783/3684296 (Python)
Mesa
36

Solusi dari masalah ini telah diberikan jutaan kali di Internet. Masalahnya disebut Masalah perubahan koin . Satu dapat menemukan solusi di http://rosettacode.org/wiki/Count_the_coins dan model matematika itu di http://jaqm.ro/issues/volume-5,issue-2/pdfs/patterson_harmel.pdf (atau perubahan koin Google masalah ).

Omong-omong, solusi Scala oleh Tsagadai, menarik. Contoh ini menghasilkan 1 atau 0. Sebagai efek samping, ia mencantumkan pada konsol semua solusi yang mungkin. Ini menampilkan solusi, tetapi gagal membuatnya dapat digunakan dengan cara apa pun.

Agar bermanfaat, kode harus mengembalikan a List[List[Int]]untuk memungkinkan mendapatkan jumlah solusi (panjang daftar daftar), solusi "terbaik" (daftar terpendek), atau semua solusi yang mungkin.

Berikut ini sebuah contoh. Ini sangat tidak efisien, tetapi mudah dimengerti.

object Sum extends App {

  def sumCombinations(total: Int, numbers: List[Int]): List[List[Int]] = {

    def add(x: (Int, List[List[Int]]), y: (Int, List[List[Int]])): (Int, List[List[Int]]) = {
      (x._1 + y._1, x._2 ::: y._2)
    }

    def sumCombinations(resultAcc: List[List[Int]], sumAcc: List[Int], total: Int, numbers: List[Int]): (Int, List[List[Int]]) = {
      if (numbers.isEmpty || total < 0) {
        (0, resultAcc)
      } else if (total == 0) {
        (1, sumAcc :: resultAcc)
      } else {
        add(sumCombinations(resultAcc, sumAcc, total, numbers.tail), sumCombinations(resultAcc, numbers.head :: sumAcc, total - numbers.head, numbers))
      }
    }

    sumCombinations(Nil, Nil, total, numbers.sortWith(_ > _))._2
  }

  println(sumCombinations(15, List(1, 2, 5, 10)) mkString "\n")
}

Saat dijalankan, ini akan menampilkan:

List(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
List(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2)
List(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2)
List(1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2)
List(1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2)
List(1, 1, 1, 1, 1, 2, 2, 2, 2, 2)
List(1, 1, 1, 2, 2, 2, 2, 2, 2)
List(1, 2, 2, 2, 2, 2, 2, 2)
List(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5)
List(1, 1, 1, 1, 1, 1, 1, 1, 2, 5)
List(1, 1, 1, 1, 1, 1, 2, 2, 5)
List(1, 1, 1, 1, 2, 2, 2, 5)
List(1, 1, 2, 2, 2, 2, 5)
List(2, 2, 2, 2, 2, 5)
List(1, 1, 1, 1, 1, 5, 5)
List(1, 1, 1, 2, 5, 5)
List(1, 2, 2, 5, 5)
List(5, 5, 5)
List(1, 1, 1, 1, 1, 10)
List(1, 1, 1, 2, 10)
List(1, 2, 2, 10)
List(5, 10)

The sumCombinations()Fungsi dapat digunakan dengan sendirinya, dan hasilnya dapat dianalisis lebih lanjut untuk menampilkan "terbaik" solusi (daftar terpendek), atau jumlah solusi (jumlah daftar).

Perhatikan bahwa meskipun seperti ini, persyaratan mungkin tidak sepenuhnya dipenuhi. Mungkin terjadi bahwa urutan setiap daftar dalam solusi menjadi signifikan. Dalam kasus seperti itu, setiap daftar harus diduplikasi sebanyak waktu karena ada kombinasi unsur-unsurnya. Atau kita mungkin hanya tertarik pada kombinasi yang berbeda.

Sebagai contoh, kami dapat mempertimbangkan bahwa List(5, 10)harus memberikan dua kombinasi: List(5, 10)dan List(10, 5). Untuk List(5, 5, 5)itu bisa memberikan tiga kombinasi atau satu saja, tergantung persyaratannya. Untuk bilangan bulat, tiga permutasi setara, tetapi jika kita berurusan dengan koin, seperti dalam "masalah perubahan koin", mereka tidak.

Juga tidak disebutkan dalam persyaratan adalah pertanyaan apakah masing-masing nomor (atau koin) dapat digunakan hanya sekali atau berkali-kali. Kita dapat (dan kita harus!) Menggeneralisasikan masalah ke daftar daftar kejadian masing-masing nomor. Ini diterjemahkan dalam kehidupan nyata menjadi "apa cara yang mungkin untuk menghasilkan sejumlah uang dengan satu set koin (dan bukan satu set nilai koin)". Masalah aslinya hanyalah kasus khusus dari kasus ini, di mana kami memiliki sebanyak mungkin setiap koin yang diperlukan untuk membuat jumlah total dengan setiap nilai koin tunggal.

Pierre-Yves Saumont
sumber
14
Masalah ini tidak persis sama dengan masalah perubahan koin. OP meminta semua kombinasi, bukan hanya minimal. Dan, mungkin, bilangan bulat di set bisa negatif. Karenanya, optimasi tertentu dari masalah perubahan koin tidak dimungkinkan dengan masalah ini.
ThomasMcLeod
5
dan juga masalah ini memungkinkan pengulangan item, saya tidak yakin OP menginginkan ini, tetapi lebih merupakan masalah ransel
caub
34

Dalam Haskell :

filter ((==) 12345 . sum) $ subsequences [1,5,22,15,0,..]

Dan J :

(]#~12345=+/@>)(]<@#~[:#:@i.2^#)1 5 22 15 0 ...

Seperti yang mungkin Anda perhatikan, keduanya mengambil pendekatan yang sama dan membagi masalah menjadi dua bagian: menghasilkan setiap anggota set daya, dan memeriksa jumlah masing-masing anggota untuk target.

Ada solusi lain tetapi ini yang paling mudah.

Apakah Anda memerlukan bantuan dengan salah satunya, atau menemukan pendekatan yang berbeda?

singkat
sumber
3
Wow, itu kode yang cukup ringkas. Saya baik-baik saja dengan jawaban Anda. Saya pikir saya hanya perlu membaca sedikit tentang algoritma secara umum. Saya akan melihat sintaks dari dua bahasa karena Anda telah memicu rasa ingin tahu saya.
James P.
Saya baru saja menginstal Haskell untuk mencoba ini, pasti tidak bisa hanya menempelkannya dan menjalankannya, not in scope: 'subsequences'ada petunjuk?
Hart CO
4
@ Hartco agak terlambat ke pesta, tetapiimport Data.List
Jir
28

Versi Javascript:

function subsetSum(numbers, target, partial) {
  var s, n, remaining;

  partial = partial || [];

  // sum partial
  s = partial.reduce(function (a, b) {
    return a + b;
  }, 0);

  // check if the partial sum is equals to target
  if (s === target) {
    console.log("%s=%s", partial.join("+"), target)
  }

  if (s >= target) {
    return;  // if we reach the number why bother to continue
  }

  for (var i = 0; i < numbers.length; i++) {
    n = numbers[i];
    remaining = numbers.slice(i + 1);
    subsetSum(remaining, target, partial.concat([n]));
  }
}

subsetSum([3,9,8,4,5,7,10],15);

// output:
// 3+8+4=15
// 3+5+7=15
// 8+7=15
// 5+10=15

rbarilani
sumber
Kode memiliki kesalahan dalam sepotong, harus remaining = numbers.slice(); remaining.slice(i + 1);dinyatakan numbers.slice(i + 1);mengubah nomor array yang
Emeeus
@Emeeus, saya pikir itu tidak benar. slicemengembalikan salinan (dangkal), itu tidak mengubah numbersarray.
Dario Seidl
@DarioSeidl ya, slice mengembalikan salinan, itu tidak mengubah array, itu intinya, itu sebabnya jika Anda tidak menetapkannya ke dalam variabel Anda tidak mengubahnya. Dalam hal ini, seperti yang saya mengerti kita harus melewati versi yang dimodifikasi, bukan yang asli. Lihat ini jsfiddle.net/che06t3w/1
Emeeus
1
@Redu ... misalnya cara mudah untuk melakukannya adalah, kita dapat sedikit memodifikasi algoritme dan menggunakan fungsi-dalam: jsbin.com/lecokaw/edit?js,console
rbarilani
1
Kode yang diberikan tidak serta merta mendapatkan semua kombinasi .. misalnya meletakkan [1,2], 3 hanya akan mengembalikan 1 + 2 = 3 bukan 1 + 1 + 1 atau 2 + 1
JuicY_Burrito
12

Versi C ++ dari algoritma yang sama

#include <iostream>
#include <list>
void subset_sum_recursive(std::list<int> numbers, int target, std::list<int> partial)
{
        int s = 0;
        for (std::list<int>::const_iterator cit = partial.begin(); cit != partial.end(); cit++)
        {
            s += *cit;
        }
        if(s == target)
        {
                std::cout << "sum([";

                for (std::list<int>::const_iterator cit = partial.begin(); cit != partial.end(); cit++)
                {
                    std::cout << *cit << ",";
                }
                std::cout << "])=" << target << std::endl;
        }
        if(s >= target)
            return;
        int n;
        for (std::list<int>::const_iterator ai = numbers.begin(); ai != numbers.end(); ai++)
        {
            n = *ai;
            std::list<int> remaining;
            for(std::list<int>::const_iterator aj = ai; aj != numbers.end(); aj++)
            {
                if(aj == ai)continue;
                remaining.push_back(*aj);
            }
            std::list<int> partial_rec=partial;
            partial_rec.push_back(n);
            subset_sum_recursive(remaining,target,partial_rec);

        }
}

void subset_sum(std::list<int> numbers,int target)
{
    subset_sum_recursive(numbers,target,std::list<int>());
}
int main()
{
    std::list<int> a;
    a.push_back (3); a.push_back (9); a.push_back (8);
    a.push_back (4);
    a.push_back (5);
    a.push_back (7);
    a.push_back (10);
    int n = 15;
    //std::cin >> n;
    subset_sum(a, n);
    return 0;
}
smac89
sumber
11

C # versi jawaban kode @msalvadores

void Main()
{
    int[] numbers = {3,9,8,4,5,7,10};
    int target = 15;
    sum_up(new List<int>(numbers.ToList()),target);
}

static void sum_up_recursive(List<int> numbers, int target, List<int> part)
{
   int s = 0;
   foreach (int x in part)
   {
       s += x;
   }
   if (s == target)
   {
        Console.WriteLine("sum(" + string.Join(",", part.Select(n => n.ToString()).ToArray()) + ")=" + target);
   }
   if (s >= target)
   {
        return;
   }
   for (int i = 0;i < numbers.Count;i++)
   {
         var remaining = new List<int>();
         int n = numbers[i];
         for (int j = i + 1; j < numbers.Count;j++)
         {
             remaining.Add(numbers[j]);
         }
         var part_rec = new List<int>(part);
         part_rec.Add(n);
         sum_up_recursive(remaining,target,part_rec);
   }
}
static void sum_up(List<int> numbers, int target)
{
    sum_up_recursive(numbers,target,new List<int>());
}
smac89
sumber
4

Saya pikir saya akan menggunakan jawaban dari pertanyaan ini tetapi saya tidak bisa, jadi inilah jawaban saya. Itu menggunakan versi modifikasi dari jawaban dalam Struktur dan Interpretasi Program Komputer . Saya pikir ini adalah solusi rekursif yang lebih baik dan harus lebih menyenangkan para puritan.

Jawaban saya ada di Scala (dan minta maaf jika Scala saya payah, saya baru saja mulai mempelajarinya). The findSumCombinations kegilaan adalah untuk mengurutkan dan unik daftar asli untuk rekursi untuk mencegah korban penipuan.

def findSumCombinations(target: Int, numbers: List[Int]): Int = {
  cc(target, numbers.distinct.sortWith(_ < _), List())
}

def cc(target: Int, numbers: List[Int], solution: List[Int]): Int = {
  if (target == 0) {println(solution); 1 }
  else if (target < 0 || numbers.length == 0) 0
  else 
    cc(target, numbers.tail, solution) 
    + cc(target - numbers.head, numbers, numbers.head :: solution)
}

Untuk menggunakannya:

 > findSumCombinations(12345, List(1,5,22,15,0,..))
 * Prints a whole heap of lists that will sum to the target *
Tsagadai
sumber
4
Thank you.. ephemient

Saya telah mengonversi logika di atas dari python ke php ..

<?php
$data = array(array(2,3,5,10,15),array(4,6,23,15,12),array(23,34,12,1,5));
$maxsum = 25;

print_r(bestsum($data,$maxsum));  //function call

function bestsum($data,$maxsum)
{
$res = array_fill(0, $maxsum + 1, '0');
$res[0] = array();              //base case
foreach($data as $group)
{
 $new_res = $res;               //copy res

  foreach($group as $ele)
  {
    for($i=0;$i<($maxsum-$ele+1);$i++)
    {   
        if($res[$i] != 0)
        {
            $ele_index = $i+$ele;
            $new_res[$ele_index] = $res[$i];
            $new_res[$ele_index][] = $ele;
        }
    }
  }

  $res = $new_res;
}

 for($i=$maxsum;$i>0;$i--)
  {
    if($res[$i]!=0)
    {
        return $res[$i];
        break;
    }
  }
return array();
}
?>
bala
sumber
4

Solusi python lainnya adalah dengan menggunakan itertools.combinationsmodul sebagai berikut:

#!/usr/local/bin/python

from itertools import combinations

def find_sum_in_list(numbers, target):
    results = []
    for x in range(len(numbers)):
        results.extend(
            [   
                combo for combo in combinations(numbers ,x)  
                    if sum(combo) == target
            ]   
        )   

    print results

if __name__ == "__main__":
    find_sum_in_list([3,9,8,4,5,7,10], 15)

Keluaran: [(8, 7), (5, 10), (3, 8, 4), (3, 5, 7)]

brainasium
sumber
itu tidak berfungsi misalnya: find_sum_in_list (range (0,8), 4). Ditemukan: [(4,), (0, 4), (1, 3), (0, 1, 3)]. Tapi (2, 2) juga merupakan pilihan!
Andre Araujo
@AndreAraujo: tidak masuk akal untuk menggunakan 0, tetapi jika Anda menggunakan (1,8) maka itertools.combinations_with_replacement berfungsi dan juga menghasilkan 2,2.
Rubenisme
@ Rubenisme Ya, bung! Masalahnya adalah penggantinya! Terima kasih! ;-)
Andre Araujo
4

Inilah solusi di R

subset_sum = function(numbers,target,partial=0){
  if(any(is.na(partial))) return()
  s = sum(partial)
  if(s == target) print(sprintf("sum(%s)=%s",paste(partial[-1],collapse="+"),target))
  if(s > target) return()
  for( i in seq_along(numbers)){
    n = numbers[i]
    remaining = numbers[(i+1):length(numbers)]
    subset_sum(remaining,target,c(partial,n))
  }
}
Menandai
sumber
Saya mencari solusi dalam R, tetapi yang ini tidak berhasil untuk saya. Misalnya, subset_sum(numbers = c(1:2), target = 5)kembali "sum(1+2+2)=5". Tetapi kombinasi 1 + 1 + 1 + 1 + 1 tidak ada. Menetapkan target ke angka yang lebih tinggi (mis. 20) bahkan lebih banyak kombinasi yang hilang.
Frederick
Apa yang Anda gambarkan bukanlah fungsi yang dimaksudkan untuk dikembalikan. Lihatlah jawaban yang diterima. Fakta bahwa 2 diulang dua kali adalah artefak tentang bagaimana R menghasilkan dan mensubseteri seri, bukan perilaku yang dimaksudkan.
Tandai
subset_sum(1:2, 4)harus mengembalikan tidak ada solusi karena tidak ada kombinasi 1 dan 2 yang menambah 4. Apa yang perlu ditambahkan ke fungsi saya adalah pelarian jika ilebih besar dari panjangnyanumbers
Tandai
3

Berikut adalah versi Java yang cocok untuk N kecil dan jumlah target yang sangat besar, ketika kompleksitas O(t*N)(solusi dinamis) lebih besar daripada algoritma eksponensial. Versi saya menggunakan pertemuan di serangan tengah, bersama dengan sedikit pergeseran untuk mengurangi kompleksitas dari naif klasik O(n*2^n)ke O(2^(n/2)).

Jika Anda ingin menggunakan ini untuk set dengan antara 32 dan 64 elemen, Anda harus mengubah intyang mewakili subset saat ini di fungsi langkah ke longwalaupun kinerja jelas akan menurun secara drastis seiring dengan meningkatnya ukuran set. Jika Anda ingin menggunakan ini untuk set dengan jumlah elemen ganjil, Anda harus menambahkan 0 ke set untuk membuatnya bernomor genap.

import java.util.ArrayList;
import java.util.List;

public class SubsetSumMiddleAttack {
    static final int target = 100000000;
    static final int[] set = new int[]{ ... };

    static List<Subset> evens = new ArrayList<>();
    static List<Subset> odds = new ArrayList<>();

    static int[][] split(int[] superSet) {
        int[][] ret = new int[2][superSet.length / 2]; 

        for (int i = 0; i < superSet.length; i++) ret[i % 2][i / 2] = superSet[i];

        return ret;
    }

    static void step(int[] superSet, List<Subset> accumulator, int subset, int sum, int counter) {
        accumulator.add(new Subset(subset, sum));
        if (counter != superSet.length) {
            step(superSet, accumulator, subset + (1 << counter), sum + superSet[counter], counter + 1);
            step(superSet, accumulator, subset, sum, counter + 1);
        }
    }

    static void printSubset(Subset e, Subset o) {
        String ret = "";
        for (int i = 0; i < 32; i++) {
            if (i % 2 == 0) {
                if ((1 & (e.subset >> (i / 2))) == 1) ret += " + " + set[i];
            }
            else {
                if ((1 & (o.subset >> (i / 2))) == 1) ret += " + " + set[i];
            }
        }
        if (ret.startsWith(" ")) ret = ret.substring(3) + " = " + (e.sum + o.sum);
        System.out.println(ret);
    }

    public static void main(String[] args) {
        int[][] superSets = split(set);

        step(superSets[0], evens, 0,0,0);
        step(superSets[1], odds, 0,0,0);

        for (Subset e : evens) {
            for (Subset o : odds) {
                if (e.sum + o.sum == target) printSubset(e, o);
            }
        }
    }
}

class Subset {
    int subset;
    int sum;

    Subset(int subset, int sum) {
        this.subset = subset;
        this.sum = sum;
    }
}
jimpudar
sumber
3

Ini mirip dengan masalah perubahan koin

public class CoinCount 
{   
public static void main(String[] args)
{
    int[] coins={1,4,6,2,3,5};
    int count=0;

    for (int i=0;i<coins.length;i++)
    {
        count=count+Count(9,coins,i,0);
    }
    System.out.println(count);
}

public static int Count(int Sum,int[] coins,int index,int curSum)
{
    int count=0;

    if (index>=coins.length)
        return 0;

    int sumNow=curSum+coins[index];
    if (sumNow>Sum)
        return 0;
    if (sumNow==Sum)
        return 1;

    for (int i= index+1;i<coins.length;i++)
        count+=Count(Sum,coins,i,sumNow);

    return count;       
}
}
DJ
sumber
2

Algoritma yang sangat efisien menggunakan tabel yang saya tulis di c ++ pasangan beberapa tahun yang lalu.

Jika Anda mengatur PRINT 1, ia akan mencetak semua kombinasi (tetapi itu tidak akan menggunakan metode yang efisien).

Sangat efisien sehingga menghitung lebih dari 10 ^ 14 kombinasi dalam waktu kurang dari 10 ms.

#include <stdio.h>
#include <stdlib.h>
//#include "CTime.h"

#define SUM 300
#define MAXNUMsSIZE 30

#define PRINT 0


long long CountAddToSum(int,int[],int,const int[],int);
void printr(const int[], int);
long long table1[SUM][MAXNUMsSIZE];

int main()
{
    int Nums[]={3,4,5,6,7,9,13,11,12,13,22,35,17,14,18,23,33,54};
    int sum=SUM;
    int size=sizeof(Nums)/sizeof(int);
    int i,j,a[]={0};
    long long N=0;
    //CTime timer1;

    for(i=0;i<SUM;++i) 
        for(j=0;j<MAXNUMsSIZE;++j) 
            table1[i][j]=-1;

    N = CountAddToSum(sum,Nums,size,a,0); //algorithm
    //timer1.Get_Passd();

    //printf("\nN=%lld time=%.1f ms\n", N,timer1.Get_Passd());
    printf("\nN=%lld \n", N);
    getchar();
    return 1;
}

long long CountAddToSum(int s, int arr[],int arrsize, const int r[],int rsize)
{
    static int totalmem=0, maxmem=0;
    int i,*rnew;
    long long result1=0,result2=0;

    if(s<0) return 0;
    if (table1[s][arrsize]>0 && PRINT==0) return table1[s][arrsize];
    if(s==0)
    {
        if(PRINT) printr(r, rsize);
        return 1;
    }
    if(arrsize==0) return 0;

    //else
    rnew=(int*)malloc((rsize+1)*sizeof(int));

    for(i=0;i<rsize;++i) rnew[i]=r[i]; 
    rnew[rsize]=arr[arrsize-1];

    result1 =  CountAddToSum(s,arr,arrsize-1,rnew,rsize);
    result2 =  CountAddToSum(s-arr[arrsize-1],arr,arrsize,rnew,rsize+1);
    table1[s][arrsize]=result1+result2;
    free(rnew);

    return result1+result2;

}

void printr(const int r[], int rsize)
{
    int lastr=r[0],count=0,i;
    for(i=0; i<rsize;++i) 
    {
        if(r[i]==lastr)
            count++;
        else
        {
            printf(" %d*%d ",count,lastr);
            lastr=r[i];
            count=1;
        }
    }
    if(r[i-1]==lastr) printf(" %d*%d ",count,lastr);

    printf("\n");

}
Mendi Barel
sumber
hai yang disana! Saya perlu kode untuk melakukan sesuatu seperti itu, temukan semua kemungkinan jumlah set 6 angka dalam daftar 60 angka. Jumlahnya harus dalam kisaran min 180, maks 191. Bisakah kode itu disesuaikan untuk itu? Di mana menjalankan kode itu di cloud? Saya mencoba tanpa hasil di Codenvy
defreturn
2

Versi Excel VBA di bawah ini. Saya perlu menerapkan ini dalam VBA (bukan preferensi saya, jangan menilai saya!), Dan menggunakan jawaban pada halaman ini untuk pendekatan. Saya mengunggah kalau-kalau orang lain juga membutuhkan versi VBA.

Option Explicit

Public Sub SumTarget()
    Dim numbers(0 To 6)  As Long
    Dim target As Long

    target = 15
    numbers(0) = 3: numbers(1) = 9: numbers(2) = 8: numbers(3) = 4: numbers(4) = 5
    numbers(5) = 7: numbers(6) = 10

    Call SumUpTarget(numbers, target)
End Sub

Public Sub SumUpTarget(numbers() As Long, target As Long)
    Dim part() As Long
    Call SumUpRecursive(numbers, target, part)
End Sub

Private Sub SumUpRecursive(numbers() As Long, target As Long, part() As Long)

    Dim s As Long, i As Long, j As Long, num As Long
    Dim remaining() As Long, partRec() As Long
    s = SumArray(part)

    If s = target Then Debug.Print "SUM ( " & ArrayToString(part) & " ) = " & target
    If s >= target Then Exit Sub

    If (Not Not numbers) <> 0 Then
        For i = 0 To UBound(numbers)
            Erase remaining()
            num = numbers(i)
            For j = i + 1 To UBound(numbers)
                AddToArray remaining, numbers(j)
            Next j
            Erase partRec()
            CopyArray partRec, part
            AddToArray partRec, num
            SumUpRecursive remaining, target, partRec
        Next i
    End If

End Sub

Private Function ArrayToString(x() As Long) As String
    Dim n As Long, result As String
    result = "{" & x(n)
    For n = LBound(x) + 1 To UBound(x)
        result = result & "," & x(n)
    Next n
    result = result & "}"
    ArrayToString = result
End Function

Private Function SumArray(x() As Long) As Long
    Dim n As Long
    SumArray = 0
    If (Not Not x) <> 0 Then
        For n = LBound(x) To UBound(x)
            SumArray = SumArray + x(n)
        Next n
    End If
End Function

Private Sub AddToArray(arr() As Long, x As Long)
    If (Not Not arr) <> 0 Then
        ReDim Preserve arr(0 To UBound(arr) + 1)
    Else
        ReDim Preserve arr(0 To 0)
    End If
    arr(UBound(arr)) = x
End Sub

Private Sub CopyArray(destination() As Long, source() As Long)
    Dim n As Long
    If (Not Not source) <> 0 Then
        For n = 0 To UBound(source)
                AddToArray destination, source(n)
        Next n
    End If
End Sub

Output (ditulis ke jendela Segera) harus:

SUM ( {3,8,4} ) = 15
SUM ( {3,5,7} ) = 15
SUM ( {8,7} ) = 15
SUM ( {5,10} ) = 15 
CodingQuant
sumber
2

Ini adalah versi yang lebih baik dengan pemformatan keluaran yang lebih baik dan fitur C ++ 11:

void subset_sum_rec(std::vector<int> & nums, const int & target, std::vector<int> & partialNums) 
{
    int currentSum = std::accumulate(partialNums.begin(), partialNums.end(), 0);
    if (currentSum > target)
        return;
    if (currentSum == target) 
    {
        std::cout << "sum([";
        for (auto it = partialNums.begin(); it != std::prev(partialNums.end()); ++it)
            cout << *it << ",";
        cout << *std::prev(partialNums.end());
        std::cout << "])=" << target << std::endl;
    }
    for (auto it = nums.begin(); it != nums.end(); ++it) 
    {
        std::vector<int> remaining;
        for (auto it2 = std::next(it); it2 != nums.end(); ++it2)
            remaining.push_back(*it2);

        std::vector<int> partial = partialNums;
        partial.push_back(*it);
        subset_sum_rec(remaining, target, partial);
    }
}
Andrushenko Alexander
sumber
2

Versi Java non-rekursif yang terus menambahkan elemen dan mendistribusikannya di antara nilai yang mungkin. 0Diabaikan dan berfungsi untuk daftar tetap (apa yang Anda berikan adalah apa yang dapat Anda mainkan) atau daftar angka yang berulang.

import java.util.*;

public class TestCombinations {

    public static void main(String[] args) {
        ArrayList<Integer> numbers = new ArrayList<>(Arrays.asList(0, 1, 2, 2, 5, 10, 20));
        LinkedHashSet<Integer> targets = new LinkedHashSet<Integer>() {{
            add(4);
            add(10);
            add(25);
        }};

        System.out.println("## each element can appear as many times as needed");
        for (Integer target: targets) {
            Combinations combinations = new Combinations(numbers, target, true);
            combinations.calculateCombinations();
            for (String solution: combinations.getCombinations()) {
                System.out.println(solution);
            }
        }

        System.out.println("## each element can appear only once");
        for (Integer target: targets) {
            Combinations combinations = new Combinations(numbers, target, false);
            combinations.calculateCombinations();
            for (String solution: combinations.getCombinations()) {
                System.out.println(solution);
            }
        }
    }

    public static class Combinations {
        private boolean allowRepetitions;
        private int[] repetitions;
        private ArrayList<Integer> numbers;
        private Integer target;
        private Integer sum;
        private boolean hasNext;
        private Set<String> combinations;

        /**
         * Constructor.
         *
         * @param numbers Numbers that can be used to calculate the sum.
         * @param target  Target value for sum.
         */
        public Combinations(ArrayList<Integer> numbers, Integer target) {
            this(numbers, target, true);
        }

        /**
         * Constructor.
         *
         * @param numbers Numbers that can be used to calculate the sum.
         * @param target  Target value for sum.
         */
        public Combinations(ArrayList<Integer> numbers, Integer target, boolean allowRepetitions) {
            this.allowRepetitions = allowRepetitions;
            if (this.allowRepetitions) {
                Set<Integer> numbersSet = new HashSet<>(numbers);
                this.numbers = new ArrayList<>(numbersSet);
            } else {
                this.numbers = numbers;
            }
            this.numbers.removeAll(Arrays.asList(0));
            Collections.sort(this.numbers);

            this.target = target;
            this.repetitions = new int[this.numbers.size()];
            this.combinations = new LinkedHashSet<>();

            this.sum = 0;
            if (this.repetitions.length > 0)
                this.hasNext = true;
            else
                this.hasNext = false;
        }

        /**
         * Calculate and return the sum of the current combination.
         *
         * @return The sum.
         */
        private Integer calculateSum() {
            this.sum = 0;
            for (int i = 0; i < repetitions.length; ++i) {
                this.sum += repetitions[i] * numbers.get(i);
            }
            return this.sum;
        }

        /**
         * Redistribute picks when only one of each number is allowed in the sum.
         */
        private void redistribute() {
            for (int i = 1; i < this.repetitions.length; ++i) {
                if (this.repetitions[i - 1] > 1) {
                    this.repetitions[i - 1] = 0;
                    this.repetitions[i] += 1;
                }
            }
            if (this.repetitions[this.repetitions.length - 1] > 1)
                this.repetitions[this.repetitions.length - 1] = 0;
        }

        /**
         * Get the sum of the next combination. When 0 is returned, there's no other combinations to check.
         *
         * @return The sum.
         */
        private Integer next() {
            if (this.hasNext && this.repetitions.length > 0) {
                this.repetitions[0] += 1;
                if (!this.allowRepetitions)
                    this.redistribute();
                this.calculateSum();

                for (int i = 0; i < this.repetitions.length && this.sum != 0; ++i) {
                    if (this.sum > this.target) {
                        this.repetitions[i] = 0;
                        if (i + 1 < this.repetitions.length) {
                            this.repetitions[i + 1] += 1;
                            if (!this.allowRepetitions)
                                this.redistribute();
                        }
                        this.calculateSum();
                    }
                }

                if (this.sum.compareTo(0) == 0)
                    this.hasNext = false;
            }
            return this.sum;
        }

        /**
         * Calculate all combinations whose sum equals target.
         */
        public void calculateCombinations() {
            while (this.hasNext) {
                if (this.next().compareTo(target) == 0)
                    this.combinations.add(this.toString());
            }
        }

        /**
         * Return all combinations whose sum equals target.
         *
         * @return Combinations as a set of strings.
         */
        public Set<String> getCombinations() {
            return this.combinations;
        }

        @Override
        public String toString() {
            StringBuilder stringBuilder = new StringBuilder("" + sum + ": ");
            for (int i = 0; i < repetitions.length; ++i) {
                for (int j = 0; j < repetitions[i]; ++j) {
                    stringBuilder.append(numbers.get(i) + " ");
                }
            }
            return stringBuilder.toString();
        }
    }
}

Input sampel:

numbers: 0, 1, 2, 2, 5, 10, 20
targets: 4, 10, 25

Output sampel:

## each element can appear as many times as needed
4: 1 1 1 1 
4: 1 1 2 
4: 2 2 
10: 1 1 1 1 1 1 1 1 1 1 
10: 1 1 1 1 1 1 1 1 2 
10: 1 1 1 1 1 1 2 2 
10: 1 1 1 1 2 2 2 
10: 1 1 2 2 2 2 
10: 2 2 2 2 2 
10: 1 1 1 1 1 5 
10: 1 1 1 2 5 
10: 1 2 2 5 
10: 5 5 
10: 10 
25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 
25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 
25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 
25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 
25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 
25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 
25: 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 
25: 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 
25: 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 
25: 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 
25: 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 
25: 1 1 1 2 2 2 2 2 2 2 2 2 2 2 
25: 1 2 2 2 2 2 2 2 2 2 2 2 2 
25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 5 
25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 5 
25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 5 
25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 5 
25: 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 5 
25: 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 5 
25: 1 1 1 1 1 1 1 1 2 2 2 2 2 2 5 
25: 1 1 1 1 1 1 2 2 2 2 2 2 2 5 
25: 1 1 1 1 2 2 2 2 2 2 2 2 5 
25: 1 1 2 2 2 2 2 2 2 2 2 5 
25: 2 2 2 2 2 2 2 2 2 2 5 
25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 5 5 
25: 1 1 1 1 1 1 1 1 1 1 1 1 1 2 5 5 
25: 1 1 1 1 1 1 1 1 1 1 1 2 2 5 5 
25: 1 1 1 1 1 1 1 1 1 2 2 2 5 5 
25: 1 1 1 1 1 1 1 2 2 2 2 5 5 
25: 1 1 1 1 1 2 2 2 2 2 5 5 
25: 1 1 1 2 2 2 2 2 2 5 5 
25: 1 2 2 2 2 2 2 2 5 5 
25: 1 1 1 1 1 1 1 1 1 1 5 5 5 
25: 1 1 1 1 1 1 1 1 2 5 5 5 
25: 1 1 1 1 1 1 2 2 5 5 5 
25: 1 1 1 1 2 2 2 5 5 5 
25: 1 1 2 2 2 2 5 5 5 
25: 2 2 2 2 2 5 5 5 
25: 1 1 1 1 1 5 5 5 5 
25: 1 1 1 2 5 5 5 5 
25: 1 2 2 5 5 5 5 
25: 5 5 5 5 5 
25: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 10 
25: 1 1 1 1 1 1 1 1 1 1 1 1 1 2 10 
25: 1 1 1 1 1 1 1 1 1 1 1 2 2 10 
25: 1 1 1 1 1 1 1 1 1 2 2 2 10 
25: 1 1 1 1 1 1 1 2 2 2 2 10 
25: 1 1 1 1 1 2 2 2 2 2 10 
25: 1 1 1 2 2 2 2 2 2 10 
25: 1 2 2 2 2 2 2 2 10 
25: 1 1 1 1 1 1 1 1 1 1 5 10 
25: 1 1 1 1 1 1 1 1 2 5 10 
25: 1 1 1 1 1 1 2 2 5 10 
25: 1 1 1 1 2 2 2 5 10 
25: 1 1 2 2 2 2 5 10 
25: 2 2 2 2 2 5 10 
25: 1 1 1 1 1 5 5 10 
25: 1 1 1 2 5 5 10 
25: 1 2 2 5 5 10 
25: 5 5 5 10 
25: 1 1 1 1 1 10 10 
25: 1 1 1 2 10 10 
25: 1 2 2 10 10 
25: 5 10 10 
25: 1 1 1 1 1 20 
25: 1 1 1 2 20 
25: 1 2 2 20 
25: 5 20 
## each element can appear only once
4: 2 2 
10: 1 2 2 5 
10: 10 
25: 1 2 2 20 
25: 5 20
Bernat
sumber
1

Untuk menemukan kombinasi menggunakan excel - (cukup mudah). (Komputer Anda tidak boleh terlalu lambat)

  1. Pergi ke situs ini
  2. Buka halaman "Sum to Target"
  3. Unduh file excel "Sum ke Target".

    Ikuti petunjuk di halaman situs web.

semoga ini membantu.

Mark van Zoest
sumber
1

Swift 3 konversi solusi Java: (oleh @JeremyThompson)

protocol _IntType { }
extension Int: _IntType {}


extension Array where Element: _IntType {

    func subsets(to: Int) -> [[Element]]? {

        func sum_up_recursive(_ numbers: [Element], _ target: Int, _ partial: [Element], _ solution: inout [[Element]]) {

            var sum: Int = 0
            for x in partial {
                sum += x as! Int
            }

            if sum == target {
                solution.append(partial)
            }

            guard sum < target else {
                return
            }

            for i in stride(from: 0, to: numbers.count, by: 1) {

                var remaining = [Element]()

                for j in stride(from: i + 1, to: numbers.count, by: 1) {
                    remaining.append(numbers[j])
                }

                var partial_rec = [Element](partial)
                partial_rec.append(numbers[i])

                sum_up_recursive(remaining, target, partial_rec, &solution)
            }
        }

        var solutions = [[Element]]()
        sum_up_recursive(self, to, [Element](), &solutions)

        return solutions.count > 0 ? solutions : nil
    }

}

pemakaian:

let numbers = [3, 9, 8, 4, 5, 7, 10]

if let solution = numbers.subsets(to: 15) {
    print(solution) // output: [[3, 8, 4], [3, 5, 7], [8, 7], [5, 10]]
} else {
    print("not possible")
}
RolandasR
sumber
1

Ini dapat digunakan untuk mencetak semua jawaban juga

public void recur(int[] a, int n, int sum, int[] ans, int ind) {
    if (n < 0 && sum != 0)
        return;
    if (n < 0 && sum == 0) {
        print(ans, ind);
        return;
    }
    if (sum >= a[n]) {
        ans[ind] = a[n];
        recur(a, n - 1, sum - a[n], ans, ind + 1);
    }
    recur(a, n - 1, sum, ans, ind);
}

public void print(int[] a, int n) {
    for (int i = 0; i < n; i++)
        System.out.print(a[i] + " ");
    System.out.println();
}

Kompleksitas Waktu bersifat eksponensial. Urutan 2 ^ n

Astha Gupta
sumber
1

Saya melakukan sesuatu yang serupa untuk tugas scala. Berpikir untuk memposting solusi saya di sini:

 def countChange(money: Int, coins: List[Int]): Int = {
      def getCount(money: Int, remainingCoins: List[Int]): Int = {
        if(money == 0 ) 1
        else if(money < 0 || remainingCoins.isEmpty) 0
        else
          getCount(money, remainingCoins.tail) +
            getCount(money - remainingCoins.head, remainingCoins)
      }
      if(money == 0 || coins.isEmpty) 0
      else getCount(money, coins)
    }
Prabodh Mhalgi
sumber
1

Saya memindahkan sampel C # ke Objective-c dan tidak melihatnya dalam respons:

//Usage
NSMutableArray* numberList = [[NSMutableArray alloc] init];
NSMutableArray* partial = [[NSMutableArray alloc] init];
int target = 16;
for( int i = 1; i<target; i++ )
{ [numberList addObject:@(i)]; }
[self findSums:numberList target:target part:partial];


//*******************************************************************
// Finds combinations of numbers that add up to target recursively
//*******************************************************************
-(void)findSums:(NSMutableArray*)numbers target:(int)target part:(NSMutableArray*)partial
{
    int s = 0;
    for (NSNumber* x in partial)
    { s += [x intValue]; }

    if (s == target)
    { NSLog(@"Sum[%@]", partial); }

    if (s >= target)
    { return; }

    for (int i = 0;i < [numbers count];i++ )
    {
        int n = [numbers[i] intValue];
        NSMutableArray* remaining = [[NSMutableArray alloc] init];
        for (int j = i + 1; j < [numbers count];j++)
        { [remaining addObject:@([numbers[j] intValue])]; }

        NSMutableArray* partRec = [[NSMutableArray alloc] initWithArray:partial];
        [partRec addObject:@(n)];
        [self findSums:remaining target:target part:partRec];
    }
}
JMan Mousey
sumber
1

@ Jawaban KeithBeller dengan nama variabel yang sedikit berubah dan beberapa komentar.

    public static void Main(string[] args)
    {
        List<int> input = new List<int>() { 3, 9, 8, 4, 5, 7, 10 };
        int targetSum = 15;
        SumUp(input, targetSum);
    }

    public static void SumUp(List<int> input, int targetSum)
    {
        SumUpRecursive(input, targetSum, new List<int>());
    }

    private static void SumUpRecursive(List<int> remaining, int targetSum, List<int> listToSum)
    {
        // Sum up partial
        int sum = 0;
        foreach (int x in listToSum)
            sum += x;

        //Check sum matched
        if (sum == targetSum)
            Console.WriteLine("sum(" + string.Join(",", listToSum.ToArray()) + ")=" + targetSum);

        //Check sum passed
        if (sum >= targetSum)
            return;

        //Iterate each input character
        for (int i = 0; i < remaining.Count; i++)
        {
            //Build list of remaining items to iterate
            List<int> newRemaining = new List<int>();
            for (int j = i + 1; j < remaining.Count; j++)
                newRemaining.Add(remaining[j]);

            //Update partial list
            List<int> newListToSum = new List<int>(listToSum);
            int currentItem = remaining[i];
            newListToSum.Add(currentItem);
            SumUpRecursive(newRemaining, targetSum, newListToSum);
        }
    }'
Strider
sumber
1

Versi PHP , seperti yang terinspirasi oleh versi C # Keith Beller's.

Versi PHP bala tidak berfungsi untuk saya, karena saya tidak perlu mengelompokkan angka. Saya ingin implementasi yang lebih sederhana dengan satu nilai target, dan kumpulan angka. Fungsi ini juga akan memangkas setiap entri duplikat.

/**
 * Calculates a subset sum: finds out which combinations of numbers
 * from the numbers array can be added together to come to the target
 * number.
 * 
 * Returns an indexed array with arrays of number combinations.
 * 
 * Example: 
 * 
 * <pre>
 * $matches = subset_sum(array(5,10,7,3,20), 25);
 * </pre>
 * 
 * Returns:
 * 
 * <pre>
 * Array
 * (
 *   [0] => Array
 *   (
 *       [0] => 3
 *       [1] => 5
 *       [2] => 7
 *       [3] => 10
 *   )
 *   [1] => Array
 *   (
 *       [0] => 5
 *       [1] => 20
 *   )
 * )
 * </pre>
 * 
 * @param number[] $numbers
 * @param number $target
 * @param array $part
 * @return array[number[]]
 */
function subset_sum($numbers, $target, $part=null)
{
    // we assume that an empty $part variable means this
    // is the top level call.
    $toplevel = false;
    if($part === null) {
        $toplevel = true;
        $part = array();
    }

    $s = 0;
    foreach($part as $x) 
    {
        $s = $s + $x;
    }

    // we have found a match!
    if($s == $target) 
    {
        sort($part); // ensure the numbers are always sorted
        return array(implode('|', $part));
    }

    // gone too far, break off
    if($s >= $target) 
    {
        return null;
    }

    $matches = array();
    $totalNumbers = count($numbers);

    for($i=0; $i < $totalNumbers; $i++) 
    {
        $remaining = array();
        $n = $numbers[$i];

        for($j = $i+1; $j < $totalNumbers; $j++) 
        {
            $remaining[] = $numbers[$j];
        }

        $part_rec = $part;
        $part_rec[] = $n;

        $result = subset_sum($remaining, $target, $part_rec);
        if($result) 
        {
            $matches = array_merge($matches, $result);
        }
    }

    if(!$toplevel) 
    {
        return $matches;
    }

    // this is the top level function call: we have to
    // prepare the final result value by stripping any
    // duplicate results.
    $matches = array_unique($matches);
    $result = array();
    foreach($matches as $entry) 
    {
        $result[] = explode('|', $entry);
    }

    return $result;
}
AeonOfTime
sumber
1

Disarankan sebagai jawaban:

Inilah solusi menggunakan generator es2015 :

function* subsetSum(numbers, target, partial = [], partialSum = 0) {

  if(partialSum === target) yield partial

  if(partialSum >= target) return

  for(let i = 0; i < numbers.length; i++){
    const remaining = numbers.slice(i + 1)
        , n = numbers[i]

    yield* subsetSum(remaining, target, [...partial, n], partialSum + n)
  }

}

Menggunakan generator sebenarnya bisa sangat berguna karena memungkinkan Anda untuk menghentikan eksekusi skrip segera setelah menemukan subset yang valid. Ini berbeda dengan solusi tanpa generator (yaitu keadaan kurang) yang harus beralih melalui setiap subset darinumbers

feihcsim
sumber
1

Kurangi 0 di tempat pertama. Nol adalah identitas tambahan sehingga tidak berguna oleh hukum monoid dalam kasus khusus ini. Juga menyimpulkan angka negatif juga jika Anda ingin naik ke angka positif. Kalau tidak, Anda juga perlu operasi pengurangan.

Jadi ... algoritma tercepat yang dapat Anda peroleh pada pekerjaan khusus ini adalah sebagai berikut diberikan dalam JS.

function items2T([n,...ns],t){
    var c = ~~(t/n);
    return ns.length ? Array(c+1).fill()
                                 .reduce((r,_,i) => r.concat(items2T(ns, t-n*i).map(s => Array(i).fill(n).concat(s))),[])
                     : t % n ? []
                             : [Array(c).fill(n)];
};

var data = [3, 9, 8, 4, 5, 7, 10],
    result;

console.time("combos");
result = items2T(data, 15);
console.timeEnd("combos");
console.log(JSON.stringify(result));

Ini adalah algoritma yang sangat cepat tetapi jika Anda mengurutkan dataarray turun akan lebih cepat. Penggunaan .sort()tidak signifikan karena algoritma akan berakhir dengan doa rekursif jauh lebih sedikit.

Redu
sumber
Bagus. Ini menunjukkan bahwa Anda adalah programmer yang berpengalaman :)
James P.
1

Versi Perl (dari jawaban utama):

use strict;

sub subset_sum {
  my ($numbers, $target, $result, $sum) = @_;

  print 'sum('.join(',', @$result).") = $target\n" if $sum == $target;
  return if $sum >= $target;

  subset_sum([@$numbers[$_ + 1 .. $#$numbers]], $target, 
             [@{$result||[]}, $numbers->[$_]], $sum + $numbers->[$_])
    for (0 .. $#$numbers);
}

subset_sum([3,9,8,4,5,7,10,6], 15);

Hasil:

sum(3,8,4) = 15
sum(3,5,7) = 15
sum(9,6) = 15
sum(8,7) = 15
sum(4,5,6) = 15
sum(5,10) = 15

Versi Javascript:

const subsetSum = (numbers, target, partial = [], sum = 0) => {
  if (sum < target)
    numbers.forEach((num, i) =>
      subsetSum(numbers.slice(i + 1), target, partial.concat([num]), sum + num));
  else if (sum == target)
    console.log('sum(%s) = %s', partial.join(), target);
}

subsetSum([3,9,8,4,5,7,10,6], 15);

Javascript satu-liner yang benar-benar mengembalikan hasil (alih-alih mencetaknya):

const subsetSum=(n,t,p=[],s=0,r=[])=>(s<t?n.forEach((l,i)=>subsetSum(n.slice(i+1),t,[...p,l],s+l,r)):s==t?r.push(p):0,r);

console.log(subsetSum([3,9,8,4,5,7,10,6], 15));

Dan favorit saya, satu-liner dengan panggilan balik:

const subsetSum=(n,t,cb,p=[],s=0)=>s<t?n.forEach((l,i)=>subsetSum(n.slice(i+1),t,cb,[...p,l],s+l)):s==t?cb(p):0;

subsetSum([3,9,8,4,5,7,10,6], 15, console.log);

niry
sumber
0
function solve(n){
    let DP = [];

     DP[0] = DP[1] = DP[2] = 1;
     DP[3] = 2;

    for (let i = 4; i <= n; i++) {
      DP[i] = DP[i-1] + DP[i-3] + DP[i-4];
    }
    return DP[n]
}

console.log(solve(5))

Ini adalah Solusi Dinamis untuk JS untuk mengetahui berapa banyak cara orang bisa mendapatkan jumlah tertentu. Ini bisa menjadi solusi yang tepat jika Anda memikirkan kompleksitas waktu dan ruang.

Dheerendra Dev
sumber
0
import java.util.*;

public class Main{

     int recursionDepth = 0;
     private int[][] memo;

     public static void main(String []args){
         int[] nums = new int[] {5,2,4,3,1};
         int N = nums.length;
         Main main =  new Main();
         main.memo = new int[N+1][N+1];
         main._findCombo(0, N-1,nums, 8, 0, new LinkedList() );
         System.out.println(main.recursionDepth);
     }


       private void _findCombo(
           int from,
           int to,
           int[] nums,
           int targetSum,
           int currentSum,
           LinkedList<Integer> list){

            if(memo[from][to] != 0) {
                currentSum = currentSum + memo[from][to];
            }

            if(currentSum > targetSum) {
                return;
            }

            if(currentSum ==  targetSum) {
                System.out.println("Found - " +list);
                return;
            }

            recursionDepth++;

           for(int i= from ; i <= to; i++){
               list.add(nums[i]);
               memo[from][i] = currentSum + nums[i];
               _findCombo(i+1, to,nums, targetSum, memo[from][i], list);
                list.removeLast();
           }

     }
}
Neel Salpe
sumber
0

Saya tidak menyukai Solusi Javascript yang saya lihat mengapa saya membuat satu untuk myselft menggunakan penerapan parsial, penutupan, dan rekursi:

Ok, saya terutama khawatir tentang apakah array kombinasi dapat memenuhi target kebutuhan, tetapi dengan pendekatan ini Anda dapat mulai menemukan sisa kombinasi

Di sini hanya mengatur target dan meneruskan array kombinasi.

function main() {
    const target = 10
    const getPermutationThatSumT = setTarget(target)
    const permutation = getPermutationThatSumT([1, 4, 2, 5, 6, 7])

    console.log( permutation );
}

implementasi saat ini saya datang dengan

function setTarget(target) {
    let partial = [];

    return function permute(input) {
        let i, removed;
        for (i = 0; i < input.length; i++) {
            removed = input.splice(i, 1)[0];
            partial.push(removed);

            const sum = partial.reduce((a, b) => a + b)
            if (sum === target) return partial.slice()
            if (sum < target) permute(input)

            input.splice(i, 0, removed);
            partial.pop();
        }
        return null
    };
}
Luillyfe
sumber