주어진 합계에 도달하기 위해 가능한 모든 숫자 조합 찾기


232

주어진 N숫자 집합에서 가능한 모든 추가 조합을 테스트 하여 주어진 최종 숫자에 더하는 방법은 무엇입니까?

간단한 예 :

  • 추가 할 숫자 세트 : N = {1,5,22,15,0,...}
  • 원하는 결과 : 12345

@James-문제를 분명히해야한다고 생각합니다. 규칙은 무엇입니까? 당신은 어떤 숫자를 선택할 수 있습니까? 세트에 어떤 숫자가 있습니까? 당신의 제약은 무엇입니까?
jmort253

9
위키 백과 기사 ( en.wikipedia.org/wiki/Subset_sum_problem )에도이 문제가 NP- 완전 문제 클래스에 대한 좋은 소개라고 언급되어 있습니다.
user57368

1
@ jmort253 : 양수이고 목표로 주어진 수보다 낮은 정수 세트를 갖는 것 외에 다른 제약이 있다고 생각하지 않습니다. 숫자 조합을 사용할 수 있습니다. 숙제는 아니지만 일부 일자리를 신청하면 해결해야 할 문제가 있습니다. 보통 필요할 때 알고리즘을 생각할 수 있지만 이와 같은 것을 보는 방법을 잘 모르겠습니다. 어떻게 든 분해해야합니다 (재귀 적입니까?).
제임스 P.

3
@ 제임스, 합에 합산되는 조합 또는 하위 집합의 수가 필요합니까?
st0le

6
원본 세트의 동일한 요소를 두 번 이상 사용할 수 있습니까? 예를 들어 입력이 {1,2,3,5}이고 대상 10 인 경우 5 + 5 = 10이 적합한 솔루션입니까?
alampada

답변:


248

이 문제는 대상에 도달하는 합계를 필터링하는 가능한 모든 합계의 재귀 조합으로 해결할 수 있습니다. 다음은 파이썬의 알고리즘입니다.

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

이 유형의 알고리즘은 다음 Standford의 추상 프로그래밍 강의 에서 잘 설명되어 있습니다. 이 비디오는 재귀가 솔루션의 순열을 생성하는 방법을 이해하는 데 매우 권장됩니다.

편집하다

위의 생성기 함수로 좀 더 유용합니다. 때문에 Python 3.3 이상이 필요합니다 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)

다음은 동일한 알고리즘의 Java 버전입니다.

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

정확히 같은 휴리스틱입니다. 내 Java는 약간 녹슬지 만 이해하기 쉽다고 생각합니다.

Java 솔루션의 C # 변환 : (@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);
    }
}

루비 솔루션 : (@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)

편집 : 복잡성 토론

다른 사람들이 언급했듯이 이것은 NP-hard 문제 입니다. 지수 시간 O (2 ^ n)에서 풀 수 있습니다. 예를 들어 n = 10의 경우 1024 개의 가능한 솔루션이 있습니다. 도달하려는 목표가 낮은 범위에 있으면이 알고리즘이 작동합니다. 예를 들어 :

subset_sum([1,2,3,4,5,6,7,8,9,10],100000) 대상이 가능한 솔루션을 필터링하지 않기 때문에 1024 개의 분기를 생성합니다.

반면 subset_sum([1,2,3,4,5,6,7,8,9,10],10)도달 할 대상이 10여러 조합을 필터링 하기 때문에 175 개의 분기 만 생성합니다 .

경우 NTarget하나가 솔루션의 대략적인 버전으로 이동해야 큰 숫자입니다.


1
자바 최적화 : ArrayList <Integer> partial_rec = 새로운 ArrayList <Integer> (부분); partial_rec.add (n); 이것은 부분의 사본을 수행합니다. 따라서 O (N)를 더합니다. 더 좋은 방법은 "partial.add (n)"이 재귀를 수행 한 다음 "partial.remove (partial.size -1)을 수행하는 것입니다. 코드를 다시 작성하여 확인합니다. 잘 작동합니다.
Christian Bongiorno

4
이 솔루션은 모든 경우에 작동하지 않습니다. 다음 과 같은 경우가 누락 된 경우 [1, 2, 0, 6, -3, 3], 3에만 출력 [1,2], [0,3], [3]됩니다.[6, -3, 3]
LiraNuna

11
또한, 예를 들어, 모든 조합에 대해 작동하지 않는 [1, 2, 5], 5경우에만 출력 [5]할 때를 [1, 1, 1, 1, 1], [2, 2, 1]그리고 [2, 1, 1, 1]솔루션입니다.
충돌

