Tìm tất cả các tổ hợp số có thể để đạt được một tổng nhất định


232

Làm thế nào bạn có thể kiểm tra tất cả các kết hợp bổ sung có thể có từ một bộ Nsố đã cho để chúng cộng với một số cuối cùng nhất định?

Một ví dụ ngắn gọn:

  • Đặt số để thêm: N = {1,5,22,15,0,...}
  • Kết quả như ý: 12345

@James - Tôi nghĩ vấn đề của bạn cần được làm rõ. Các quy tắc là gì? Bạn có thể chọn bất kỳ số nào? Những con số nào trong tập hợp? Những hạn chế của bạn là gì?
jmort253

9
Bài viết trên wikipedia ( en.wikipedia.org/wiki/Subset_sum_pro Hiệu ) thậm chí còn đề cập rằng vấn đề này là một giới thiệu tốt về lớp các vấn đề hoàn thành NP.
dùng57368

1
@ jmort253: Tôi không nghĩ có bất kỳ ràng buộc nào ngoài việc có một bộ số nguyên dương và thấp hơn số được đưa ra làm mục tiêu. Bất kỳ sự kết hợp của số có thể được sử dụng. Đó không phải là bài tập về nhà mà là loại vấn đề bạn sẽ được đưa ra để giải quyết nếu bạn áp dụng cho một số công việc. Tôi thường có thể nghĩ ra một thuật toán khi cần nhưng tôi không chắc làm thế nào để xem một cái gì đó như thế này. Nó cần phải được phân tách bằng cách nào đó (đệ quy?).
James P.

3
@James, Bạn có cần các kết hợp hoặc chỉ số lượng tập hợp con cộng với tổng không?
st0le

6
Chúng ta có thể sử dụng cùng một yếu tố của bộ gốc nhiều lần không? Ví dụ: nếu đầu vào là {1,2,3,5} và mục tiêu 10, thì 5 + 5 = 10 có phải là một giải pháp chấp nhận được không?
alampada

Câu trả lời:


248

Vấn đề này có thể được giải quyết với sự kết hợp đệ quy của tất cả các khoản tiền có thể lọc ra những mục tiêu đạt được mục tiêu. Đây là thuật toán trong Python:

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

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

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


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

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

Loại thuật toán này được giải thích rất rõ trong bài giảng Lập trình trừu tượng sau đây của Standford - video này rất được khuyến khích để hiểu cách thức đệ quy hoạt động để tạo ra hoán vị các giải pháp.

Biên tập

Ở trên là một hàm tạo, làm cho nó hữu ích hơn một chút. Yêu cầu Python 3.3+ vì 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)

Đây là phiên bản Java của cùng một thuật toán:

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

Nó chính xác là cùng heuristic. Java của tôi hơi thô nhưng tôi nghĩ là dễ hiểu.

Chuyển đổi C # của giải pháp Java: (bởi @JeremyThndry)

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

Dung dịch Ruby: (bởi @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)

Chỉnh sửa: thảo luận phức tạp

Như những người khác đề cập đây là một vấn đề NP-hard . Nó có thể được giải trong thời gian theo cấp số nhân O (2 ^ n), ví dụ với n = 10 sẽ có 1024 giải pháp khả thi. Nếu các mục tiêu bạn đang cố gắng đạt được trong phạm vi thấp thì thuật toán này hoạt động. Ví dụ:

subset_sum([1,2,3,4,5,6,7,8,9,10],100000) tạo ra 1024 chi nhánh vì mục tiêu không bao giờ được lọc ra các giải pháp có thể.

Mặt khác, subset_sum([1,2,3,4,5,6,7,8,9,10],10)chỉ tạo 175 chi nhánh, vì mục tiêu cần đạt 10được sẽ lọc ra nhiều kết hợp.

Nếu NTargetlà số lớn, người ta nên chuyển sang một phiên bản gần đúng của giải pháp.


1
Tối ưu hóa Java: ArrayList <Integer> part_rec = new ArrayList <Integer> (part); part_rec.add (n); đây là một bản sao của một phần. và do đó thêm O (N). Một cách tốt hơn là chỉ "part.add (n)" thực hiện đệ quy và sau đó "part.remove (part.size -1). Tôi chạy lại mã của bạn để đảm bảo. Nó hoạt động tốt
Christian Bongiorno

