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