3
@cbrad 그 때문이다 i+1에서 remaining = numbers[i+1:]. 알고리즘이 중복을 허용하지 않는 것 같습니다.
Leonid Vasilev

1
중복 포함 @cbrad도 얻으려면 솔루션 좋아 [1, 1, 3]한 번 봐 가지고 stackoverflow.com/a/34971783/3684296 (파이썬)
메사

36

이 문제의 해결책은 인터넷에서 백만 번 주어졌습니다. 문제는 동전 변경 문제 입니다. http://rosettacode.org/wiki/Count_the_coins 에서 솔루션을 찾을 수 있고 http://jaqm.ro/issues/volume-5,issue-2/pdfs/patterson_harmel.pdf (또는 Google 코인 변경) 에서 수학적 모델을 찾을 수 있습니다. 문제 ).

그런데 Tsagadai의 Scala 솔루션은 흥미 롭습니다. 이 예제는 1 또는 0을 생성합니다. 부작용으로 콘솔에 가능한 모든 솔루션이 나열됩니다. 솔루션을 표시하지만 어떤 식 으로든 사용할 수 없게합니다.

가능한 한 유용하도록 코드는 List[List[Int]]솔루션 수 (목록 목록의 길이), "최상의"솔루션 (가장 짧은 목록) 또는 가능한 모든 솔루션을 얻을 수 있도록 a 를 반환해야 합니다.

다음은 예입니다. 매우 비효율적이지만 이해하기 쉽습니다.

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

실행되면 다음이 표시됩니다.

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()기능은 단독으로 사용될 수 있으며 결과는 "최상의"솔루션 (가장 짧은 목록) 또는 솔루션 수 (목록 수)를 표시하기 위해 추가로 분석 될 수 있습니다.

이렇게해도 요구 사항이 완전히 충족되지 않을 수 있습니다. 솔루션의 각 목록 순서가 중요 할 수 있습니다. 이 경우 각 목록은 요소 조합이있을 때마다 복제해야합니다. 또는 다른 조합에만 관심이있을 수 있습니다.

예를 들어, 우리는 그 고려해 List(5, 10)두 조합을 제공해야 List(5, 10)하고 List(10, 5). 들어 List(5, 5, 5)는 요구 사항에 따라 세 가지 조합 또는 하나를 줄 수 있습니다. 정수의 경우 세 순열은 동일하지만 "코인 변경 문제"와 같이 동전을 다루는 경우에는 그렇지 않습니다.

또한 요구 사항에는 각 숫자 (또는 동전)를 한 번 또는 여러 번 사용할 수 있는지에 대한 질문이 나와 있지 않습니다. 문제를 각 숫자의 발생 목록으로 일반화 할 수 있습니다. 이것은 실생활에서 "일련의 동전 (일련의 동전 가치가 아닌)으로 일정 금액을 벌 수있는 방법은 무엇인가"로 해석됩니다. 원래 문제는이 문제의 특별한 경우이며, 각 단일 코인 값으로 총액을 만드는 데 필요한만큼의 각 코인이 발생합니다.


14
이 문제는 동전 변경 문제와 정확히 동일하지 않습니다. OP는 최소한의 것이 아니라 모든 조합을 요구하고 있습니다. 그리고 아마도 집합의 정수는 음수 일 수 있습니다. 따라서이 문제로는 코인 변경 문제의 특정 최적화가 불가능합니다.
ThomasMcLeod

5
또한이 문제는 항목의 반복을 허용합니다. OP가 이것을 원했는지 확실하지 않지만 더 많은 배낭 문제가 있습니다
caub

34

에서 하스켈 :

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

그리고 J :

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

알다시피, 동일한 접근 방식을 취하고 문제를 두 부분으로 나눕니다. 전원 세트의 각 멤버를 생성하고 각 멤버의 합계를 대상으로 확인하십시오.

다른 솔루션이 있지만 가장 간단합니다.

도움이 필요하거나 다른 접근법을 찾고 있습니까?


3
와우, 그것은 꽤 간결한 코드입니다. 나는 당신의 대답에 괜찮습니다. 나는 일반적으로 알고리즘을 조금 읽어야한다고 생각합니다. 호기심을 불러 일으키면서 두 언어의 구문을 살펴 보겠습니다.
James P.

방금 Haskell을 설치하여 시도해 보았습니다. 붙여 넣을 수 없으며 not in scope: 'subsequences'포인터가 있습니까?
Hart CO

4
@HartCO 파티에 조금 늦었지만import Data.List
Jir

28

자바 스크립트 버전 :

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


코드는 슬라이스에 실수가 있습니다. remaining = numbers.slice(); remaining.slice(i + 1);그렇지 않으면 numbers.slice(i + 1);숫자 배열을 변경 해야합니다
Emeeus