4
Giải pháp này không hoạt động cho tất cả các trường hợp. Hãy xem xét [1, 2, 0, 6, -3, 3], 3- nó chỉ xuất ra [1,2], [0,3], [3]trong khi các trường hợp bị thiếu như[6, -3, 3]
LiraNuna

11
Nó cũng không làm việc cho tất cả các kết hợp, ví dụ [1, 2, 5], 5chỉ ra [5], khi [1, 1, 1, 1, 1], [2, 2, 1][2, 1, 1, 1]là giải pháp.
cbrad

3
@cbrad đó là vì i+1trong remaining = numbers[i+1:]. Có vẻ như thuật toán đó không cho phép trùng lặp.
Leonid Vasilev

1
@cbrad Để nhận thêm các giải pháp bao gồm các bản sao như [1, 1, 3]hãy xem stackoverflow.com/a/34971783/3684296 (Python)
Mesa

36

Giải pháp cho vấn đề này đã được đưa ra hàng triệu lần trên Internet. Vấn đề được gọi là Vấn đề thay đổi tiền xu . Người ta có thể tìm thấy các giải pháp tại http://rosettacode.org/wiki/Count_the_coins và mô hình toán học của nó tại http://jaqm.ro/issues/volume-5,su-2/pdfs/patterson_harmel.pdf (hoặc thay đổi đồng xu của Google vấn đề ).

Nhân tiện, giải pháp Scala của Tsagadai, thật thú vị. Ví dụ này tạo ra 1 hoặc 0. Là một hiệu ứng phụ, nó liệt kê trên bàn điều khiển tất cả các giải pháp có thể. Nó hiển thị giải pháp, nhưng không thể sử dụng nó theo bất kỳ cách nào.

Để hữu ích nhất có thể, mã phải trả về List[List[Int]]để cho phép lấy số lượng giải pháp (độ dài của danh sách), giải pháp "tốt nhất" (danh sách ngắn nhất) hoặc tất cả các giải pháp có thể.

Đây là một ví dụ. Nó rất không hiệu quả, nhưng nó rất dễ hiểu.

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

Khi chạy, nó sẽ hiển thị:

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)

Các sumCombinations()chức năng có thể được sử dụng bởi chính nó, và kết quả có thể được phân tích thêm để hiển thị "tốt nhất" giải pháp (danh sách ngắn), hoặc số giải pháp (số lượng danh sách).

Lưu ý rằng ngay cả như thế này, các yêu cầu có thể không được đáp ứng đầy đủ. Nó có thể xảy ra rằng thứ tự của mỗi danh sách trong giải pháp là đáng kể. Trong trường hợp như vậy, mỗi danh sách sẽ phải được sao chép nhiều lần vì có sự kết hợp của các yếu tố của nó. Hoặc chúng ta có thể chỉ quan tâm đến các kết hợp khác nhau.

Ví dụ: chúng tôi có thể xem xét List(5, 10)nên đưa ra hai kết hợp: List(5, 10)List(10, 5). Đối với List(5, 5, 5)nó có thể cung cấp ba kết hợp hoặc chỉ một, tùy thuộc vào yêu cầu. Đối với các số nguyên, ba hoán vị là tương đương, nhưng nếu chúng ta đang xử lý các đồng tiền, như trong "vấn đề thay đổi tiền xu", thì không.

Cũng không được nêu trong các yêu cầu là câu hỏi liệu mỗi số (hoặc đồng xu) chỉ có thể được sử dụng một lần hay nhiều lần. Chúng ta có thể (và chúng ta nên!) Tổng quát hóa vấn đề thành một danh sách các danh sách xuất hiện của mỗi số. Điều này biến trong cuộc sống thực thành "những cách có thể để kiếm một số tiền nhất định với một bộ tiền xu (chứ không phải một bộ giá trị tiền xu)". Vấn đề ban đầu chỉ là một trường hợp cụ thể của trường hợp này, trong đó chúng tôi có càng nhiều lần xuất hiện của mỗi đồng tiền khi cần để tạo ra tổng số tiền với mỗi giá trị đồng xu duy nhất.


