File Coverage

blib/lib/Algorithm/Knap01DP.pm
Criterion Covered Total %
statement 126 126 100.0
branch 22 28 78.5
condition 6 16 37.5
subroutine 11 11 100.0
pod 0 6 0.0
total 165 187 88.2


line stmt bran cond sub pod time code
1             package Algorithm::Knap01DP;
2 2     2   96333 use 5.008004;
  2         12  
  2         87  
3 2     2   12 use strict;
  2         4  
  2         74  
4 2     2   10 use warnings;
  2         9  
  2         90  
5 2     2   10 use Carp;
  2         3  
  2         190  
6 2     2   14924 use IO::File;
  2         60442  
  2         2914  
7              
8             our $VERSION = '0.25';
9             # Still a very early "alpha" version
10              
11             sub new {
12 8     8 0 27 my $class = shift;
13 8         110 my $self = {
14             capacity => 0, # total capacity of this knapsack
15             numobjects => 0, # number of objects
16             weights => [], # weights to be packed into the knapsack
17             profits => [], # profits to be packed into the knapsack
18             tableval => [], # f[k][c] DP table of values
19             tablesol => [], # x[k][c] DP table of sols
20             # (0 = out, 1 = in, 2 = in and out)
21             solutions => [], # list of lists of object indexes
22             filename => "", # name of the file the problem was read from
23             @_,
24             };
25            
26 8         21 croak "Profits and Weights don't have the same size"
27 8 100       15 unless scalar(@{$self->{weights}}) == scalar(@{$self->{profits}});
  8         336  
28              
29 7         127 bless $self, $class;
30             }
31              
32             sub Knap01DP {
33 5     5 0 8063 my $self = shift();
34 5         13 my $M = $self->{capacity};
35 5         9 my @w = @{$self->{weights}};
  5         26  
36 5         8 my @p = @{$self->{profits}};
  5         22  
37              
38 5 50       17 croak "Weight list is empty" unless (@w > 0);
39              
40 5         10 my $N = @w;
41 5         8 my (@f, @x);
42              
43 5         26 for my $c (0..$M) {
44 481 100       722 if ($w[0] <= $c) {
45 353         542 $f[0][$c] = $p[0];
46 353         497 $x[0][$c] = 1;
47             }
48             else {
49 128         178 $f[0][$c] = 0;
50 128         198 $x[0][$c] = 0;
51             }
52             }
53              
54 5         19 for my $k (1..$N-1) {
55 30         59 for my $c (0..$M) {
56 2872         3837 my $n = $f[$k-1][$c];
57 2872 100       4585 if ($c >= $w[$k]) {
58 2159         3638 my $y = $f[$k-1][$c-$w[$k]]+$p[$k];
59 2159 100       3931 if ($n < $y) {
    100          
60 1016         1348 $f[$k][$c] = $y;
61 1016         1998 $x[$k][$c] = 1;
62             }
63             elsif ($n > $y) {
64 995         1775 $f[$k][$c] = $n;
65 995         2163 $x[$k][$c] = 0;
66             }
67             else { # $n == $y
68 148         229 $f[$k][$c] = $n;
69 148         343 $x[$k][$c] = 2; # both ways
70             }
71             }
72             else {
73 713         1028 $f[$k][$c] = $n;
74 713         1277 $x[$k][$c] = 0;
75             }
76             }
77             }
78 5         157 ($self->{tableval}, $self->{tablesol}) = (\@f, \@x);
79             }
80              
81             sub solutions {
82 5     5 0 4940 my $self = shift();
83 5         16 my $N = $self->{numobjects};
84 5         44 my $M = $self->{capacity};
85 5         12 my @w = @{$self->{weights}};
  5         29  
86 5         9 my (@f, @x);
87              
88 5 50       6 $self->Knap01DP() if (!@{$self->{tableval}});
  5         23  
89 5         10 @f = @{$self->{tableval}};
  5         16  
90 5         9 @x = @{$self->{tablesol}};
  5         16  
91              
92 5         11 my ($k, $c, $s);
93              
94 5         30 my @sol = ({ sol=>[], cap=>$M });
95 5         21 for($k = $N-1; $k >= 0; $k--) {
96 35         48 my @temp = ();
97 35         45 foreach $s (@sol) {
98 44         64 $c = $s->{cap};
99 44 100       138 if ($x[$k][$c] == 1) {
    100          
100 25         31 unshift @{$s->{sol}}, $k;
  25         50  
101 25         74 $s->{cap} -= $w[$k];
102             }
103             elsif ($x[$k][$c] == 2) {
104 2         4 push @temp, {sol => [ @{$s->{sol}} ], cap =>$s->{cap}};
  2         7  
105 2         3 unshift @{$s->{sol}}, $k;
  2         5  
106 2         7 $s->{cap} -= $w[$k];
107             }
108             } # foreach $s
109 35 100       215 push @sol, @temp if @temp;
110             } # for
111 5         29 $self->{solutions} = \@sol;
112             }
113              
114             sub ReadKnap {
115 5     5 0 332 my $class = shift;
116 5         11 my $filename = shift;
117              
118 5         60 my $file = IO::File->new("< $filename");
119 5 50       597 croak "Can't open $filename" unless defined($file);
120 5         9 my (@w, @p);
121            
122 5         127 my $N = <$file>; chomp($N);
  5         15  
123 5         11 my $M = <$file>; chomp($M);
  5         6  
124 5         23 for (0..$N-1) {
125 35         69 $w[$_] = <$file>;
126 35         68 $p[$_] = <$file>;
127             }
128 5         13 chomp @w; chomp @p;
  5         9  
129 5         42 return $class->new(
130             capacity => $M,
131             numobjects => $N,
132             weights =>\@w,
133             profits => \@p,
134             filename => $filename);
135             }
136              
137             sub GenKnap {
138 2     2 0 866 my $class = shift;
139 2   50     12 my $N = (shift() || 17); # number of objects
140 2   33     10 my $R = (shift() || $N); # range
141              
142 2 50 33     26 croak "Number of objects and Range must be positive integers"
      33        
      33        
143             unless ($N > 0) and ($R > 0) and ($N == int($N)) and ($R == int($R));
144            
145 2         3 my ($x, $M, @w);
146 2         8 @w = map { $x = 1 + int(rand($R)); $M += $x; $x } 1..$N;
  34         38  
  34         28  
  34         51  
147 2         6 $M = int ($M / 2);
148 2         10 return $class->new(
149             capacity => $M,
150             numobjects => $N,
151             weights =>\@w,
152             profits => \@w,
153             filename => 'RANDOM');
154             }
155              
156             sub ShowResults {
157 5     5 0 35 my $self = shift();
158 5   50     33 my $width = (shift() || 8);
159 5         6 my @sol = @{$self->{solutions}};
  5         15  
160 5         9 my ($x, $i, $w);
161              
162 5         282 print "Problem: ";
163 5 50       869 print "$self->{filename}\n" if defined($self->{filename});
164 5         619 print "Number of Objects = $self->{numobjects} Capacity = $self->{capacity}\n";
165 5         17 for (@sol) {
166 7         14 my @s = @{$_->{sol}};
  7         27  
167              
168 7         11 $i = 1;
169 7         24 $w = 0;
170 7         13 for $x (@s) {
171 27         926 print "$x ($self->{weights}[$x])\t";
172 27         74 $w += $self->{weights}[$x];
173 27 50       70 print "\n" if ($i % $width) == 0;
174 27         44 $i++;
175             }
176 7         929 print "Used Capacity = $w\n";
177             }
178 5         593 print "Profit = $self->{tableval}[-1][-1]\n";
179             }
180              
181             1;
182             __END__