File Coverage

blib/lib/Algorithm/Knapsack.pm
Criterion Covered Total %
statement 32 32 100.0
branch 6 6 100.0
condition n/a
subroutine 6 6 100.0
pod 3 3 100.0
total 47 47 100.0


line stmt bran cond sub pod time code
1             # $Id: Knapsack.pm,v 1.11 2004/10/23 18:52:19 alex Exp $
2              
3             package Algorithm::Knapsack;
4              
5 1     1   21441 use strict;
  1         2  
  1         43  
6 1     1   7 use vars qw($VERSION);
  1         1  
  1         585  
7              
8             $VERSION = '0.02';
9              
10             =head1 NAME
11              
12             Algorithm::Knapsack - brute-force algorithm for the knapsack problem
13              
14             =head1 SYNOPSIS
15              
16             use Algorithm::Knapsack;
17              
18             my $knapsack = Algorithm::Knapsack->new(
19             capacity => $capacity,
20             weights => \@weights,
21             );
22              
23             $knapsack->compute();
24              
25             foreach my $solution ($knapsack->solutions()) {
26             foreach my $index (@{$solution}) {
27             # do something with $weights[$index]
28             }
29             }
30              
31             =head1 DESCRIPTION
32              
33             The knapsack problem asks, given a set of items of various weights, find a
34             subset or subsets of items such that their total weight is no larger than
35             some given capacity but as large as possible.
36              
37             This module solves a special case of the 0-1 knapsack problem when the
38             value of each item is equal to its weight. Capacity and weights are
39             restricted to positive integers.
40              
41             =head1 METHODS
42              
43             =over 7
44              
45             =item B
46              
47             my $knapsack = Algorithm::Knapsack->new(
48             capacity => $capacity,
49             weights => \@weights,
50             );
51              
52             Creates a new Algorith::Knapsack object. Value of $capacity is a
53             positive integer and \@weights is a reference to an array of positive
54             integers, each of which is less than $capacity.
55              
56             =cut
57              
58             sub new {
59 1     1 1 12 my $class = shift;
60 1         7 my $self = {
61             capacity => 0, # total capacity of this knapsack
62             weights => [], # weights to be packed into the knapsack
63             @_,
64             solutions => [], # lol of indexes to weights
65             emptiness => 0, # capacity minus sum of weights in a solution
66             };
67 1         4 bless $self, $class;
68             }
69              
70             =item B
71              
72             $knapsack->compute();
73              
74             Iterates over all possible combinations of weights to solve the knapsack
75             problem. Note that the time to solve the problem grows exponentially with
76             respect to the number of items (weights) to choose from.
77              
78             =cut
79              
80             sub compute {
81 1     1 1 744 my $self = shift;
82 1         6 $self->{emptiness} = $self->{capacity};
83 1         3 $self->_knapsack($self->{capacity}, [0 .. $#{ $self->{weights} }], []);
  1         7  
84             }
85              
86             sub _knapsack {
87 52     52   55 my $self = shift;
88 52         50 my $capacity = shift;
89 52         52 my @indexes = @{ shift() };
  52         107  
90 52         55 my @knapsack = @{ shift() };
  52         91  
91              
92 52         204 while ($#indexes >= 0) {
93 59         68 my $index = shift @indexes;
94 59 100       216 next if $self->{weights}->[$index] > $capacity;
95              
96 51 100       111 if ($capacity - $self->{weights}->[$index] < $self->{emptiness}) {
97 6         11 $self->{emptiness} = $capacity - $self->{weights}->[$index];
98 6         10 $self->{solutions} = [];
99             }
100 51 100       115 if ($capacity - $self->{weights}->[$index] == $self->{emptiness}) {
101 8         8 push(@{ $self->{solutions} }, [@knapsack, $index]);
  8         19  
102             }
103              
104 51         167 $self->_knapsack($capacity - $self->{weights}->[$index],
105             \@indexes,
106             [@knapsack, $index]);
107             }
108             }
109              
110             =item B
111              
112             my @solutions = $knapsack->solutions();
113              
114             Returns a list of solutions. Each solution is a reference to an array of
115             indexes to @weights.
116              
117             =cut
118              
119             sub solutions {
120 1     1 1 6 my $self = shift;
121 1         2 return @{ $self->{solutions} };
  1         4  
122             }
123              
124             1;
125              
126             __END__