14
Vấn đề này không hoàn toàn giống như vấn đề thay đổi tiền xu. OP đang yêu cầu tất cả sự kết hợp, không chỉ là tối thiểu. Và, có lẽ, các số nguyên trong tập hợp có thể âm. Do đó, tối ưu hóa nhất định của vấn đề thay đổi tiền xu là không thể với vấn đề này.
ThomasMcLeod

5
và vấn đề này cũng cho phép lặp lại các mặt hàng, tôi không chắc OP muốn điều này, nhưng nhiều hơn một vấn đề về chiếc ba lô
caub

34

Trong Haskell :

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

J :

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

Như bạn có thể nhận thấy, cả hai đều có cùng một cách tiếp cận và chia vấn đề thành hai phần: tạo từng thành viên của tập hợp sức mạnh và kiểm tra tổng của từng thành viên cho mục tiêu.

Có những giải pháp khác nhưng đây là cách đơn giản nhất.

Bạn có cần giúp đỡ với một trong hai, hoặc tìm một cách tiếp cận khác không?


3
Wow, đó là một số mã khá súc tích. Tôi ổn với câu trả lời của bạn. Tôi nghĩ rằng tôi chỉ cần đọc lên một chút về các thuật toán nói chung. Tôi sẽ xem cú pháp của hai ngôn ngữ khi bạn khơi dậy sự tò mò của tôi.
James P.

Tôi vừa cài đặt Haskell để dùng thử, chắc chắn không thể chỉ dán nó vào và thực hiện nó, not in scope: 'subsequences'có con trỏ nào không?
Hart CO

4
@HartCO hơi trễ bữa tiệc, nhưngimport Data.List
Jir

28

Phiên bản Javascript:

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

  partial = partial || [];

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

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

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

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

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

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


Mã có lỗi trong lát cắt, remaining = numbers.slice(); remaining.slice(i + 1);nếu không thì sẽ numbers.slice(i + 1);thay đổi mảng số
Emeeus

@Emeeus, tôi không nghĩ đó là sự thật. slicetrả về một bản sao (nông), nó không sửa đổi numbersmảng.
Dario Seidl

@DarioSeidl có, lát trả về một bản sao, nó không sửa đổi mảng, đó là điểm, đó là lý do tại sao nếu bạn không gán nó thành một biến bạn không thay đổi nó. Trong trường hợp này, theo tôi hiểu, chúng ta phải vượt qua một phiên bản sửa đổi, không phải bản gốc. Xem jsfiddle.net/che06t3w/1
Emeeus

1
@Redu ... ví dụ một cách dễ dàng để làm điều đó là, chúng ta có thể sửa đổi một chút thuật toán và sử dụng hàm bên trong: jsbin.com/lecokaw/edit?js,console
rbarilani

1
Mã được cung cấp không nhất thiết phải có tất cả các kết hợp .. ví dụ: đặt [1,2], 3 sẽ chỉ trả về 1 + 2 = 3 chứ không phải 1 + 1 + 1 hoặc 2 + 1
JuicY_Burrito

12

Phiên bản C ++ của cùng một thuật toán

#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

Phiên bản C # của câu trả lời mã @msalvadores

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

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

4

Tôi nghĩ rằng tôi sẽ sử dụng một câu trả lời từ câu hỏi này nhưng tôi không thể, vì vậy đây là câu trả lời của tôi. Nó đang sử dụng một phiên bản sửa đổi của một câu trả lời trong Cấu trúc và Giải thích các Chương trình Máy tính . Tôi nghĩ rằng đây là một giải pháp đệ quy tốt hơn và nên làm hài lòng những người theo chủ nghĩa thuần túy hơn.

Câu trả lời của tôi là ở Scala (và xin lỗi nếu Scala của tôi hút, tôi mới bắt đầu học nó). Sự điên rồ của findSumCombinating là sắp xếp và duy nhất danh sách ban đầu cho đệ quy để ngăn chặn các bản sao.

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

Để dùng nó:

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

4
Thank you.. ephemient

tôi đã chuyển đổi logic trên từ python sang 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

Một giải pháp python khác là sử dụng itertools.combinationsmô-đun như sau:

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

Đầu ra: [(8, 7), (5, 10), (3, 8, 4), (3, 5, 7)]


