Belirli bir miktara ulaşmak için tüm olası sayı kombinasyonlarını bulma


232

Belirli bir Nsayı kümesinden olası tüm ekleme kombinasyonlarını belirli bir son sayıya ekleyecek şekilde nasıl test edersiniz ?

Kısa bir örnek:

  • Eklenecek sayılar kümesi: N = {1,5,22,15,0,...}
  • İstenen sonuç: 12345

@James - Sanırım sorununun açıklığa kavuşturulması gerekiyor. Kurallar neler? Sadece herhangi bir sayı seçebilir misin? Sette hangi numaralar var? Kısıtlamalarınız neler?
jmort253

9
Wikipedia makalesi ( en.wikipedia.org/wiki/Subset_sum_problem ) bile bu sorunun NP-tamamlama problemleri sınıfına iyi bir giriş olduğunu belirtmektedir.
user57368

1
@ jmort253: Pozitif ve hedef olarak verilen sayıdan daha düşük bir tamsayı kümesine sahip olmak dışında herhangi bir kısıtlama olduğunu düşünmüyorum. Herhangi bir sayı kombinasyonu kullanılabilir. Ev ödevi değil, bazı işler için başvurursanız çözmeniz gereken bir tür problem. Genellikle gerektiğinde bir algoritma düşünebilirim ama böyle bir şeyi nasıl görüntüleyeceğimi bilmiyorum. Bir şekilde ayrıştırılması gerekiyor (özyinelemeli?).
James P.

3
@James, Kombinasyonlara mı yoksa toplamı toplayan alt kümelerin sayısına mı ihtiyacınız var?
st0le

6
Orijinal setin aynı öğesini bir kereden fazla kullanabilir miyiz? Örneğin, girdi {1,2,3,5} ve hedef 10 ise 5 + 5 = 10 kabul edilebilir bir çözüm mü?
alampada

Yanıtlar:


248

Bu sorun, hedefe ulaşanları filtreleyen tüm olası toplamların özyinelemeli kombinasyonları ile çözülebilir. İşte Python'daki algoritma:

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

Bu tür algoritmalar aşağıdaki Standford'un Soyut Programlama dersinde çok iyi açıklanmıştır - bu video özyinelemelerin çözüm permütasyonları oluşturmak için nasıl çalıştığını anlamak için çok tavsiye edilir.

Düzenle

Yukarıda bir jeneratör işlevi olarak, biraz daha kullanışlı hale getirir. Çünkü Python 3.3+ gerektirir 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)

İşte aynı algoritmanın Java sürümü:

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);
    }
}

Tam olarak aynı buluşsal yöntemdir. Java'm biraz paslı ama bence anlaması kolay.

Java çözümünün C # dönüşümü: (@JeremyThompson tarafından)

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);
    }
}

Ruby çözümü: (@emaillenin tarafından)

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)

Düzenleme: karmaşıklık tartışması

Diğerlerinin de belirttiği gibi bu NP zor bir sorundur . Üstel zaman O (2 ^ n) içinde çözülebilir, örneğin n = 10 için 1024 olası çözüm olacaktır. Ulaşmaya çalıştığınız hedefler düşük bir aralıktaysa, bu algoritma çalışır. Örneğin:

subset_sum([1,2,3,4,5,6,7,8,9,10],100000) hedef hiçbir zaman olası çözümleri filtrelemediği için 1024 dal üretir.

Öte yandan subset_sum([1,2,3,4,5,6,7,8,9,10],10), sadece 175 dal üretir, çünkü ulaşılacak hedef 10birçok kombinasyonu filtrelemeye başlar.

Eğer Nve Targettek çözümün yaklaşık bir sürümüne hareket etmelidir büyük sayılardır.


1
Java optimizasyonu: ArrayList <Integer> partial_rec = yeni ArrayList <Integer> (kısmi); partial_rec.add (n); bu kısmi bir kopyasını yapar. ve böylece O (N) ekler. Daha iyi bir yol sadece "partial.add (n)" özyineleme yapmak ve sonra "partial.remove (partial.size -1). Emin olmak için kodunuzu yeniden. İyi çalışıyor
Christian Bongiorno

