|
Algorithm::SetCovering - Algorithms to solve the "set covering problem" |
Algorithm::SetCovering - Algorithms to solve the ``set covering problem''
use Algorithm::SetCovering;
my $alg = Algorithm::SetCovering->new(
columns => 4,
mode => "greedy");
$alg->add_row(1, 0, 1, 0);
$alg->add_row(1, 1, 0, 0);
$alg->add_row(1, 1, 1, 0);
$alg->add_row(0, 1, 0, 1);
$alg->add_row(0, 0, 1, 1);
my @to_be_opened = (@ARGV || (1, 1, 1, 1));
my @set = $alg->min_row_set(@to_be_opened);
print "To open (@to_be_opened), we need ",
scalar @set, " keys:\n";
for(@set) {
print "$_: ", join('-', $alg->row($_)), "\n";
}
Consider having M keys and N locks. Every key opens one or more locks:
| lock1 lock2 lock3 lock4
-----+------------------------
key1 | x x
key2 | x x
key3 | x x x
key4 | x x
key5 | x x
Given an arbitrary set of locks you have to open (e.g. 2,3,4), the task is to find a minimal set of keys to accomplish this. In the example above, the set [key4, key5] fulfils that condition.
The underlying problem is called ``set covering problem'' and the corresponding decision problem is NP-complete.
columns needs to be set to the number of columns in the matrix
(the number of locks in the introductory example).
mode is optional and selects an algorithm for finding the solution.
The following values for mode are implemented:
The default for mode is set to ``greedy''.
add_row(@columns)
$alg->add_row(1,0,0,1);
specifies that the new key can open locks #1 and #4.
The number of elements in @columns needs to match the previously defined number of columns.
min_row_set(@columns_to_cover)Defines which columns have to be covered by passing in an array with true values on element positions that need to be covered. For example,
my @idx_set = $alg->min_row_set(1,1,0,1);
specifies that all but the third column have to be covered and returns
an array of index numbers into an array, defined previously
(and implicitely) via successive add_row() commands.
If no set of keys can be found that satisfies the given requirement, an empty list is returned.
If you've forgotten which locks the key referred to by a certain index number
can open, use the rows() method to find out:
my(@opens_locks) = $alg->rows($idx_set[0]);
will give back an array of 0's and 1's, basically returning the
very parameters we've passed on to the
add_row() command previously.
Currently, the module implements the Greedy algorithm and also (just for scientific purposes) a dumb brute force method, creating all possible combinations of keys, sorting them by the number of keys used (combinations with fewer keys have priority) and trying for each of them if it fits the requirement of opening a given number of locks.
This obviously won't scale beyond a really small number of keys (N), because the number of permutations will be 2**N-2.
The Greedy Algorithm, on the other hand scales with O(mn^2), with m being the number of keys and n being the number of locks.
Mike Schilli, 2003, <m@perlmeister.com>
Thanks to the friendly guys on rec.puzzles, who provided me with valuable input to analyze the problem and explained the algorithm:
Craig <c_quest000@yahoo.com>
Robert Israel <israel@math.ubc.ca>
Patrick Hamlyn <path@multipro.n_ocomsp_am.au>
Copyright 2003 by Mike Schilli
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
|
Algorithm::SetCovering - Algorithms to solve the "set covering problem" |