การหาตัวเลขที่เป็นไปได้ทั้งหมดเพื่อให้ได้ผลรวมที่กำหนด


232

คุณจะไปเกี่ยวกับการทดสอบการรวมกันที่เป็นไปได้ทั้งหมดจากชุดNของตัวเลขที่กำหนดเพื่อให้พวกเขารวมถึงจำนวนสุดท้ายที่กำหนด?

ตัวอย่างสั้น ๆ :

  • ชุดของตัวเลขที่จะเพิ่ม: N = {1,5,22,15,0,...}
  • ผลลัพธ์ที่ต้องการ: 12345

@ James - ฉันคิดว่าปัญหาของคุณต้องได้รับการชี้แจง กฎคืออะไร? คุณสามารถเลือกหมายเลขได้หรือไม่? ตัวเลขใดบ้างในชุด คุณมีข้อ จำกัด อะไรบ้าง?
jmort253

9
บทความวิกิพีเดีย ( en.wikipedia.org/wiki/Subset_sum_problem ) กล่าวถึงว่าปัญหานี้เป็นการแนะนำที่ดีสำหรับปัญหา NP-Complete
user57368

1
@ jmort253: ฉันไม่คิดว่าจะมีข้อ จำกัด ใด ๆ นอกเหนือจากการมีชุดของจำนวนเต็มซึ่งเป็นค่าบวกและต่ำกว่าจำนวนที่กำหนดเป็นเป้าหมาย การรวมกันของตัวเลขใด ๆ สามารถนำมาใช้ ไม่ใช่การบ้าน แต่เป็นปัญหาที่คุณจะได้รับหากคุณสมัครงานบางอย่าง ฉันมักจะคิดอัลกอริทึมเมื่อจำเป็น แต่ฉันไม่แน่ใจว่าจะดูอะไรเช่นนี้ มันจะต้องมีการย่อยสลายอย่างใด (recursive?)
James P.

3
@ James, คุณต้องการชุดค่าผสมหรือเพียงแค่จำนวนชุดย่อยที่รวมกันได้ไม่เกิน?
st0le

6
เราสามารถใช้องค์ประกอบเดิมของชุดเดิมมากกว่าหนึ่งครั้งได้หรือไม่? ตัวอย่างเช่นถ้าอินพุตเป็น {1,2,3,5} และเป้าหมาย 10 คือ 5 + 5 = 10 เป็นโซลูชันที่ยอมรับได้หรือไม่
alampada

คำตอบ:


248

ปัญหานี้สามารถแก้ไขได้ด้วยการรวมซ้ำของจำนวนเงินที่เป็นไปได้ทั้งหมดที่กรองออกไปถึงเป้าหมาย นี่คืออัลกอริทึมใน 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

อัลกอริธึมประเภทนี้มีการอธิบายที่ดีมากในการบรรยายการเขียนโปรแกรมนามธรรมของ Standfordต่อไปนี้- วิดีโอนี้แนะนำให้เข้าใจอย่างมากว่าการเรียกซ้ำทำงานเพื่อสร้างวิธีการเรียงสับเปลี่ยน

แก้ไข

ข้างต้นเป็นฟังก์ชั่นเครื่องกำเนิดไฟฟ้าทำให้มีประโยชน์มากขึ้น ต้องหลาม 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 ของฉันค่อนข้างสนิม แต่ฉันคิดว่าเข้าใจง่าย

การแปลง C # ของโซลูชัน Java: (โดย @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);
    }
}

โซลูชัน Ruby: (โดย @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-ยาก มันสามารถแก้ไขได้ในเวลาเอ็กซ์โพเนนเชียลเวลา 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)สร้างเพียง 175 สาขาเพราะเป้าหมายที่จะไปถึง10ได้รับการกรองชุดค่าผสมจำนวนมาก

หากNและTargetเป็นจำนวนมากหนึ่งควรย้ายไปเป็นรุ่นโดยประมาณของการแก้ปัญหา


1
การเพิ่มประสิทธิภาพ Java: ArrayList <Integer> partial_rec = new 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]การแก้ปัญหา
cbrad