4
Bu çözüm her durumda çalışmaz. Düşünün [1, 2, 0, 6, -3, 3], 3- sadece [1,2], [0,3], [3]eksik durumlarda çıktı verir[6, -3, 3]
LiraNuna

11
Aynı zamanda, örneğin, her bir kombinasyon için çalışmaz [1, 2, 5], 5yalnızca üretimi [5]zaman [1, 1, 1, 1, 1], [2, 2, 1]ve [2, 1, 1, 1]çözeltilerdir.
cbrad

3
@cbrad çünkü biridir i+1içinde remaining = numbers[i+1:]. Görünüşe göre algoritma yinelemelere izin vermiyor.
Leonid Vasilev

1
yinelenenler dahil @cbrad da almak için çözümler gibi [1, 1, 3]bir göz stackoverflow.com/a/34971783/3684296 (Python)
Mesa

36

Bu sorunun çözümü internette milyonlarca kez verilmiştir. Soruna bozuk para değiştirme sorunu denir . Bir de çözüm bulabilirsiniz http://rosettacode.org/wiki/Count_the_coins de ve bunun matematiksel model http://jaqm.ro/issues/volume-5,issue-2/pdfs/patterson_harmel.pdf (veya Google sikke değişikliği sorun ).

Bu arada, Tsagadai'nin Scala çözümü ilginç. Bu örnek 1 veya 0 üretir. Bir yan etki olarak, konsolda olası tüm çözümleri listeler. Çözümü görüntüler, ancak herhangi bir şekilde kullanılabilir hale getirmez.

Mümkün olduğunca faydalı olması için, kodun List[List[Int]](liste listesinin uzunluğu), "en iyi" çözümün (en kısa liste) veya tüm olası çözümlerin elde edilmesine izin vermek için bir a döndürmesi gerekir .

İşte bir örnek. Çok verimsizdir, ancak anlaşılması kolaydır.

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")
}

Çalıştırıldığında şunları görüntüler:

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)

sumCombinations()Fonksiyonu kendi başına kullanılabilir ve sonuç ayrıca "en iyi" çözeltisi (kısa liste), ya da çözeltilerin sayısı (listeleri sayısı) göstermek için analiz edilebilir.

Böyle olsa bile, gereksinimlerin tam olarak karşılanamayabileceğini unutmayın. Çözümdeki her listenin sıralaması önemli olabilir. Böyle bir durumda, her liste, öğelerinin birleşimi olduğu kadar çoğaltılmalıdır. Veya yalnızca farklı kombinasyonlarla ilgilenebiliriz.

Örneğin, bunun List(5, 10)iki kombinasyon vermesi gerektiğini düşünebiliriz : List(5, 10)ve List(10, 5). Çünkü List(5, 5, 5)gereksinimlere bağlı olarak üç kombinasyon veya sadece bir tane verebilir. Tamsayılar için, üç permütasyon eşdeğerdir, ancak "jeton değiştirme probleminde" olduğu gibi jetonlarla uğraşıyorsak, değildir.

Ayrıca şartlarda belirtilmeyen, her bir sayının (veya madeni para) yalnızca bir kez mi yoksa birden fazla kez mi kullanılacağı sorusudur. Sorunu her bir sayının tekrarlama listelerine göre genelleştirebiliriz (ve yapmalıyız!). Bu, gerçek hayatta "bir dizi madeni para (ve bir madeni para değeri kümesi değil) ile belirli bir miktar para kazanmanın olası yolları nelerdir" anlamına gelir. Orijinal problem, bunun her bir jeton değerinde toplam miktarı yapmak için gerektiği kadar çok sayıda oluşumumuz olduğu belirli bir durumdur.