nó không hoạt động, ví dụ: find_sum_in_list (phạm vi (0,8), 4). Đã tìm thấy: [(4,), (0, 4), (1, 3), (0, 1, 3)]. Nhưng (2, 2) cũng là một lựa chọn!
Andre Araujo

@AndreAraujo: không có ý nghĩa gì khi sử dụng 0, nhưng nếu bạn sử dụng (1,8) thì itertools.combinations_with numplocation hoạt động và cũng cho ra 2.2.
Rubenisme

@Rubenisme Vâng, anh bạn! Vấn đề là sự thay thế! Cảm ơn! ;-)
Andre Araujo

4

Đây là một giải pháp trong 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))
  }
}

Tôi đang tìm kiếm một giải pháp trong R, nhưng giải pháp này không phù hợp với tôi. Ví dụ, subset_sum(numbers = c(1:2), target = 5)trả về "sum(1+2+2)=5". Nhưng kết hợp 1 + 1 + 1 + 1 + 1 bị thiếu. Đặt mục tiêu thành số cao hơn (ví dụ 20) thậm chí còn thiếu nhiều kết hợp hơn.
Frederick

Những gì bạn mô tả không phải là những gì chức năng dự định trả lại. Nhìn vào câu trả lời được chấp nhận. Việc 2 được lặp lại hai lần là một yếu tố của cách R tạo và tập hợp chuỗi con, không phải là hành vi dự định.
Đánh dấu

subset_sum(1:2, 4)không nên trả về giải pháp nào vì không có kết hợp 1 và 2 nào thêm vào 4. Những gì cần thêm vào chức năng của tôi là một lối thoát nếu ilớn hơn độ dài củanumbers
Đánh dấu

3

Đây là một phiên bản Java rất phù hợp với N nhỏ và tổng mục tiêu rất lớn, khi độ phức tạp O(t*N)(giải pháp động) lớn hơn thuật toán theo cấp số nhân. Phiên bản của tôi sử dụng một cuộc gặp gỡ trong cuộc tấn công giữa, cùng với một chút thay đổi để giảm sự phức tạp từ ngây thơ cổ điển O(n*2^n)sang O(2^(n/2)).

Nếu bạn muốn sử dụng điều này cho các tập hợp có từ 32 đến 64 phần tử, bạn nên thay đổi phần tử intđại diện cho tập hợp con hiện tại trong hàm bước thành longhiệu suất mặc dù rõ ràng sẽ giảm đáng kể khi kích thước tập tăng. Nếu bạn muốn sử dụng điều này cho một tập hợp có số phần tử lẻ, bạn nên thêm 0 vào tập hợp để làm cho nó được đánh số chẵn.

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

Điều này tương tự như một vấn đề thay đổi tiền xu

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

Thuật toán rất hiệu quả bằng cách sử dụng các bảng tôi đã viết trong vài năm trước.

Nếu bạn đặt IN 1, nó sẽ in tất cả các kết hợp (nhưng nó sẽ không sử dụng phương pháp hiệu quả).

Nó hiệu quả đến mức nó tính toán hơn 10 ^ 14 kết hợp trong chưa đầy 10ms.

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

}

chào bạn Tôi cần một mã để làm một cái gì đó như thế, tìm tất cả các tổng có thể có của 6 số trong một danh sách 60 số. Các khoản tiền phải nằm trong phạm vi tối thiểu 180, tối đa 191. Mã đó có thể được điều chỉnh cho điều đó không? Nơi để chạy mã đó trên đám mây? Tôi đã cố gắng không thành công tại Codenvy
defreturn

2

Phiên bản VBA Excel bên dưới. Tôi cần thực hiện điều này trong VBA (không phải sở thích của tôi, đừng phán xét tôi!) Và sử dụng các câu trả lời trên trang này cho cách tiếp cận. Tôi đang tải lên trong trường hợp những người khác cũng cần phiên bản 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

Đầu ra (được ghi vào cửa sổ Ngay lập tức) phải là:

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

2

Đây là phiên bản tốt hơn với định dạng đầu ra tốt hơn và các tính năng 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