3
@cbrad ว่าเป็นเพราะในi+1 remaining = numbers[i+1:]ดูเหมือนว่าอัลกอริทึมนั้นไม่อนุญาตให้มีการซ้ำซ้อน
Leonid Vasilev

1
@cbrad เพื่อรับโซลูชันรวมถึงรายการที่ซ้ำกันเช่น[1, 1, 3]ดูที่stackoverflow.com/a/34971783/3684296 (Python)
เมซ่า

36

การแก้ปัญหานี้ได้รับล้านครั้งบนอินเทอร์เน็ต ปัญหาที่เรียกว่าปัญหาเหรียญเปลี่ยนแปลง สามารถหาวิธีแก้ไขได้ที่http://rosettacode.org/wiki/Count_the_coinsและแบบจำลองทางคณิตศาสตร์ของมันที่http://jaqm.ro/issues/volume-5,issue-2/pdfs/patterson_harmel.pdf (หรือการเปลี่ยนเหรียญของ Google ปัญหา )

โดยวิธีการแก้ปัญหา Scala โดย Tsagadai เป็นที่น่าสนใจ ตัวอย่างนี้สร้าง 1 หรือ 0 เป็นผลข้างเคียงโดยจะแสดงรายการบนโซลูชันที่เป็นไปได้ทั้งหมดบนคอนโซล มันแสดงวิธีแก้ปัญหา แต่ไม่สามารถใช้งานได้ แต่อย่างใด

เพื่อให้มีประโยชน์มากที่สุดโค้ดควรส่งคืน a List[List[Int]]เพื่อให้ได้จำนวนโซลูชัน (ความยาวของรายการ), โซลูชัน "ดีที่สุด" (รายการสั้นที่สุด) หรือโซลูชันที่เป็นไปได้ทั้งหมด

นี่คือตัวอย่าง มันไม่มีประสิทธิภาพมาก แต่ก็เข้าใจง่าย

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

ในHaskell :

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

และเจ :

(]#~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

เวอร์ชัน 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


รหัสมีข้อผิดพลาดในส่วนที่เป็นremaining = numbers.slice(); remaining.slice(i + 1);อย่างอื่นควรจะnumbers.slice(i + 1);เปลี่ยนอาร์เรย์ตัวเลข
Emeeus

@Emeeus ฉันไม่คิดว่าจริง sliceส่งคืนสำเนา (ตื้น) แต่จะไม่แก้ไขnumbersอาร์เรย์
Dario Seidl

@DarioSeidl ใช่ชิ้นส่งคืนสำเนามันไม่ได้แก้ไขอาร์เรย์นั่นคือจุดนั่นคือสาเหตุที่ถ้าคุณไม่ได้กำหนดให้เป็นตัวแปรที่คุณไม่เปลี่ยน ในกรณีนี้ตามที่ฉันเข้าใจว่าเราต้องผ่านการแก้ไขเวอร์ชันไม่ใช่ต้นฉบับ ดูjsfiddle.net/che06t3w/1
Emeeus

1
@Redu ... เช่นวิธีง่ายๆในการทำเช่นนั้นเราสามารถปรับเปลี่ยนอัลกอริทึมและใช้ฟังก์ชันภายใน: jsbin.com/lecokaw/edit?js,console
rbarilani

1
รหัสที่ให้มาไม่จำเป็นต้องได้รับชุดค่าผสมทั้งหมด .. เช่นการใส่ [1,2], 3 จะคืนค่า 1 + 2 = 3 ไม่ใช่ 1 + 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

ฉันคิดว่าฉันจะใช้คำตอบจากคำถามนี้ แต่ฉันทำไม่ได้ดังนั้นนี่คือคำตอบของฉัน มันถูกใช้เป็นรุ่นที่แก้ไขของคำตอบในโครงสร้างและการแปลความหมายของโปรแกรมคอมพิวเตอร์ ฉันคิดว่านี่เป็นวิธีแก้ปัญหาแบบเรียกซ้ำได้ดีกว่าและควรทำให้นักปราชญ์พอใจมากขึ้น

