File Coverage

blib/lib/Algorithm/Munkres.pm
Criterion Covered Total %
statement 161 162 99.3
branch 50 50 100.0
condition 9 9 100.0
subroutine 18 18 100.0
pod 0 15 0.0
total 238 254 93.7


line stmt bran cond sub pod time code
1             package Algorithm::Munkres;
2              
3 13     13   394272 use 5.006;
  13         53  
  13         503  
4 13     13   79 use strict;
  13         23  
  13         10984  
5 13     13   108 use warnings;
  13         30  
  13         33296  
6              
7             require Exporter;
8              
9             our @ISA = qw(Exporter);
10              
11             our @EXPORT = qw( assign );
12              
13             our $VERSION = '0.08';
14              
15             #Variables global to the package
16             my @mat = ();
17             my @mask = ();
18             my @colcov = ();
19             my @rowcov = ();
20             my $Z0_row = 0;
21             my $Z0_col = 0;
22             my @path = ();
23              
24             #The exported subroutine.
25             #Expected Input: Reference to the input matrix (MxN)
26             #Output: Mx1 matrix, giving the column number of the value assigned to each row. (For more explaination refer perldoc)
27             sub assign
28             {
29             #reference to the input matrix
30 19     19 0 19718 my $rmat = shift;
31 19         47 my $rsolution_mat = shift;
32 19         45 my ($row, $row_len) = (0,0);
33              
34             # re-initialize that global variables
35 19         57 @mat = ();
36 19         52 @mask = ();
37 19         245 @colcov = ();
38 19         35 @rowcov = ();
39 19         38 $Z0_row = 0;
40 19         42 $Z0_col = 0;
41 19         46 @path = ();
42              
43             #variables local to the subroutine
44 19         219 my $step = 0;
45 19         65 my ($i, $j) = (0,0);
46              
47             #the input matrix
48 19         71 my @inp_mat = @$rmat;
49              
50             #copy the orginal matrix, before applying the algorithm to the matrix
51 19         81 foreach (@inp_mat)
52             {
53 89         333 push @mat, [ @$_ ];
54             }
55              
56             #check if the input matrix is well-formed i.e. either square or rectangle.
57 19         51 $row_len = $#{$mat[0]};
  19         72  
58 19         48 foreach my $row (@mat)
59             {
60 89 100       239 if($row_len != $#$row)
61             {
62 1         7 die "Please check the input matrix.\nThe input matrix is not a well-formed matrix!\nThe input matrix has to be rectangular or square matrix.\n";
63             }
64             }
65              
66             #check if the matrix is a square matrix,
67             #if not convert it to square matrix by padding zeroes.
68 18 100       36 if($#mat < $#{$mat[0]})
  18 100       83  
69 16         139 {
70             # Add rows
71 2         5 my $diff = $#{$mat[0]} - $#mat;
  2         6  
72 2         8 for (1 .. $diff)
73             {
74 11         12 push @mat, [ (0) x @{$mat[0]} ];
  11         48  
75             }
76             }
77             elsif($#mat > $#{$mat[0]})
78             {
79             # Add columns
80 3         5 my $diff = $#mat - $#{$mat[0]};
  3         11  
81 3         11 for (0 .. $#mat)
82             {
83 19         26 push @{$mat[$_]}, (0) x $diff;
  19         68  
84             }
85             }
86              
87             #initialize mask, column cover and row cover matrices
88 18         92 clear_covers();
89              
90 18         92 for($i=0;$i<=$#mat;$i++)
91             {
92 98         412 push @mask, [ (0) x @mat ];
93             }
94              
95             #The algorithm can be grouped in 6 steps.
96 18         64 &stepone();
97 18         104 &steptwo();
98 18         52 $step = &stepthree();
99 18         73 while($step == 4)
100             {
101 33         84 $step = &stepfour();
102 33         678 while($step == 6)
103             {
104 23         67 &stepsix();
105 23         69 $step = &stepfour();
106             }
107 33         84 &stepfive();
108 33         90 $step = &stepthree();
109             }
110              
111             #create the output matrix
112 18         58 for my $i (0 .. $#mat)
113             {
114 98         121 for my $j (0 .. $#{$mat[$i]})
  98         218  
115             {
116 1002 100       2608 if($mask[$i][$j] == 1)
117             {
118 98         207 $rsolution_mat->[$i] = $j;
119             }
120             }
121             }
122              
123              
124             #Code for tracing------------------
125 18         91 <<'ee';
126             print "\nInput Matrix:\n";
127             for($i=0;$i<=$#mat;$i++)
128             {
129             for($j=0;$j<=$#mat;$j++)
130             {
131             print $mat[$i][$j] . "\t";
132             }
133             print "\n";
134             }
135            
136             print "\nMask Matrix:\n";
137             for($i=0;$i<=$#mat;$i++)
138             {
139             for($j=0;$j<=$#mat;$j++)
140             {
141             print $mask[$i][$j] . "\t";
142             }
143             print "\n";
144             }
145              
146             print "\nOutput Matrix:\n";
147             print "$_\n" for @$rsolution_mat;
148             ee
149              
150             #----------------------------------
151              
152             }
153              
154             #Step 1 - Find minimum value for every row and subtract this min from each element of the row.
155             sub stepone
156             {
157             # print "Step 1 \n";
158              
159             #Find the minimum value for every row
160 18     18 0 40 for my $row (@mat)
161             {
162 98         173 my $min = $row->[0];
163 98         160 for (@$row)
164             {
165 1002 100       1938 $min = $_ if $min > $_;
166             }
167            
168             #Subtract the minimum value of the row from each element of the row.
169 98         186 @$row = map {$_ - $min} @$row;
  1002         1793  
170             }
171             # print "Step 1 end \n";
172             }
173              
174             #Step 2 - Star the zeroes, Create the mask and cover matrices. Re-initialize the cover matrices for next steps.
175             #To star a zero: We search for a zero in the matrix and than cover the column and row in which it occurs. Now this zero is starred.
176             #A next starred zero can occur only in those columns and rows which have not been previously covered by any other starred zero.
177             sub steptwo
178             {
179             # print "Step 2 \n";
180            
181 18     18 0 41 my ($i, $j) = (0,0);
182              
183 18         78 for($i=0;$i<=$#mat;$i++)
184             {
185 98         148 for($j=0;$j<=$#{$mat[$i]};$j++)
  1100         2487  
186             {
187 1002 100 100     9558 if($mat[$i][$j] == 0 && $colcov[$j] == 0 && $rowcov[$i] == 0)
      100        
188             {
189 65         106 $mask[$i][$j] = 1;
190 65         88 $colcov[$j] = 1;
191 65         102 $rowcov[$i] = 1;
192             }
193             }
194             }
195             #Re-initialize the cover matrices
196 18         59 &clear_covers();
197             # print "Step 2 end\n";
198             }
199              
200             #Step 3 - Check if each column has a starred zero. If yes then the problem is solved else proceed to step 4
201             sub stepthree
202             {
203             # print "Step 3 \n";
204              
205 51     51 0 87 my $cnt = 0;
206              
207 51         129 for my $i (0 .. $#mat)
208             {
209 635         1064 for my $j (0 .. $#mat)
210             {
211 13187 100       31781 if($mask[$i][$j] == 1)
212             {
213 428         535 $colcov[$j] = 1;
214 428         671 $cnt++;
215             }
216             }
217             }
218 51 100       155 if($cnt > $#mat)
219             {
220             # print "Step 3 end. Next expected step 7 \n";
221 18         64 return 7;
222             }
223             else
224             {
225             # print "Step 3 end. Next expected step 4 \n";
226 33         120 return 4;
227             }
228              
229             }
230              
231             #Step 4 - Try to find a zero which is not starred and whose columns and rows are not yet covered.
232             #If such a zero found, prime it, try to find a starred zero in its row,
233             # if not found proceed to step 5
234             # else continue
235             #Else proceed to step 6.
236             sub stepfour
237             {
238             # print "Step 4 \n";
239              
240 56     56 0 154 while(1)
241             {
242 373         637 my ($row, $col) = &find_a_zero();
243 373 100       950 if ($row < 0)
244             {
245             # No zeroes
246 23         69 return 6;
247             }
248              
249 350         520 $mask[$row][$col] = 2;
250 350         1241 my $star_col = &find_star_in_row($row);
251 350 100       755 if ($star_col >= 0)
252             {
253 317         362 $col = $star_col;
254 317         480 $rowcov[$row] = 1;
255 317         479 $colcov[$col] = 0;
256             }
257             else
258             {
259 33         42 $Z0_row = $row;
260 33         45 $Z0_col = $col;
261 33         101 return 5;
262             }
263             }
264             }
265              
266             #Tries to find yet uncovered zero
267             sub find_a_zero
268             {
269 373     373 0 618 for my $i (0 .. $#mat)
270             {
271 3244 100       6669 next if $rowcov[$i];
272              
273 614         1399 for my $j (reverse(0 .. $#mat)) # Prefer large $j
274             {
275 7347 100       13483 next if $colcov[$j];
276 3494 100       8024 return ($i, $j) if $mat[$i][$j] == 0;
277             }
278             }
279              
280 23         60 return (-1, -1);
281             }
282              
283             #Tries to find starred zero in the given row and returns the column number
284             sub find_star_in_row
285             {
286 350     350 0 400 my $row = shift;
287              
288 350         565 for my $j (0 .. $#mat)
289             {
290 4067 100       7427 if($mask[$row][$j] == 1)
291             {
292 317         526 return $j;
293             }
294             }
295 33         74 return -1;
296             }
297              
298             #Step 5 - Try to find a starred zero in the column of the uncovered zero found in the step 4.
299             #If starred zero found, try to find a prime zero in its row.
300             #Continue finding starred zero in the column and primed zero in the row until,
301             #we get to a primed zero which does not have a starred zero in its column.
302             #At this point reduce the non-zero values of mask matrix by 1. i.e. change prime zeros to starred zeroes.
303             #Clear the cover matrices and clear any primes i.e. values=2 from mask matrix.
304             sub stepfive
305             {
306             # print "Step 5 \n";
307              
308 33     33 0 56 my $cnt = 0;
309 33         41 my $done = 0;
310              
311 33         66 $path[$cnt][0] = $Z0_row;
312 33         72 $path[$cnt][1] = $Z0_col;
313            
314 33         80 while($done == 0)
315             {
316 166         334 my $row = &find_star_in_col($path[$cnt][1]);
317 166 100       329 if($row > -1)
318             {
319 133         151 $cnt++;
320 133         229 $path[$cnt][0] = $row;
321 133         236 $path[$cnt][1] = $path[$cnt - 1][1];
322             }
323             else
324             {
325 33         45 $done = 1;
326             }
327 166 100       503 if($done == 0)
328             {
329 133         249 my $col = &find_prime_in_row($path[$cnt][0]);
330 133         202 $cnt++;
331 133         307 $path[$cnt][0] = $path[$cnt - 1][0];
332 133         333 $path[$cnt][1] = $col;
333             }
334             }
335 33         86 &convert_path($cnt);
336 33         80 &clear_covers();
337 33         104 &erase_primes();
338              
339             # print "Step 5 end \n";
340             }
341              
342             #Tries to find starred zero in the given column and returns the row number
343             sub find_star_in_col
344             {
345 166     166 0 200 my $col = shift;
346              
347 166         299 for my $i (0 .. $#mat)
348             {
349 2103 100       8152 return $i if $mask[$i][$col] == 1;
350             }
351            
352 33         70 return -1;
353             }
354              
355             #Tries to find primed zero in the given row and returns the column number
356             sub find_prime_in_row
357             {
358 133     133 0 159 my $row = shift;
359              
360 133         242 for my $j (0 .. $#mat)
361             {
362 1973 100       3929 return $j if $mask[$row][$j] == 2;
363             }
364            
365 0         0 return -1;
366             }
367              
368             #Reduces non-zero value in the mask matrix by 1.
369             #i.e. converts all primes to stars and stars to none.
370             sub convert_path
371             {
372 33     33 0 49 my $cnt = shift;
373              
374 33         98 for my $i (0 .. $cnt)
375             {
376 299         578 for ( $mask[$path[$i][0]][$path[$i][1]] ) {
377 299 100       2034 $_ = ( $_ == 1 ) ? 0 : 1;
378             }
379             }
380             }
381              
382             #Clears cover matrices
383             sub clear_covers
384             {
385 69     69 0 356 @rowcov = @colcov = (0) x @mat;
386             }
387              
388             #Changes all primes i.e. values=2 to 0.
389             sub erase_primes
390             {
391 33     33 0 62 for my $row (@mask)
392             {
393 537         902 for my $j (0 .. $#$row)
394             {
395 12185 100       26982 $row->[$j] = 0 if $row->[$j] == 2;
396             }
397             }
398             }
399              
400             #Step 6 - Find the minimum value from the rows and columns which are currently not covered.
401             #Subtract this minimum value from all the elements of the columns which are not covered.
402             #Add this minimum value to all the elements of the rows which are covered.
403             #Proceed to step 4.
404             sub stepsix
405             {
406             # print "Step 6 \n";
407 23     23 0 596 my ($i, $j);
408 23         6376 my $minval = 0;
409              
410 23         599 $minval = &find_smallest();
411            
412 23         76 for($i=0;$i<=$#mat;$i++)
413             {
414 227         296 for($j=0;$j<=$#{$mat[$i]};$j++)
  4362         8790  
415             {
416 4135 100       7799 if($rowcov[$i] == 1)
417             {
418 30         43 $mat[$i][$j] += $minval;
419             }
420 4135 100       7990 if($colcov[$j] == 0)
421             {
422 2072         3114 $mat[$i][$j] -= $minval;
423             }
424             }
425             }
426              
427             # print "Step 6 end \n";
428             }
429              
430             #Finds the minimum value from all the matrix values which are not covered.
431             sub find_smallest
432             {
433 23     23 0 26 my $minval;
434              
435 23         60 for my $i (0 .. $#mat)
436             {
437 227 100       615 next if $rowcov[$i];
438              
439 221         390 for my $j (0 .. $#mat)
440             {
441 4105 100       7329 next if $colcov[$j];
442 2056 100 100     7571 if( !defined($minval) || $minval > $mat[$i][$j])
443             {
444 56         117 $minval = $mat[$i][$j];
445             }
446             }
447             }
448 23         59 return $minval;
449             }
450              
451              
452             1;
453             __END__