Phiên bản không đệ quy Java chỉ đơn giản là tiếp tục thêm các phần tử và phân phối lại chúng trong số các giá trị có thể. 0Các bị bỏ qua và hoạt động cho các danh sách cố định (những gì bạn đưa ra là những gì bạn có thể chơi với) hoặc danh sách các số lặp lại.

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

Đầu vào mẫu:

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

Đầu ra mẫu:

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

Để tìm các kết hợp bằng excel - (nó khá dễ). (Máy tính của bạn không được quá chậm)

  1. Tới trang này
  2. Chuyển đến trang "Tổng hợp để nhắm mục tiêu"
  3. Tải xuống tệp excel "Sum to Target".

    Thực hiện theo các hướng dẫn trên trang web.

hi vọng điêu nay co ich.


1

Chuyển đổi Swift 3 giải pháp Java: (bởi @JeremyThndry)

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
    }

}

sử dụng:

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

Điều này có thể được sử dụng để in tất cả các câu trả lời là tốt

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

Độ phức tạp thời gian là cấp số nhân. Thứ tự 2 ^ n


1

Tôi đã làm một cái gì đó tương tự cho một nhiệm vụ scala. Suy nghĩ về việc đăng giải pháp của tôi ở đây:

 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

Tôi đã chuyển mẫu C # sang Objective-c và không thấy nó trong các phản hồi:

//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's trả lời với tên biến thay đổi một chút và một số ý kiến.

    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

Phiên bản PHP , lấy cảm hứng từ phiên bản C # của Keith Beller's.

Phiên bản PHP của bala không hoạt động với tôi, vì tôi không cần phải nhóm số. Tôi muốn thực hiện đơn giản hơn với một giá trị đích và một nhóm số. Chức năng này cũng sẽ cắt bất kỳ mục trùng lặp.

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

Đề xuất như một câu trả lời:

Đây là một giải pháp sử dụng máy phát điện 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)
  }

}

Sử dụng các trình tạo thực sự có thể rất hữu ích vì nó cho phép bạn tạm dừng thực thi tập lệnh ngay khi tìm thấy một tập hợp con hợp lệ. Điều này trái ngược với các giải pháp không có máy phát điện (tức là thiếu trạng thái) phải lặp lại qua từng tập hợp con củanumbers


1

Giảm 0 ở vị trí đầu tiên. Zero là một định danh để bổ sung nên nó vô dụng bởi các luật đơn trị trong trường hợp cụ thể này. Cũng suy ra số âm cũng như nếu bạn muốn leo lên một số dương. Nếu không, bạn cũng sẽ cần hoạt động trừ.

Vì vậy, ... thuật toán nhanh nhất bạn có thể nhận được trong công việc cụ thể này như sau được đưa ra trong 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));

Đây là một thuật toán rất nhanh nhưng nếu bạn sắp xếp datamảng giảm dần thì nó sẽ còn nhanh hơn nữa. Việc sử dụng .sort()là không đáng kể vì thuật toán sẽ kết thúc với các yêu cầu đệ quy ít hơn nhiều .


Đẹp. Nó cho thấy bạn là một lập trình viên giàu kinh nghiệm :)
James P.

1

Phiên bản Perl (của câu trả lời hàng đầu):

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

Kết quả:

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

Phiên bản Javascript:

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

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

Javascript một lớp thực sự trả về kết quả (thay vì in nó):

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

Và yêu thích của tôi, một lót với gọi lại:

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

Đây là một Giải pháp động cho JS để cho biết có bao nhiêu cách mọi người có thể nhận được số tiền nhất định. Đây có thể là giải pháp phù hợp nếu bạn nghĩ về sự phức tạp của thời gian và không gian.


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

Tôi không thích Giải pháp Javascript Tôi thấy rằng tại sao tôi xây dựng một giải pháp cho myselft bằng cách áp dụng một phần, đóng và đệ quy:

Ok, tôi chủ yếu quan tâm đến việc mảng kết hợp có thể đáp ứng mục tiêu yêu cầu hay không, nhưng với cách tiếp cận này, bạn có thể bắt đầu tìm phần còn lại của các kết hợp

Ở đây chỉ cần đặt mục tiêu và vượt qua mảng kết hợp.

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

    console.log( permutation );
}

việc thực hiện hiện tại tôi đã đưa ra

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
    };
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.