คำตอบของฉันอยู่ใน Scala (และขอโทษถ้า Scala ของฉันดูดฉันเพิ่งเริ่มเรียนรู้มัน) 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 (ช่วง (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)ไม่ควรคืนค่าโซลูชันเนื่องจากไม่มีการรวมกันของ 1 และ 2 ที่เพิ่มเข้ากับ 4 สิ่งที่ต้องเพิ่มในการทำงานของฉันคือการหลบหนีถ้าiมากกว่าความยาวของnumbers
Mark

3

นี่คือเวอร์ชัน Java ซึ่งเหมาะสำหรับ N ขนาดเล็กและผลรวมเป้าหมายที่มีขนาดใหญ่มากเมื่อความซับซ้อนO(t*N)(โซลูชันแบบไดนามิก) มากกว่าอัลกอริทึมแบบเอ็กซ์โปเนนเชียล เวอร์ชันของฉันใช้นัดพบในการโจมตีตรงกลางพร้อมกับขยับเล็กน้อยเพื่อลดความซับซ้อนจากความไร้เดียงสาแบบคลาสสิค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 ระบบจะพิมพ์ชุดค่าผสมทั้งหมด (แต่จะไม่ใช้วิธีที่มีประสิทธิภาพ)

มันมีประสิทธิภาพมากที่จะคำนวณชุดค่าผสมมากกว่า 10 ^ 14 ในเวลาน้อยกว่า 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");

}

สวัสดี! ฉันต้องการรหัสเพื่อทำอะไรเช่นนั้นหาผลรวมของชุดตัวเลข 6 ตัวในรายการ 60 หมายเลข ผลรวมควรอยู่ในช่วงต่ำสุด 180, สูงสุด 191. สามารถปรับรหัสนั้นได้หรือไม่? จะเรียกใช้รหัสนั้นบนคลาวด์ได้ที่ไหน ฉันพยายามไม่ประสบความสำเร็จที่ Codenvy
คืน

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. ไปที่หน้า "รวมไปยังเป้าหมาย"
  3. ดาวน์โหลดไฟล์ "Sum to Target" excel

    ทำตามคำแนะนำในหน้าเว็บไซต์

หวังว่านี่จะช่วยได้


1

การแปลง Swift 3 ของโซลูชัน Java: (โดย @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

PHP Versionซึ่งได้รับแรงบันดาลใจจากเวอร์ชั่น C # ของ Keith Beller

รุ่น PHP ของ bala ไม่ทำงานสำหรับฉันเพราะฉันไม่จำเป็นต้องจัดกลุ่มตัวเลข ฉันต้องการการนำไปใช้ที่ง่ายขึ้นด้วยค่าเป้าหมายเดียวและกลุ่มของตัวเลข ฟังก์ชันนี้จะตัดรายการที่ซ้ำกัน

/**
 * 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 ในตอนแรก Zero เป็นตัวบ่งชี้สำหรับการเพิ่มดังนั้นมันจึงไร้ประโยชน์โดยกฎหมาย monoid ในกรณีนี้ ยังอนุมานจำนวนลบเช่นกันหากคุณต้องการปีนขึ้นไปเป็นจำนวนบวก มิฉะนั้นคุณจะต้องทำการลบด้วย

ดังนั้น ... อัลกอริทึมที่เร็วที่สุดที่คุณจะได้รับจากงานนี้มีดังต่อไปนี้ใน 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

รุ่น Perl (ของคำตอบที่นำ):

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

รุ่น 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 หนึ่งซับที่ส่งคืนผลลัพธ์จริง (แทนที่จะพิมพ์):

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 เพื่อบอกจำนวนผู้ที่ได้รับผลรวมแน่นอน นี่อาจเป็นทางออกที่ถูกต้องหากคุณคิดถึงเวลาและความซับซ้อนของพื้นที่


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 ฉันเห็นว่าเหตุใดฉันจึงสร้างบัญชี myselft โดยใช้การปิดและการเรียกซ้ำบางส่วน:

ตกลงฉันกังวลเกี่ยวกับว่าชุดค่าผสมสามารถตอบสนองเป้าหมายความต้องการได้หรือไม่ แต่ด้วยวิธีการนี้คุณสามารถเริ่มค้นหาชุดค่าผสมที่เหลือได้

ที่นี่เพียงกำหนดเป้าหมายและส่งผ่านชุดค่าผสม

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.