Basic Concept
In the Sum-of-Subsets problem, there are n positive integers (weights) $w_i$ and a positive integer W. The goal is to find all subsets of the integers that sum to W. As mentioned earlier, we usually state our problems so as to find all solutions.
๐ ๋ถ๋ถ์งํฉ์ ํฉ์ ๊ตฌํ๋ ๋ฌธ์ ๋ n๊ฐ์ ์์ ์ ์(๋ฌด๊ฒ) $w_i$์ ์์ ์ ์ $W$๊ฐ ์ฃผ์ด์ก์ ๋, ํฉ์ด $W$๊ฐ ๋๋ ์ ์์ ๋ถ๋ถ์งํฉ์ ๋ชจ๋ ์ฐพ๋ ๊ฒ์ด๋ค. n-Queens ๋ฌธ์ ์ฒ๋ผ ๋ชจ๋ ํด๋ต์ ์ฐพ๋๋ก ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌํํ ๊ฒ์ด๋ค.
Example Suppose that n = 5, W = 21, and
$$ w_1=5 \quad w_2=6 \quad w_3=10 \quad w_4=11 \quad w_5=16. $$
because
$$ \begin{align}w_1 + w_2 + w_3 &= 5 + 6 + 10 = 21, \notag{}\\ w_1 + w_5 &= 5 + 16 = 21, \notag{}\\w_3 + w_4 &= 10 + 11 = 21, \notag{}\end{align}$$
the solutions are {$ w_1$, $w_2$, $w_3$}, {$w_1$, $w_5$}, and {$w_3$, $w_4$}.
Example Suppose that n = 4, W = 13, and
$$ w_1=3 \quad w_2=4 \quad w_3=5 \quad w_4=6. $$
becasue
$$ w_1 + w_2 + w_4 = 3 + 4 + 6 = 13$$
the solutions are {$ w_1, w_2, w_4$}.
The only solution is found at the node shaded in color. The nonpromising nodes are marked with crosses. The nodes containing 12, 8, and 9 are nonpromising because adding the next weight (6) would make the value of weight exceed W. The nodes containing 7, 3, 4, and 0 are nonpromising because there is not enough total weight remaining to bring the value of weight up to W.