14
Bu sorun bozuk para değiştirme sorunuyla tam olarak aynı değildir. OP sadece minimal değil tüm kombinasyonu istiyor. Ve muhtemelen kümedeki tamsayılar negatif olabilir. Bu nedenle, bozuk para değiştirme sorununun bazı optimizasyonları bu sorunla mümkün değildir.
13:24, ThomasMcLeod

5
ve ayrıca bu sorun öğelerin tekrarına izin veriyor, OP'nin bunu istediğinden emin değilim, ama daha çok bir sırt çantası sorunu
caub

34

In Haskell :

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

Ve J :

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

Fark edebileceğiniz gibi, her ikisi de aynı yaklaşımı benimseyin ve sorunu iki parçaya bölün: güç setinin her bir üyesini oluşturun ve her üyenin toplamını hedefe kontrol edin.

Başka çözümler de var, ama bu en basit olanı.

İkisinden biri için yardıma mı ihtiyacınız var, yoksa farklı bir yaklaşım mı arıyorsunuz?


3
Vay canına, bu oldukça özlü bir kod. Cevabında iyiyim. Genel olarak algoritmaları biraz okumam gerektiğini düşünüyorum. Merakımı tetiklerken iki dilin sözdizimine bir göz atacağım.
James P.

Bunu denemek için Haskell'i yeni kurdum, kesinlikle yapıştırıp çalıştıramazsınız, not in scope: 'subsequences'herhangi bir işaretçi?
Hart CO

4
@HartCO partiye biraz geç, amaimport Data.List
Jir

28

Javascript sürümü:

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


Kod dilimde bir hata var, remaining = numbers.slice(); remaining.slice(i + 1);aksi takdirde numbers.slice(i + 1);sayılar dizisi değiştirilmelidir
Emeeus

@Emeeus, bunun doğru olduğunu düşünmüyorum. slice(sığ) bir kopya döndürür, numbersdiziyi değiştirmez .
Dario Seidl

@DarioSeidl evet, dilim bir kopyasını döndürür, diziyi değiştirmez, nokta budur, bu yüzden bir değişkene atamazsanız değiştirmezsiniz. Bu durumda, anladığım kadarıyla orijinali değil, değiştirilmiş bir versiyonu geçmeliyiz. Bkz. Bu jsfiddle.net/che06t3w/1
Emeeus

1
@Redu ... örneğin bunu yapmanın kolay bir yolu, algoritmayı biraz değiştirebilir ve bir iç fonksiyon kullanabiliriz: jsbin.com/lecokaw/edit?js,console
rbarilani

1
Verilen kod tüm kombinasyonları almak zorunda değildir .. örneğin [1,2] koymak, 3 sadece 1 + 2 = 3 değil 1 + 1 + 1 veya 2 + 1
döndürür

12

Aynı algoritmanın C ++ sürümü

#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;
}

11

C # msalvadores kod cevap sürümü

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>());
}

4

Bu sorunun cevabını kullanacağımı düşündüm ama yapamadım, işte cevabım. Bilgisayar Programlarının Yapısı ve Yorumlanmasında cevabın değiştirilmiş bir versiyonunu kullanmaktadır . Bence bu daha iyi bir özyinelemeli çözümdür ve safları daha çok memnun etmelidir.

Cevabım Scala'da (ve Scala'm berbatsa özür dilerim, yeni öğrenmeye başladım). FindSumCombinations delilik dupes önlemek için yineleme için sıralama ve eşsiz orijinal liste etmektir.

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)
}

Kullanmak için:

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

4
Thank you.. ephemient

ben php için python mantık yukarıda dönüştürdüm ..

<?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();
}
?>

4

Başka bir python çözümü, itertools.combinationsmodülü aşağıdaki gibi kullanmak olacaktır :

#!/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)

Çıktı: [(8, 7), (5, 10), (3, 8, 4), (3, 5, 7)]


çalışmaz, örneğin: find_sum_in_list (aralık (0,8), 4). Bulunan: [(4,), (0, 4), (1, 3), (0, 1, 3)]. Ancak (2, 2) de bir seçenektir!
Andre Araujo