@Emeeus, 나는 그것이 사실이라고 생각하지 않습니다. slice(얕은) 복사본을 반환하지만 numbers배열을 수정하지 않습니다 .
Dario Seidl

@DarioSeidl 예, slice는 복사본을 반환하지만 배열을 수정하지 않습니다. 즉, 변수에 할당하지 않으면 변경하지 않는 것입니다. 이 경우 이해 했으므로 원본이 아닌 수정 된 버전을 전달해야합니다. 이 jsfiddle.net/che06t3w/1
Emeeus

1
@Redu ... 예를 들어, 쉬운 방법은 알고리즘을 약간 수정하고 내부 기능을 사용할 수 있다는 것입니다. jsbin.com/lecokaw/edit?js,console
rbarilani

1
주어진 코드는 반드시 모든 조합을하지 않습니다 .. 예를 들어 [1,2], 3 만 + 2 = 3 일하지 + 1 + 1 또는 2 + 1 리턴 퍼팅
JuicY_Burrito

12

동일한 알고리즘의 C ++ 버전

#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

@msalvadores 코드 답변의 C # 버전

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

나는이 질문에 대한 답변을 사용할 것이라고 생각했지만 할 수 없었으므로 여기에 내 대답이 있습니다. 컴퓨터 프로그램의 구조 및 해석 에서 수정 된 버전의 답변을 사용하고 있습니다. 나는 이것이 더 나은 재귀 솔루션이라고 생각하며 순수 주의자들을 더 기쁘게해야합니다.

내 대답은 스칼라에 있습니다 (그리고 스칼라가 짜증 나면 사과하기 시작했습니다). findSumCombinations크래 기는 재귀를 방지하기 위해 원래 목록을 정렬하고 고유 화하여 속력 을 방지하는 것입니다.

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

그것을 사용하려면 :

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

4
Thank you.. ephemient

위의 논리를 파이썬에서 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();
}
?>

4

다른 파이썬 솔루션은 itertools.combinations다음과 같이 모듈 을 사용하는 것입니다 .

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

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


find_sum_in_list (range (0,8), 4)와 같이 작동하지 않습니다. 발견 : [(4,), (0, 4), (1, 3), (0, 1, 3)]. 그러나 (2, 2)도 옵션입니다!
Andre Araujo

@AndreAraujo : 0을 사용하는 것은 의미가 없지만 (1,8)을 사용하면 itertools.combinations_with_replacement가 작동하고 2,2를 출력합니다.
Rubenisme

@Rubenisme 네! 문제는 교체였습니다! 감사! ;-)
Andre Araujo

4

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

R에서 솔루션을 찾고 있지만이 방법으로는 효과가 없습니다. 예를 subset_sum(numbers = c(1:2), target = 5)들어을 반환합니다 "sum(1+2+2)=5". 그러나 조합 1 + 1 + 1 + 1 + 1이 없습니다. 대상을 더 높은 숫자 (예 : 20)로 설정하면 더 많은 조합이 누락됩니다.
Frederick

설명하는 것은 함수가 반환하려는 것이 아닙니다. 허용 된 답변을보십시오. 2가 두 번 반복된다는 사실은 R이 의도 된 동작이 아니라 직렬을 생성하고 부분 집합하는 방식의 인공물입니다.
Mark

subset_sum(1:2, 4)4에 추가되는 1과 2의 조합이 없기 때문에 해를 구하지 않아야 i합니다. 길이보다 큰 경우 내 함수에 추가해야 할 것은 이스케이프 입니다numbers
Mark

3

다음은 복잡성 O(t*N)(동적 솔루션)이 지수 알고리즘보다 클 때 작은 N과 매우 큰 대상 합계에 적합한 Java 버전입니다 . 내 버전은 조금 고전 순진한에서 복잡성을 줄이기 위해 이동과 함께 중앙 공격에 대회를 사용 O(n*2^n)하는 O(2^(n/2)).

32 ~ 64 개의 요소가있는 세트에 이것을 사용하려면 int, 단계 함수의 현재 서브 세트를 나타내는 long것을 세트 크기가 증가함에 따라 성능이 현저하게 떨어질지라도 변경해야합니다 . 홀수의 요소가있는 세트에 이것을 사용하려면 세트에 0을 추가하여 짝수로 만들어야합니다.

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

이것은 동전 변경 문제와 유사합니다

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

몇 년 전에 C ++로 작성한 테이블을 사용하는 매우 효율적인 알고리즘.

PRINT 1을 설정하면 모든 조합이 인쇄되지만 효율적인 방법은 사용되지 않습니다.