๐ ๊ฐ ๋ ธ๋์๋ ๊ทธ ๋ ธ๋๊น์ง์ ์ด ํฉ์ด ์ ํ ์๋ค. ๊ฐ์ฅ ์ผ์ชฝ ์๋ ธ๋์ธ 12๋ 3 + 4 + 5์ ๊ฒฐ๊ณผ์ด๋ค. ๋ถ๋ชจ๋ ธ๋๋ฅผ ๊ธฐ์ค์ผ๋ก ์ผ์ชฝ ์์๋ ธ๋๋ $w_i$๋ฅผ ํฌํจ์ํจ๋ค๋ ๋ป์ด๋ฉฐ ์ค๋ฅธ์ชฝ ๋ ธ๋๋ $w_i$๋ฅผ ๋ฐฐ์ ํ๋ค๋ ์๋ฏธ์ด๋ค.
์์ ์ํ๊ณต๊ฐํธ๋ฆฌ์์ ์ ๋งํ์ง ์์ ์์๋ ธ๋๋ค์ X๋ก ํ์๋๋ค. 12, 8, 9๋ฅผ ํฌํจํ๋ ๋ ธ๋๋ค์ ๋ค์ ๋ฌด๊ฒ์ธ 6์ ์ถ๊ฐํ๋ฉด weight์ ๊ฐ์ด W๋ฅผ ์ด๊ณผํ๊ธฐ ๋๋ฌธ์ ์ ๋งํ์ง ์๋ค. 7, 3, 4, 0์ ํฌํจํ๋ ๋ ธ๋๋ค์ ๋จ์ ๋ฌด๊ฒ๋ฅผ ๋ํ์๋์ ์ดํฉ์ด W๊น์ง ๊ฐ๊ธฐ์๋ ์ถฉ๋ถํ์ง ์์ผ๋ฏ๋ก ์ ๋งํ์ง ์๋ค.
The Backtracking Algorithm for the Sum-of-Subsets Problem
Algorithm Design
If we sort the weights in nondecreasing order before doing the search, there are obvious signs that a node is nonpromising.
๐ ๋ถ๋ถํฉ ๋ฌธ์ ์ ์ํ๊ณต๊ฐํธ๋ฆฌ ์ ์ฒด๋ฅผ ๊น์ด์ฐ์ ํ์ํ๋ ๊ฒ์ ๋นํจ์จ์ ์ด๋ค. $w_1~ w_n$์ ์ค๋ฆ์ฐจ์์ผ๋ก ์ ๋ ฌํ๋ฉด ๊ฐ์ง์น๊ธฐ๋ฅผ ํ๋จํ ์ ์๋ ๋ถ๋ช ํ ์ฆ๊ฑฐ๋ค์ด ์๋ค.
If the weights are sorted in this manner, then $w_i$+1 is the lightest weight remaining when we are at the $i$th level. Let weight be the sum of the weights that have been included up to a node at level $i$. If the $w_{i+1}$ would bring the value of weight above W, then so would any other weight following it. Therefore, unless weight equals W (which means that there is a solution at the node), a node at the $i$th level is nonpromising if $weight + w_{i+1} < W$. The algorithm uses an array include. It sets include[i] to “YES” if w[i] is to be included and to “NO” if it is not.
- $weight$: the sum of the weights that have been included up to a node at level $i$
$$ weight + w_{i+1} > W \text{: a node is nonpromising.} $$
- $total$: the total weight of the remaining weights
$$weight + total < W \text{: a node is nonpromising.}$$
๐ $weight$๋ $i$๋ ๋ฒจ์ ์๋ ๋ ธ๋๊น์ง ํฌํจ๋ ๋ฌด๊ฒ์ ํฉ์ด๋ค. weight์ $w_i$+1์ ๋ํ ๊ฒฐ๊ณผ๊ฐ $W$๋ฅผ ์ด๊ณผํ๋ค๋ฉด ๊ทธ ์ดํ์ ์ด๋ค ๋ค๋ฅธ ๋ฌด๊ฒ๋ $W$๋ฅผ ๋๊ธธ ๊ฒ์ด๋ค. ์ฆ, $w_i+1$๋ถํฐ๋ ์ ๋งํ์ง ์์ผ๋ฏ๋ก ๊ทธ ์์ ๋ ธ๋๋ค์ ํ์๋์์์ ์ ์ธํ๋ค.
$total$์ ๋จ์์๋ ์์ดํ ์ ์ด ๋ฌด๊ฒ์ด๋ค. $total$์ $weight$๋ฅผ ๋ํด์ $W$๋ณด๋ค ์๋ค๋ฉด, ๊ทธ ๋ ธ๋ ์๋๋ก ํ์ฅ์ ํด๋ $W$์ ๊ฐ์์ง ์ ์๋ค. ๋ฐ๋ผ์ ์ด ๊ฒฝ์ฐ๋ ์ ๋งํ์ง ์์ผ๋ฏ๋ก ๊ฐ์ง์น๊ธฐ๋ฅผ ํตํด ํ์๋์์์ ์ ์ธํ๋ค.
$w_i$ ์ ํฌํจ์ฌ๋ถ๋ include[i] ๋ฐฐ์ด์ ํตํด ๊ด๋ฆฌํ๋ค. include[i]๋ $w_i$๋ฅผ ํฌํจํ๋ฉด “YES”, ๊ทธ๋ ์ง ์์ผ๋ฉด “NO” ๊ฐ์ ๊ฐ์ง๋ค.
When the sum of the weights included up to a node equals W , there is a solution at that node. Therefore, we cannot get another solution by including more items. This means that if
$$W = weight$$
we should print the solution and backtrack.
๐ W = weight ๋ฑ์์ด ์ฑ๋ฆฝํ๋ฉด ํด๋ต์ ์ถ๋ ฅํ๊ณ ๋ฐฑํธ๋ํนํ๋ค.
Pseudo Code
- The Backtracking Algorithm for the Sum-of-Subsets Problem
void sum_of_subsets(index i, int weight, int total)
{
if (promising(i))
if (weight == W)
cout << include[1] through include[i];
else {
include[i+1] = "yes"; // Include w[i+1]
sum_of_subsets(i+1, weight + w[i+1], total - w[i+1]);
include[i+1] = "no"; // Do not include w[i+1]
sum_of_subsets(i+1, weight, total - w[i+1]);
}
}
bool promising(index i)
{
return (weight + total >= W) && (weight == W || weight + w[i+1] <= W);
}
Soruce Code
- sumofsubs.h
// File: sumofsubs.h
#include <cstring>
using namespace std; // for string
namespace algorithms
{
void sum_of_subsets(int i, int weight, int total, int W, int* w, string* include);
// Problem: Given n positive integers (weights) and a positive integer W,
// determine all combinations of the integers that sum to W.
// Inputs: positive integer n, sorted (nondecreasing order) array of positive
// integers w indexed from 1 to n, and a positive integer W.
// Outputs: all combinations of the integers that sum to W.
bool promising(int i, int weight, int total, int W, int* w);
template <typename Item>
Item* get_vector_space(const int n)
// Prostcondition: Return the array containing n spaces
{
Item* v = new Item[n];
return (v-1); // offset the pointer
}
}
- sumofsubs.cpp
// File: sumofsubs.cpp
#include <iostream>
#include <iomanip> // Provides setw
#include "sumofsubs.h"
namespace algorithms
{
void sum_of_subsets(int i, int weight, int total, int W, int* w, string* include)
{
if (promising(i, weight, total, W, w))
{
if (weight == W)
{
for (int j = 1; j <= i; ++j)
cout << setw(3) << "w" << j << ": " << include[j] << setw(3);
cout << endl;
}
else
{
include[i + 1] = "YES"; // Include w[i+1]
sum_of_subsets(i + 1, weight + w[i + 1], total - w[i + 1], W, w, include);
include[i + 1] = "NO "; // Do not include w[i+1]
sum_of_subsets(i + 1, weight, total - w[i + 1], W, w, include);
}
}
}
bool promising(int i, int weight, int total, int W, int *w)
{
return (weight + total >= W) && (weight == W || weight + w[i + 1] <= W);
}
}
- sumofsubstest.cpp
// File: sumofsubstest.cpp
#include <iostream>
#include <cstdlib> // Provides EXIT_SUCCESS
#include "sumofsubs.h"
using namespace algorithms;
int main( )
{
const int N = 5;
const int W = 21;
int* w = get_vector_space<int>(N);
int total = 0;
string* include = get_vector_space<string>(N);
w[1] = 5;
w[2] = 6;
w[3] = 10;
w[4] = 11;
w[5] = 16;
// total ์ด๊ธฐ๊ฐ ๊ณ์ฐ
for (int i = 1; i <= 5; ++i)
total += w[i];
sum_of_subsets(0, 0, total, W, w, include);
delete [ ] (w + 1);
delete [ ] (include + 1);
return EXIT_SUCCESS;
}
Time Complexity Analysis
Worst-Case Time Complexity
$\displaystyle O(n) = 1 + 2 + 2^2 + 2^3 + … + 2^n = 2^{n+1} - 1 \in \Theta(2^n)$
As stressed before, even though the worst case is exponential, the algorithm can be efficient for many large instances.
๐ $w_i$๋ฅผ ํฌํจ์ํค๋๋ ๊ทธ๋ ์ง ์๋๋์ ๋ ๊ฐ์ง ์ ํ์ด๋ฏ๋ก ์ํ๊ณต๊ฐํธ๋ฆฌ ๋ด ์ ์ฒด ๋ ธ๋์ ์๋ ์ต๋ $2^{n+1}-1$๊ฐ๊ฐ ๋๋ค. ์ต์ ์ ๊ฒฝ์ฐ ์๊ฐ๋ณต์ก๋๊ฐ ์ง์์๊ฐ์ด์ง๋ง, ๊ฐ์ง์น๊ธฐ๋ฅผ ํตํด ์ํ๋ ธ๋์ ์๋ฅผ ์ค์ผ ์ ์๊ธฐ ๋๋ฌธ์ ์๊ณ ๋ฆฌ์ฆ์ ํจ์จ์ ๋ ์ข์ ์ ์๋ค.
n-Queens ๋ฌธ์ ์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ ๋งํ ๋ ธ๋์ ์๋ฅผ ์ ํํ ๊ตฌํ๊ธฐ ์ํ ๋ฐฉ๋ฒ์ ์ค์ ์๊ณ ๋ฆฌ์ฆ์ ์ํ ํ ๊ตฌ์ถ๋ ์ํ๊ณต๊ฐํธ๋ฆฌ์ ๋ ธ๋ ๊ฐฏ์๋ฅผ ์ธ์ด ๋ณด๋ ์ ๋ฐ์ ์๋ค. ๋ํ ๋ชฌํ ์นด๋ฅผ๋ก ์๊ณ ๋ฆฌ์ฆ์ ์ด์ฉํด ์๊ฐ๋ณต์ก๋๋ฅผ ์ถ์ ํ ์ ์๋ค.
'Algorithms > Backtracking' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| Backtracking with the 0-1 Knapsack problem(Depth-First Search)(0-1 ๋ฐฐ๋ญ ๋ฌธ์ -๊น์ด์ฐ์ ํ์) (2) | 2011.12.24 |
|---|---|
| Graph Coloring(๊ทธ๋ํ ์ฑ์) (0) | 2011.12.23 |
| n-Queens Problem(n-ํธ ๋ฌธ์ ) (0) | 2011.10.03 |
| Backtracking(๋ฐฑํธ๋ํน) (0) | 2011.08.06 |