@AndreAraujo: 0 kullanmak mantıklı değil, ancak (1,8) kullanırsanız itertools.combinations_with_replacement çalışır ve ayrıca 2,2 çıktı alır.
Rubenisme

@Rubenisme Evet adamım! Sorun değiştirme oldu! Teşekkürler! ;-)
Andre Araujo

4

İşte R'de bir çözüm

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))
  }
}

R'de bir çözüm arıyorum, ama bu benim için çalışmıyor. Örneğin, subset_sum(numbers = c(1:2), target = 5)döndürür "sum(1+2+2)=5". Ancak 1 + 1 + 1 + 1 + 1 kombinasyonu eksik. Hedefleri daha yüksek sayılara (ör. 20) ayarlamak daha fazla kombinasyon içermiyor.
Frederick

Açıkladığınız, işlevin geri dönmek istediği şey değildir. Kabul edilen cevaba bakın. 2'nin iki kez tekrarlanması, R'nin amaçlanan davranışı değil, seriyi nasıl ürettiğini ve alt küme yaptığını gösterir.
Mark

subset_sum(1:2, 4)hiçbir çözüm döndürmemelidir çünkü 4'e ekleyen 1 ve 2 kombinasyonu yoktur. İşlevime eklenmesi gereken şey i, uzunluktan büyükse bir numbers
Mark

3

Burada, karmaşıklık O(t*N)(dinamik çözüm) üstel algoritmadan daha büyük olduğunda, küçük N ve çok büyük hedef toplamı için çok uygun bir Java sürümü bulunmaktadır . Benim versiyon biraz klasik naif karmaşıklığı azaltmak amacıyla kayması ile birlikte, orta saldırıda buluşacak kullanır O(n*2^n)için O(2^(n/2)).

Bunu 32 ila 64 eleman içeren setler için kullanmak istiyorsanız int, adım fonksiyonundaki geçerli altkümeyi temsil eden alt kümeyi değiştirerek değiştirmelisiniz, longancak ayarlanan boyut arttıkça performans belirgin şekilde azalacaktır. Bunu, tek sayıda öğe içeren bir küme için kullanmak istiyorsanız, kümeye çift olarak numaralandırmak için 0 eklemeniz gerekir.

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;
    }
}

3

Bu bir bozuk para değiştirme sorununa benzer

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;       
}
}

2

Bir yıl önce c ++ yazdım tabloları kullanarak çok verimli algoritma.

PRINT 1 ayarını yaparsanız tüm kombinasyonları yazdırır (ancak etkin yöntemi kullanmayacaktır).

O kadar etkilidir ki 10ms'den daha az 10 ^ 14 kombinasyon hesaplar.

#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");

}

Merhaba! Böyle bir şey yapmak için bir koda ihtiyacım var, 60 sayı listesinde 6 sayı kümelerinin tüm olası toplamları bulmak. Toplamlar en az 180, en fazla 191 aralığında olmalıdır. Bu kod bunun için ayarlanabilir mi? Bu kodu bulutta nerede çalıştırmalı? Codenvy'de başarılı olamadım
defreturn

2

Aşağıdaki Excel VBA sürümü. Bunu VBA'da uygulamam gerekiyordu (benim tercihim değil, beni yargılamayın!) Ve yaklaşım için bu sayfadaki cevapları kullandım. Başkalarının da VBA sürümüne ihtiyaç duyması durumunda yüklüyorum.

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

Çıktı (Hemen pencereye yazılır) şöyle olmalıdır:

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

2

İşte daha iyi çıktı biçimlendirme ve C ++ 11 özellikleri ile daha iyi bir sürüm:

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);
    }
}

2

Öğeleri eklemeyi ve olası değerler arasında yeniden dağıtmayı sağlayan Java özyinelemeli olmayan sürüm. 0yok sayılır ve sabit listeler (verdiğiniz şeyle oynayabileceğiniz şeydir) veya tekrarlanabilir sayıların bir listesi için çalışır.

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();
        }
    }
}