10ms 미만으로 10 ^ 14 이상의 조합을 계산할 수있을 정도로 효율적입니다.

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

}

안녕! 그와 같은 일을하기 위해 코드가 필요합니다 .60 개의 숫자 목록에서 6 개의 숫자 집합을 모두 찾으십시오. 합계는 최소 180, 최대 191 범위에 있어야합니다. 해당 코드를 조정할 수 있습니까? 클라우드에서 해당 코드를 어디에서 실행할 수 있습니까? Codenvy에서 성공하지 못했습니다
defreturn

2

아래 Excel VBA 버전입니다. 나는 이것을 VBA (내 선호도가 아니라 판단하지 마십시오!)로 구현해야 했으며이 페이지의 답변을 접근 방식으로 사용했습니다. 다른 사람들도 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

출력 (즉시 창에 기록)은 다음과 같아야합니다.

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

2

다음은 더 나은 출력 형식과 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);
    }
}

2

Java 비 재귀 버전으로 요소를 계속 추가하고 가능한 값 사이에 재배포합니다. 0의 목록은 무시되며 고정 목록 (제공된 항목은 재생할 수있는 항목) 또는 반복 가능한 숫자 목록에서 작동합니다.

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

샘플 입력 :

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

샘플 출력 :

## 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을 사용하여 조합을 찾으려면-(매우 쉽습니다). (컴퓨터 속도가 너무 느려서는 안됩니다)

  1. 이 사이트로 이동
  2. "Sum to Target"페이지로 이동
  3. "Sum to Target"Excel 파일을 다운로드하십시오.

    웹 사이트 페이지의 지시 사항을 따르십시오.

도움이 되었기를 바랍니다.


1

Java 솔루션의 신속한 3 변환 : (@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
    }

}

용법:

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

이것은 모든 답변을 인쇄하는 데 사용될 수 있습니다

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

시간 복잡도는 지수입니다. 2 ^ n의 차수


1

스칼라 과제와 비슷한 일을하고있었습니다. 내 솔루션을 여기에 게시 할 생각 :

 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 # 샘플을 Objective-c로 포팅했지만 응답에서 보지 못했습니다.

//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의 답변은 약간 변경된 변수 이름과 주석이 있습니다.

    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의 C # 버전에서 영감을 얻은 PHP 버전.

bala의 PHP 버전은 숫자를 그룹화 할 필요가 없기 때문에 작동하지 않았습니다. 하나의 목표 값과 숫자 풀로 간단한 구현을 원했습니다. 이 기능은 중복 항목도 제거합니다.

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

답변으로 추천 :

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

}

생성기를 사용하면 유효한 하위 집합을 찾은 즉시 스크립트 실행을 일시 중지 할 수 있기 때문에 실제로 매우 유용 할 수 있습니다. 이것은 모든 하위 집합을 반복해야하는 생성기가없는 (즉, 상태가없는) 솔루션과 대조적입니다.numbers


1

처음에 0을 추론하십시오. 0은 덧셈의 신원이므로이 특별한 경우에는 단일 법에 의해 쓸모가 없습니다. 양수까지 올라가려면 음수도 추론하십시오. 그렇지 않으면 빼기 연산이 필요합니다.

그래서 ...이 특정 직업에서 얻을 수있는 가장 빠른 알고리즘은 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));

이것은 매우 빠른 알고리즘이지만 data배열을 내림차순으로 정렬하면 훨씬 빠릅니다. .sort()알고리즘은 재귀 호출 이 훨씬 적기 때문에 사용 이 중요하지 않습니다 .


좋은. 그것은 당신이 숙련 된 프로그래머임을 보여줍니다 :)
James P.

1

펄 버전 (대답) :

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

결과:

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

자바 스크립트 버전 :

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 one-liner :

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

콜백이있는 가장 좋아하는 단일 라이너 :

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

이것은 JS가 Dynamic Sum (동적 솔루션)으로 누구나 특정 금액을 얻을 수있는 방법을 알려줍니다. 시간과 공간의 복잡성을 생각하면 올바른 솔루션이 될 수 있습니다.


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 솔루션이 마음에 들지 않아 부분 적용, 클로저 및 재귀를 사용하여 직접 빌드하는 이유를 알았습니다.

좋아, 나는 주로 조합 배열이 요구 사항 목표를 충족시킬 수 있는지에 대해 걱정했지만이 접근법을 사용하면 나머지 조합을 찾을 수 있습니다.

여기서 목표를 설정하고 조합 배열을 전달하십시오.

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

    console.log( permutation );
}

내가 생각 해낸 현재 구현

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
    };
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.