Algorithm-TravelingSalesman-BitonicTour
view release on metacpan or search on metacpan
lib/Algorithm/TravelingSalesman/BitonicTour.pm view on Meta::CPAN
package Algorithm::TravelingSalesman::BitonicTour;
use strict;
use warnings FATAL => 'all';
use base 'Class::Accessor::Fast';
use Carp 'croak';
use List::Util 'reduce';
use Params::Validate qw/ validate_pos SCALAR /;
use Regexp::Common;
our $VERSION = '0.05';
__PACKAGE__->mk_accessors(qw/ _points _sorted_points _tour /);
=head1 NAME
Algorithm::TravelingSalesman::BitonicTour - solve the euclidean traveling-salesman problem with bitonic tours
=head1 SYNOPSIS
use Algorithm::TravelingSalesman::BitonicTour;
my $bt = Algorithm::TravelingSalesman::BitonicTour->new;
$bt->add_point($x1,$y1);
$bt->add_point($x2,$y2);
$bt->add_point($x3,$y3);
# ...add other points as needed...
# get and print the solution
my ($len, @coords) = $bt->solve;
print "optimal path length: $len\n";
print "coordinates of optimal path:\n";
print " ($_->[0], $_->[1])\n" for @coords;
=head1 THE PROBLEM
From I<Introduction to Algorithms>, 2nd ed., T. H. Cormen, C. E. Leiserson, R.
Rivest, and C. Stein, MIT Press, 2001, problem 15-1, p. 364:
=over 4
The B<euclidean traveling-salesman problem> is the problem of determining the
shortest closed tour that connects a given set of I<n> points in the plane.
Figure 15.9(a) shows the solution to a 7-point problem. The general problem is
NP-complete, and its solution is therefore believed to require more than
polynomial time (see Chapter 34).
J. L. Bentley has suggested that we simplify the problem by restricting our
attention to B<bitonic tours>, that is, tours that start at the leftmost point,
go strictly left to right to the rightmost point, and then go strictly right to
left back to the starting point. Figure 15.9(b) shows the shortest bitonic
tour of the same 7 points. In this case, a polynomial-time algorithm is
possible.
Describe an I<O>(n^2)-time algorithm for determining an optimal bitonic tour.
You may assume that no two points have the same I<x>-coordinate. (I<Hint:>
Scan left to right, maintaining optimal possibilities for the two parts of the
tour.)
=back
From Wikipedia (L<http://en.wikipedia.org/wiki/bitonic_tour>):
=over 4
In computational geometry, a B<bitonic tour> of a set of point sites in the
Euclidean plane is a closed polygonal chain that has each site as one of its
vertices, such that any vertical line crosses the chain at most twice.
lib/Algorithm/TravelingSalesman/BitonicTour.pm view on Meta::CPAN
These exercises may clarify the above analysis.
=over 4
=item Exercise 1.
Consider the parallelogram ((0,0), (1,1), (2,0), (3,1)).
a. Draw it on graph paper.
b. Label points "0" through "3"
c. Draw t[0,1]. Calculate its length.
d. Draw t[0,2] and t[1,2]. Calculate their lengths.
e. Draw t[0,3], t[1,3], and t[2,3]. Calculate their lengths.
f. What is the optimal bitonic tour?
g. Draw the suboptimal bitonic tour.
h. Why does the above algorithm find the optimal tour,
and not the suboptimal tour?
=item Exercise 2.
Repeat Exercise 1 with pentagon ((0,2), (1,0), (2,3), (3,0), (4,2)).
=back
=head1 METHODS
=head2 $class->new()
Constructs a new solution object.
Example:
my $ts = Algorithm::TravelingSalesman::BitonicTour->new;
=cut
sub new {
my $class = shift;
my $self = bless { _tour => {}, _points => {} }, $class;
return $self;
}
=head2 $ts->add_point($x,$y)
Adds a point at position (C<$x>, C<$y>) to be included in the solution. Method
C<add_point()> checks to make sure that no two points have the same
I<x>-coordinate. This method will C<croak()> with a descriptive error message
if anything goes wrong.
Example:
# add point at position (x=2, y=3) to the problem
$ts->add_point(2,3);
=cut
sub add_point {
my $self = shift;
my $valid = { type => SCALAR, regexp => $RE{num}{real} };
my ($x, $y) = validate_pos(@_, ($valid) x 2);
if (exists $self->_points->{$x}) {
my $py = $self->_points->{$x};
croak "FAIL: point ($x,$y) duplicates previous point ($x,$py)";
}
else {
$self->_sorted_points(undef); # clear any previous cache of sorted points
$self->_points->{$x} = $y;
return [$x, $y];
}
}
=head2 $ts->N()
Returns the number of points that have been added to the object (mnemonic:
B<n>umber).
Example:
print "I have %d points.\n", $ts->N;
=cut
sub N {
my $self = shift;
return scalar keys %{ $self->_points };
}
=head2 $ts->R()
Returns the index of the rightmost point that has been added to the object
(mnemonic: B<r>ightmost). This is always one less than C<< $ts->N >>.
=cut
sub R {
my $self = shift;
die 'Problem has no rightmost point (N < 1)' if $self->N < 1;
return $self->N - 1;
}
=head2 $ts->sorted_points()
Returns an array of points sorted by increasing I<x>-coordinate. The first
("zeroI<th>") array element returned is thus the leftmost point in the problem.
Each point is represented as an arrayref containing (x,y) coordinates. The
sorted array of points is cached, but the cache is cleared by each call to
C<add_point()>.
Example:
my $ts = Algorithm::TravelingSalesman::BitonicTour->new;
$ts->add_point(1,1);
$ts->add_point(0,0);
$ts->add_point(2,0);
my @sorted = $ts->sorted_points;
# @sorted contains ([0,0], [1,1], [2,0])
=cut
( run in 0.813 second using v1.01-cache-2.11-cpan-5511b514fd6 )