Örnek giriş:

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

Örnek çıktı:

## 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

1

Excel kullanarak kombinasyonları bulmak için - (oldukça kolay). (Bilgisayarınız çok yavaş olmamalıdır)

  1. Bu siteye git
  2. "Hedefe Toplam" sayfasına gidin
  3. "Hedefe Toplam" excel dosyasını indirin.

    Web sitesi sayfasındaki talimatları izleyin.

Bu yardımcı olur umarım.


1

Java çözümünün Swift 3 dönüşümü: (@JeremyThompson tarafından)

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
    }

}

kullanımı:

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")
}

1

Bu, tüm cevapları yazdırmak için de kullanılabilir

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();
}

Zaman Karmaşıklığı üsteldir. 2 ^ n sırası


1

Bir skala ataması için benzer bir şey yapıyordum. Çözümümü buraya göndermeyi düşündüm:

 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)
    }

1

C # örneğini Objective-c'ye taşıdım ve yanıtlarda görmedim:

//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];
    }
}

1

@ KeithBeller'ın değişken isimleri ve bazı yorumlarıyla cevabı.

    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);
        }
    }'

1

Keith Beller'in C # sürümünden esinlenilen PHP Sürümü .

bala'nın PHP sürümü benim için çalışmadı, çünkü sayıları gruplandırmam gerekmiyordu. Bir hedef değeri ve bir sayı havuzuyla daha basit bir uygulama istedim. Bu işlev ayrıca yinelenen girişleri de budanır.

/**
 * 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;
}

1

Cevap olarak önerilir:

İşte es2015 jeneratörlerini kullanan bir çözüm :

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)
  }

}

Jeneratörleri kullanmak aslında çok yararlı olabilir, çünkü geçerli bir alt küme bulduktan hemen sonra komut dosyası yürütmeyi duraklatmanıza izin verir. Bu, her bir alt kümesinde yineleme yapmak zorunda olan jeneratörsüz çözümlerin (yani eksik durum) aksinenumbers


1

İlk etapta 0 çıkarın. Sıfır, toplama için bir özdeştir, bu nedenle bu özel durumda monoid yasalar tarafından işe yaramaz. Ayrıca pozitif bir sayıya tırmanmak istiyorsanız negatif sayıları da çıkarın. Aksi takdirde çıkarma işlemine de ihtiyacınız olacaktır.

Yani ... bu işte alabileceğiniz en hızlı algoritma JS'de verilen gibidir.

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));

Bu çok hızlı bir algoritmadır, ancak azalandata diziyi sıralarsanız daha da hızlı olacaktır. Kullanılması algoritması ile sona erecek çünkü önemsiz çok az özyinelemeli çağırmaları..sort()


Güzel. Deneyimli bir programcı olduğunuzu gösteriyor :)
James P.

1

Perl sürümü (baştaki cevabın):

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);

Sonuç:

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

Javascript sürümü:

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);

Sonuçları gerçekten döndüren bir tek satırlık Javascript (yazdırmak yerine):

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));

Ve benim en sevdiğim, geri çağırma ile tek astar:

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);


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))

Bu JS için herkesin toplamı kaç yoldan alabileceğini söyleyen Dinamik bir Çözümdür. Zaman ve mekan karmaşıklığı hakkında düşünürseniz, bu doğru çözüm olabilir.


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();
           }

     }
}

0

Javascript Çözüm hoşlanmadı neden kısmi uygulama, kapanış ve özyineleme kullanarak myselft için bir inşa gördüm:

Tamam, esas olarak kombinasyonlar dizisinin gereksinim hedefini karşılayıp karşılayamayacağı konusunda endişeliydim, ancak bu yaklaşımla kombinasyonların geri kalanını bulmaya başlayabilirsiniz

Burada sadece hedefi belirleyin ve kombinasyonlar dizisini iletin.

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

    console.log( permutation );
}

şu anda uyguladığım uygulama

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
    };
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.