line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Algorithm::TravelingSalesman::BitonicTour; |
2
|
|
|
|
|
|
|
|
3
|
9
|
|
|
9
|
|
89427
|
use strict; |
|
9
|
|
|
|
|
20
|
|
|
9
|
|
|
|
|
351
|
|
4
|
9
|
|
|
9
|
|
46
|
use warnings FATAL => 'all'; |
|
9
|
|
|
|
|
19
|
|
|
9
|
|
|
|
|
407
|
|
5
|
9
|
|
|
9
|
|
61
|
use base 'Class::Accessor::Fast'; |
|
9
|
|
|
|
|
15
|
|
|
9
|
|
|
|
|
11463
|
|
6
|
9
|
|
|
9
|
|
36231
|
use Carp 'croak'; |
|
9
|
|
|
|
|
20
|
|
|
9
|
|
|
|
|
704
|
|
7
|
9
|
|
|
9
|
|
555
|
use List::Util 'reduce'; |
|
9
|
|
|
|
|
22
|
|
|
9
|
|
|
|
|
1218
|
|
8
|
9
|
|
|
9
|
|
10508
|
use Params::Validate qw/ validate_pos SCALAR /; |
|
9
|
|
|
|
|
107525
|
|
|
9
|
|
|
|
|
930
|
|
9
|
9
|
|
|
9
|
|
8420
|
use Regexp::Common; |
|
9
|
|
|
|
|
42879
|
|
|
9
|
|
|
|
|
50
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
our $VERSION = '0.05'; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
__PACKAGE__->mk_accessors(qw/ _points _sorted_points _tour /); |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 NAME |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
Algorithm::TravelingSalesman::BitonicTour - solve the euclidean traveling-salesman problem with bitonic tours |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=head1 SYNOPSIS |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
use Algorithm::TravelingSalesman::BitonicTour; |
22
|
|
|
|
|
|
|
my $bt = Algorithm::TravelingSalesman::BitonicTour->new; |
23
|
|
|
|
|
|
|
$bt->add_point($x1,$y1); |
24
|
|
|
|
|
|
|
$bt->add_point($x2,$y2); |
25
|
|
|
|
|
|
|
$bt->add_point($x3,$y3); |
26
|
|
|
|
|
|
|
# ...add other points as needed... |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
# get and print the solution |
29
|
|
|
|
|
|
|
my ($len, @coords) = $bt->solve; |
30
|
|
|
|
|
|
|
print "optimal path length: $len\n"; |
31
|
|
|
|
|
|
|
print "coordinates of optimal path:\n"; |
32
|
|
|
|
|
|
|
print " ($_->[0], $_->[1])\n" for @coords; |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=head1 THE PROBLEM |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
From I, 2nd ed., T. H. Cormen, C. E. Leiserson, R. |
37
|
|
|
|
|
|
|
Rivest, and C. Stein, MIT Press, 2001, problem 15-1, p. 364: |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=over 4 |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
The B is the problem of determining the |
42
|
|
|
|
|
|
|
shortest closed tour that connects a given set of I points in the plane. |
43
|
|
|
|
|
|
|
Figure 15.9(a) shows the solution to a 7-point problem. The general problem is |
44
|
|
|
|
|
|
|
NP-complete, and its solution is therefore believed to require more than |
45
|
|
|
|
|
|
|
polynomial time (see Chapter 34). |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
J. L. Bentley has suggested that we simplify the problem by restricting our |
48
|
|
|
|
|
|
|
attention to B, that is, tours that start at the leftmost point, |
49
|
|
|
|
|
|
|
go strictly left to right to the rightmost point, and then go strictly right to |
50
|
|
|
|
|
|
|
left back to the starting point. Figure 15.9(b) shows the shortest bitonic |
51
|
|
|
|
|
|
|
tour of the same 7 points. In this case, a polynomial-time algorithm is |
52
|
|
|
|
|
|
|
possible. |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
Describe an I(n^2)-time algorithm for determining an optimal bitonic tour. |
55
|
|
|
|
|
|
|
You may assume that no two points have the same I-coordinate. (I |
56
|
|
|
|
|
|
|
Scan left to right, maintaining optimal possibilities for the two parts of the |
57
|
|
|
|
|
|
|
tour.) |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=back |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
From Wikipedia (L): |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=over 4 |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
In computational geometry, a B of a set of point sites in the |
66
|
|
|
|
|
|
|
Euclidean plane is a closed polygonal chain that has each site as one of its |
67
|
|
|
|
|
|
|
vertices, such that any vertical line crosses the chain at most twice. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=back |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=head1 THE SOLUTION |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=head2 Conventions |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
Points are numbered from left to right, starting with "0", then "1", and so on. |
76
|
|
|
|
|
|
|
For convenience I call the rightmost point C. |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
=head2 Key Insights Into the Problem |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=over 4 |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=item 1. Focus on optimal B bitonic tours. |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
B have endpoints (i,j) where C<< i < j < R >>, and |
85
|
|
|
|
|
|
|
they are the building blocks of the optimal closed bitonic tour we're trying to |
86
|
|
|
|
|
|
|
find. |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
An open bitonic tour, optimal or not, has these properties: |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
* it's bitonic (a vertical line crosses the tour at most twice) |
91
|
|
|
|
|
|
|
* it's open (it has endpoints), which we call "i" and "j" (i < j) |
92
|
|
|
|
|
|
|
* all points to the left of "j" are visited by the tour |
93
|
|
|
|
|
|
|
* points i and j are endpoints (connected to exactly one edge) |
94
|
|
|
|
|
|
|
* all other points in the tour are connected to two edges |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
For a given set of points there may be many open bitonic tours with endpoints |
97
|
|
|
|
|
|
|
(i,j), but we are only interested in the I open tour--the tour with |
98
|
|
|
|
|
|
|
the shortest length. Let's call this tour B>. |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=item 2. Grok the (slightly) messy recurrence relation. |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
A concrete example helps clarify this. Assume we know the optimal tour lengths |
103
|
|
|
|
|
|
|
for all (i,j) to the right of point C<5>: |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
T(0,1) |
106
|
|
|
|
|
|
|
T(0,2) T(1,2) |
107
|
|
|
|
|
|
|
T(0,3) T(1,3) T(2,3) |
108
|
|
|
|
|
|
|
T(0,4) T(1,4) T(2,4) T(3,4) |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
From this information, we can find C, C, ... C. |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=over 4 |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=item B> |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
From our definition, C must have endpoints C<0> and C<5>, and must also |
117
|
|
|
|
|
|
|
include all intermediate points C<1>-C<4>. This means C is composed of |
118
|
|
|
|
|
|
|
points C<0> through C<5> in order. This is also the union of C and the |
119
|
|
|
|
|
|
|
line segment C<4>-C<5>. |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=item B> |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
C has endpoints at C<1> and C<5>, and visits all points to the left of |
124
|
|
|
|
|
|
|
C<5>. To preserve the bitonicity of C, the only possibility for the |
125
|
|
|
|
|
|
|
tour is the union of C and line segment C<4>-C<5>. I have included a |
126
|
|
|
|
|
|
|
short proof by contradiction of this in the source code. |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=begin comment |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
Proof by contradiction: |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
1. Assume the following: |
133
|
|
|
|
|
|
|
* T(1,5) is an optimal open bitonic tour having endpoints 1 and 5. |
134
|
|
|
|
|
|
|
* Points 4 and 5 are not adjacent in the tour, i.e. the final segment in |
135
|
|
|
|
|
|
|
the tour joins points "P" and 5, where "P" is to the left of point 4. |
136
|
|
|
|
|
|
|
2. Since T(1,5) is an optimal open bitonic tour, point 4 is included in the |
137
|
|
|
|
|
|
|
middle of the tour, not at an endpoint, and is connected to two edges. |
138
|
|
|
|
|
|
|
3. Since 4 is not connected to 5, both its edges connect to points to its |
139
|
|
|
|
|
|
|
left. |
140
|
|
|
|
|
|
|
4. Combined with the segment from 5 to P, a vertical line immediately to the |
141
|
|
|
|
|
|
|
left of point 4 must cross at least three line segments, thus our proposed |
142
|
|
|
|
|
|
|
T(1,5) is not bitonic. |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
Thus points 4 and 5 must be adjacent in tour T(1,5), so the tour must be the |
145
|
|
|
|
|
|
|
optimal tour from 1 to 4 (more convenently called "T(1,4)"), plus the segment |
146
|
|
|
|
|
|
|
from 4 to 5. |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=end comment |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=item B> |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
Our logic for finding C applies to these cases as well. |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=item B> |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
Tour C breaks the pattern seen in C through C, because |
157
|
|
|
|
|
|
|
the leftmost point (point 4) must be an endpoint in the tour. Via proof by |
158
|
|
|
|
|
|
|
contradiction similar to the above, our choices for constructing C are: |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
T(0,4) + line segment from 0 to 5 |
161
|
|
|
|
|
|
|
T(1,4) + line segment from 1 to 5 |
162
|
|
|
|
|
|
|
T(2,4) + line segment from 2 to 5 |
163
|
|
|
|
|
|
|
T(3,4) + line segment from 3 to 5 |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
All of them meet our bitonicity requirements, so we choose the shortest of |
166
|
|
|
|
|
|
|
these for C. |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=back |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
To summarize the recurrence, and using function C to calculate the |
171
|
|
|
|
|
|
|
distance between points: |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=over 5 |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=item if i < j-1: |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
C<< T(i,j) = T(i,j-1) + delta(j-1,j) >> |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=item if i == j-1: |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
C<< T(i,j) = min{ T(k,i) + delta(k,j) } >>, for all k < i |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=back |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
=item 3. The base case. |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
The open tour C has to be the line segment from 0 to 1. |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=back |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=head2 Dynamic Programming |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
This problem exhibits the classic features suggesting a dynamic programming |
194
|
|
|
|
|
|
|
solution: I and I. |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=head3 Overlapping Subproblems |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
The construction of tour C depends on knowledge of tours to the left of |
199
|
|
|
|
|
|
|
C: |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
to construct: one must know: |
202
|
|
|
|
|
|
|
------------- -------------- |
203
|
|
|
|
|
|
|
T(0,5) T(0,4) |
204
|
|
|
|
|
|
|
T(1,5) T(1,4) |
205
|
|
|
|
|
|
|
T(2,5) T(2,4) |
206
|
|
|
|
|
|
|
T(3,5) T(3,4) |
207
|
|
|
|
|
|
|
T(4,5) T(0,4), T(1,4), T(2,4), T(3,4) |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
We also see that a given optimal tour is used I in constructing |
210
|
|
|
|
|
|
|
longer tours. For example, C is used in the construction of both |
211
|
|
|
|
|
|
|
C and C. |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=head3 Optimal Substructure |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
As shown in the above analysis, constructing a given optimal tour depends on |
216
|
|
|
|
|
|
|
knowledge of shorter "included" optimal tours; suboptimal tours are irrelevant. |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
=head1 EXERCISES |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
These exercises may clarify the above analysis. |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
=over 4 |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=item Exercise 1. |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
Consider the parallelogram ((0,0), (1,1), (2,0), (3,1)). |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
a. Draw it on graph paper. |
229
|
|
|
|
|
|
|
b. Label points "0" through "3" |
230
|
|
|
|
|
|
|
c. Draw t[0,1]. Calculate its length. |
231
|
|
|
|
|
|
|
d. Draw t[0,2] and t[1,2]. Calculate their lengths. |
232
|
|
|
|
|
|
|
e. Draw t[0,3], t[1,3], and t[2,3]. Calculate their lengths. |
233
|
|
|
|
|
|
|
f. What is the optimal bitonic tour? |
234
|
|
|
|
|
|
|
g. Draw the suboptimal bitonic tour. |
235
|
|
|
|
|
|
|
h. Why does the above algorithm find the optimal tour, |
236
|
|
|
|
|
|
|
and not the suboptimal tour? |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=item Exercise 2. |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
Repeat Exercise 1 with pentagon ((0,2), (1,0), (2,3), (3,0), (4,2)). |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=back |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=head1 METHODS |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=head2 $class->new() |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
Constructs a new solution object. |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
Example: |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
my $ts = Algorithm::TravelingSalesman::BitonicTour->new; |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=cut |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
sub new { |
257
|
25
|
|
|
25
|
1
|
23049
|
my $class = shift; |
258
|
25
|
|
|
|
|
131
|
my $self = bless { _tour => {}, _points => {} }, $class; |
259
|
25
|
|
|
|
|
68
|
return $self; |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
=head2 $ts->add_point($x,$y) |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
Adds a point at position (C<$x>, C<$y>) to be included in the solution. Method |
265
|
|
|
|
|
|
|
C checks to make sure that no two points have the same |
266
|
|
|
|
|
|
|
I-coordinate. This method will C with a descriptive error message |
267
|
|
|
|
|
|
|
if anything goes wrong. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
Example: |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
# add point at position (x=2, y=3) to the problem |
272
|
|
|
|
|
|
|
$ts->add_point(2,3); |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
=cut |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
sub add_point { |
277
|
247
|
|
|
247
|
1
|
9106
|
my $self = shift; |
278
|
247
|
|
|
|
|
1060
|
my $valid = { type => SCALAR, regexp => $RE{num}{real} }; |
279
|
247
|
|
|
|
|
12533
|
my ($x, $y) = validate_pos(@_, ($valid) x 2); |
280
|
247
|
100
|
|
|
|
964
|
if (exists $self->_points->{$x}) { |
281
|
4
|
|
|
|
|
34
|
my $py = $self->_points->{$x}; |
282
|
4
|
|
|
|
|
73
|
croak "FAIL: point ($x,$y) duplicates previous point ($x,$py)"; |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
else { |
285
|
243
|
|
|
|
|
2162
|
$self->_sorted_points(undef); # clear any previous cache of sorted points |
286
|
243
|
|
|
|
|
1499
|
$self->_points->{$x} = $y; |
287
|
243
|
|
|
|
|
2700
|
return [$x, $y]; |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
=head2 $ts->N() |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
Returns the number of points that have been added to the object (mnemonic: |
294
|
|
|
|
|
|
|
Bumber). |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
Example: |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
print "I have %d points.\n", $ts->N; |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=cut |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
sub N { |
303
|
281854
|
|
|
281854
|
1
|
354079
|
my $self = shift; |
304
|
281854
|
|
|
|
|
271918
|
return scalar keys %{ $self->_points }; |
|
281854
|
|
|
|
|
674829
|
|
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
=head2 $ts->R() |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
Returns the index of the rightmost point that has been added to the object |
310
|
|
|
|
|
|
|
(mnemonic: Bightmost). This is always one less than C<< $ts->N >>. |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
=cut |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
sub R { |
315
|
468
|
|
|
468
|
1
|
4278
|
my $self = shift; |
316
|
468
|
100
|
|
|
|
797
|
die 'Problem has no rightmost point (N < 1)' if $self->N < 1; |
317
|
467
|
|
|
|
|
2695
|
return $self->N - 1; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
=head2 $ts->sorted_points() |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
Returns an array of points sorted by increasing I-coordinate. The first |
324
|
|
|
|
|
|
|
("zeroI | ") array element returned is thus the leftmost point in the problem.
|
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
Each point is represented as an arrayref containing (x,y) coordinates. The |
327
|
|
|
|
|
|
|
sorted array of points is cached, but the cache is cleared by each call to |
328
|
|
|
|
|
|
|
C. |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
Example: |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
my $ts = Algorithm::TravelingSalesman::BitonicTour->new; |
333
|
|
|
|
|
|
|
$ts->add_point(1,1); |
334
|
|
|
|
|
|
|
$ts->add_point(0,0); |
335
|
|
|
|
|
|
|
$ts->add_point(2,0); |
336
|
|
|
|
|
|
|
my @sorted = $ts->sorted_points; |
337
|
|
|
|
|
|
|
# @sorted contains ([0,0], [1,1], [2,0]) |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=cut |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
sub sorted_points { |
342
|
80353
|
|
|
80353
|
1
|
93397
|
my $self = shift; |
343
|
80353
|
100
|
|
|
|
185571
|
unless ($self->_sorted_points) { |
344
|
27
|
|
|
|
|
145
|
my @x = sort { $a <=> $b } keys %{ $self->_points }; |
|
1322
|
|
|
|
|
1584
|
|
|
27
|
|
|
|
|
71
|
|
345
|
27
|
|
|
|
|
187
|
my @p = map [ $_, $self->_points->{$_} ], @x; |
346
|
27
|
|
|
|
|
913
|
$self->_sorted_points(\@p); |
347
|
|
|
|
|
|
|
} |
348
|
80353
|
|
|
|
|
422341
|
return @{ $self->_sorted_points }; |
|
80353
|
|
|
|
|
172126
|
|
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
=head2 coord($n) |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
Returns an array containing the coordinates of point C<$n>. |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
Examples: |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
my ($x0, $y0) = $ts->coord(0); # coords of leftmost point |
358
|
|
|
|
|
|
|
my ($x1, $y1) = $ts->coord(1); # coords of next point |
359
|
|
|
|
|
|
|
# ... |
360
|
|
|
|
|
|
|
my ($xn, $yn) = $ts->coord(-1); # coords of rightmost point--CLEVER! |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
=cut |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
sub coord { |
365
|
80348
|
|
|
80348
|
1
|
106349
|
my ($self, $n) = @_; |
366
|
80348
|
|
|
|
|
91929
|
return @{ ($self->sorted_points)[$n] }; |
|
80348
|
|
|
|
|
138112
|
|
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
=head2 $ts->solve() |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
Solves the problem as configured. Returns a list containing the length of the |
372
|
|
|
|
|
|
|
minimum tour, plus the coordinates of the points in the tour in traversal |
373
|
|
|
|
|
|
|
order. |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
Example: |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
my ($length, @points) = $ts->solve(); |
378
|
|
|
|
|
|
|
print "length: $length\n"; |
379
|
|
|
|
|
|
|
for (@points) { |
380
|
|
|
|
|
|
|
my ($x,$y) = @$_; |
381
|
|
|
|
|
|
|
print "($x,$y)\n"; |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=cut |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
sub solve { |
387
|
23
|
|
|
23
|
1
|
764
|
my $self = shift; |
388
|
23
|
|
|
|
|
29
|
my ($length, @points); |
389
|
23
|
100
|
|
|
|
59
|
if ($self->N < 1) { |
|
|
100
|
|
|
|
|
|
390
|
1
|
|
|
|
|
28
|
die "ERROR: you need to add some points!"; |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
elsif ($self->N == 1) { |
393
|
10
|
|
|
|
|
76
|
($length, @points) = (0,0); |
394
|
|
|
|
|
|
|
} |
395
|
|
|
|
|
|
|
else { |
396
|
12
|
|
|
|
|
76
|
($length, @points) = $self->optimal_closed_tour; |
397
|
|
|
|
|
|
|
} |
398
|
22
|
|
|
|
|
65
|
my @coords = map { [ $self->coord($_) ] } @points; |
|
247
|
|
|
|
|
5352
|
|
399
|
22
|
|
|
|
|
339
|
return ($length, @coords); |
400
|
|
|
|
|
|
|
} |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
=head2 $ts->optimal_closed_tour |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
Find the length of the optimal complete (closed) bitonic tour. This is done by |
405
|
|
|
|
|
|
|
choosing the shortest tour from the set of all possible complete tours. |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
A possible closed tour is composed of a partial tour with rightmost point C |
408
|
|
|
|
|
|
|
as one of its endpoints plus the final return segment from R to the other |
409
|
|
|
|
|
|
|
endpoint of the tour. |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
T(0,R) + delta(0,R) |
412
|
|
|
|
|
|
|
T(1,R) + delta(1,R) |
413
|
|
|
|
|
|
|
... |
414
|
|
|
|
|
|
|
T(i,R) + delta(i,R) |
415
|
|
|
|
|
|
|
... |
416
|
|
|
|
|
|
|
T(R-1,R) + delta(R-1,R) |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
=cut |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
sub optimal_closed_tour { |
421
|
12
|
|
|
12
|
1
|
16
|
my $self = shift; |
422
|
12
|
|
|
|
|
38
|
$self->populate_open_tours; |
423
|
12
|
|
|
|
|
89
|
my $R = $self->R; |
424
|
213
|
|
|
|
|
457
|
my @tours = map { |
425
|
12
|
|
|
|
|
67
|
my $cost = $self->tour_length($_,$self->R) + $self->delta($_,$self->R); |
426
|
213
|
|
|
|
|
538
|
my @points = ($self->tour_points($_,$R), $_); |
427
|
213
|
|
|
|
|
14637
|
[ $cost, @points ]; |
428
|
|
|
|
|
|
|
} 0 .. $self->R - 1; |
429
|
12
|
100
|
|
201
|
|
112
|
my $tour = reduce { $a->[0] < $b->[0] ? $a : $b } @tours; |
|
201
|
|
|
|
|
353
|
|
430
|
12
|
|
|
|
|
821
|
return @$tour; |
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
=head2 $ts->populate_open_tours |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
Populates internal data structure C describing all possible |
436
|
|
|
|
|
|
|
optimal open tour costs and paths for this problem configuration. |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
=cut |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
sub populate_open_tours { |
441
|
13
|
|
|
13
|
1
|
1083
|
my $self = shift; |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
# Base case: tour(0,1) has to be the segment (0,1). It would be nice if |
444
|
|
|
|
|
|
|
# the loop indices handled this case correctly, but they don't. |
445
|
13
|
|
|
|
|
39
|
$self->tour_length(0, 1, $self->delta(0,1) ); |
446
|
13
|
|
|
|
|
93
|
$self->tour_points(0, 1, 0, 1); |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
# find optimal tours for all points to the left of 2, 3, ... up to R |
449
|
13
|
|
|
|
|
90
|
foreach my $k (2 .. $self->R) { |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
# for each point "i" to the left of "k", find (and save) the optimal |
452
|
|
|
|
|
|
|
# open bitonic tour from "i" to "k". |
453
|
203
|
|
|
|
|
2658
|
foreach my $i (0 .. $k - 1) { |
454
|
20109
|
|
|
|
|
200315
|
my ($len, @points) = $self->optimal_open_tour($i,$k); |
455
|
20109
|
|
|
|
|
116524
|
$self->tour_length($i, $k, $len); |
456
|
20109
|
|
|
|
|
206925
|
$self->tour_points($i, $k, @points); |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
} |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
=head2 $ts->optimal_open_tour($i,$j) |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
Determines the optimal open tour from point C<$i> to point C<$j>, based on the |
464
|
|
|
|
|
|
|
values of previously calculated optimal tours to the left of C<$j>. |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
As discussed above, there are two distinct cases for this calculation: when C<< |
467
|
|
|
|
|
|
|
$i == $j - 1 >> and when C<< $i < $j - 1 >>. |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
# determine the length of and points in the tour |
470
|
|
|
|
|
|
|
# starting at point 20 and ending at point 25 |
471
|
|
|
|
|
|
|
my ($length,@points) = $ts->optimal_open_tour(20,25); |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
=cut |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
sub optimal_open_tour { |
476
|
20113
|
|
|
20113
|
1
|
40048
|
my ($self, $i, $j) = @_; |
477
|
20113
|
|
|
|
|
31637
|
local $" = q(,); |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
# we want $i to be strictly less than $j (we can actually be more lax with |
480
|
|
|
|
|
|
|
# the inputs, but this stricture halves our storage needs) |
481
|
20113
|
100
|
|
|
|
39568
|
croak "ERROR: bad call, optimal_open_tour(@_)" unless $i < $j; |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
# if $i and $j are adjacent, many valid bitonic tours (i => x => j) are |
484
|
|
|
|
|
|
|
# possible; choose the shortest one. |
485
|
20112
|
100
|
|
|
|
42254
|
return $self->optimal_open_tour_adjacent($i, $j) if $i + 1 == $j; |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
# if $i and $j are NOT adjacent, then only one bitonic tour (i => j-1 => j) |
488
|
|
|
|
|
|
|
# is possible. |
489
|
19908
|
100
|
|
|
|
55776
|
return $self->optimal_open_tour_nonadjacent($i, $j) if $i + 1 < $j; |
490
|
|
|
|
|
|
|
|
491
|
1
|
|
|
|
|
28
|
croak "ERROR: bad call, optimal_open_tour(@_)"; |
492
|
|
|
|
|
|
|
} |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
=head2 $obj->optimal_open_tour_adjacent($i,$j) |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
Uses information about optimal open tours to the left of <$j> to find the |
497
|
|
|
|
|
|
|
optimal tour with endpoints (C<$i>, C<$j>). |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
This method handles the case where C<$i> and C<$j> are adjacent, i.e. C<< $i = |
500
|
|
|
|
|
|
|
$j - 1 >>. In this case there are many possible bitonic tours, all going from |
501
|
|
|
|
|
|
|
C<$i> to "C<$x>" to C<$j>. All points C<$x> in the range C<(0 .. $i - 1)> are |
502
|
|
|
|
|
|
|
examined to find the optimal tour. |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
=cut |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
sub optimal_open_tour_adjacent { |
507
|
204
|
|
|
204
|
1
|
367
|
my ($self, $i, $j) = @_; |
508
|
19907
|
|
|
|
|
27201
|
my @tours = map { |
509
|
204
|
|
|
|
|
2510
|
my $x = $_; |
510
|
19907
|
|
|
|
|
36644
|
my $len = $self->tour_length($x,$i) + $self->delta($x,$j); |
511
|
19907
|
|
|
|
|
46616
|
my @path = reverse($j, $self->tour_points($x, $i) ); |
512
|
19907
|
|
|
|
|
843537
|
[ $len, @path ]; |
513
|
|
|
|
|
|
|
} 0 .. $i - 1; |
514
|
204
|
100
|
|
19703
|
|
5467
|
my $min_tour = reduce { $a->[0] < $b->[0] ? $a : $b } @tours; |
|
19703
|
|
|
|
|
36890
|
|
515
|
204
|
|
|
|
|
65090
|
return @$min_tour; |
516
|
|
|
|
|
|
|
} |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
=head2 $obj->optimal_open_tour_nonadjacent($i,$j) |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
Uses information about optimal open tours to the left of <$j> to find the |
521
|
|
|
|
|
|
|
optimal tour with endpoints (C<$i>, C<$j>). |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
This method handles the case where C<$i> and C<$j> are not adjacent, i.e. C<< |
524
|
|
|
|
|
|
|
$i < $j - 1 >>. In this case there is only one bitonic tour possible, going |
525
|
|
|
|
|
|
|
from C<$i> to C<$j-1> to C<$j>. |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
=cut |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
sub optimal_open_tour_nonadjacent { |
530
|
19907
|
|
|
19907
|
1
|
27264
|
my ($self, $i, $j) = @_; |
531
|
19907
|
|
|
|
|
41607
|
my $len = $self->tour_length($i, $j - 1) + $self->delta($j - 1,$j); |
532
|
19907
|
|
|
|
|
64970
|
my @points = ($self->tour_points($i, $j - 1), $j); |
533
|
19907
|
|
|
|
|
897318
|
return($len, @points); |
534
|
|
|
|
|
|
|
} |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
=head2 $b->tour($i,$j) |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
Returns the data structure associated with the optimal open bitonic tour with |
540
|
|
|
|
|
|
|
endpoints (C<$i>, C<$j>). |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
=cut |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
sub tour { |
545
|
280868
|
|
|
280868
|
1
|
401386
|
my ($self, $i, $j) = @_; |
546
|
280868
|
100
|
|
|
|
592456
|
croak "ERROR: tour($i,$j) ($i >= $j)" |
547
|
|
|
|
|
|
|
unless $i < $j; |
548
|
280867
|
100
|
|
|
|
516236
|
croak "ERROR: tour($i,$j,...) ($j >= @{[ $self->N ]})" |
|
1
|
|
|
|
|
12
|
|
549
|
|
|
|
|
|
|
unless $j < $self->N; |
550
|
280866
|
100
|
|
|
|
1932973
|
$self->_tour->{$i}{$j} = [] unless $self->_tour->{$i}{$j}; |
551
|
280866
|
|
|
|
|
2013032
|
return $self->_tour->{$i}{$j}; |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
=head2 $b->tour_length($i, $j, [$len]) |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
Returns the length of the optimal open bitonic tour with endpoints (C<$i>, |
557
|
|
|
|
|
|
|
C<$j>). If C<$len> is specified, set the length to C<$len>. |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
=cut |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
sub tour_length { |
562
|
60158
|
|
|
60158
|
1
|
94125
|
my $self = shift; |
563
|
60158
|
|
|
|
|
65800
|
my $i = shift; |
564
|
60158
|
|
|
|
|
57680
|
my $j = shift; |
565
|
60158
|
100
|
|
|
|
118567
|
if (@_) { |
566
|
20123
|
100
|
|
|
|
42591
|
croak "ERROR: tour_length($i,$j,$_[0]) has length <= 0 ($_[0])" |
567
|
|
|
|
|
|
|
unless $_[0] > 0; |
568
|
20122
|
|
|
|
|
36141
|
$self->tour($i,$j)->[0] = $_[0]; |
569
|
|
|
|
|
|
|
} |
570
|
60157
|
100
|
|
|
|
202774
|
if (exists $self->tour($i,$j)->[0]) { |
571
|
60155
|
|
|
|
|
393776
|
return $self->tour($i,$j)->[0]; |
572
|
|
|
|
|
|
|
} |
573
|
|
|
|
|
|
|
else { |
574
|
1
|
|
|
|
|
37
|
croak "Don't know the length of tour($i,$j)"; |
575
|
|
|
|
|
|
|
} |
576
|
|
|
|
|
|
|
} |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
=head2 $b->tour_points($i, $j, [@points]) |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
Returns an array containing the indices of the points in the optimal open |
581
|
|
|
|
|
|
|
bitonic tour with endpoints (C<$i>, C<$j>). |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
If C<@points> is specified, set the endpoints to C<@points>. |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
=cut |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
sub tour_points { |
588
|
60159
|
|
|
60159
|
1
|
85510
|
my $self = shift; |
589
|
60159
|
|
|
|
|
63763
|
my $i = shift; |
590
|
60159
|
|
|
|
|
64235
|
my $j = shift; |
591
|
60159
|
100
|
|
|
|
135368
|
if (@_) { |
592
|
20125
|
100
|
|
|
|
37044
|
croak "ERROR: tour_points($i,$j,@_) ($i != first point)" |
593
|
|
|
|
|
|
|
unless $i == $_[0]; |
594
|
20124
|
100
|
|
|
|
38882
|
croak "ERROR: tour_points($i,$j,@_) ($j != last point)" |
595
|
|
|
|
|
|
|
unless $j == $_[-1]; |
596
|
20123
|
100
|
|
|
|
39182
|
croak "ERROR: tour_points($i,$j,@_) (wrong number of points in @_)" |
597
|
|
|
|
|
|
|
unless scalar(@_) == $j + 1; |
598
|
20122
|
|
|
|
|
56025
|
$self->tour($i,$j)->[1] = \@_; |
599
|
|
|
|
|
|
|
} |
600
|
60156
|
100
|
|
|
|
200177
|
if (exists $self->tour($i,$j)->[1]) { |
601
|
60155
|
|
|
|
|
325973
|
return @{ $self->tour($i,$j)->[1] }; |
|
60155
|
|
|
|
|
109133
|
|
602
|
|
|
|
|
|
|
} |
603
|
|
|
|
|
|
|
else { |
604
|
1
|
|
|
|
|
21
|
croak "Don't know the points for tour($i,$j)"; |
605
|
|
|
|
|
|
|
} |
606
|
|
|
|
|
|
|
} |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
=head2 $b->delta($p1,$p2); |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
Returns the euclidean distance from point C<$p1> to point C<$p2>. |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
Examples: |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
# print the distance from the leftmost to the next point |
615
|
|
|
|
|
|
|
print $b->delta(0,1); |
616
|
|
|
|
|
|
|
# print the distance from the leftmost to the rightmost point |
617
|
|
|
|
|
|
|
print $b->delta(0,-1); |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
=cut |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
sub delta { |
622
|
40048
|
|
|
40048
|
1
|
262834
|
my ($self, $p1, $p2) = @_; |
623
|
40048
|
|
|
|
|
72347
|
my ($x1, $y1) = $self->coord($p1); |
624
|
40048
|
|
|
|
|
993006
|
my ($x2, $y2) = $self->coord($p2); |
625
|
40048
|
|
|
|
|
971719
|
return sqrt((($x1-$x2)*($x1-$x2))+(($y1-$y2)*($y1-$y2))); |
626
|
|
|
|
|
|
|
} |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
=head1 RESOURCES |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
=over 4 |
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
=item |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
Cormen, Thomas H.; Leiserson, Charles E.; Rivest, Ronald L.; Stein, Clifford |
636
|
|
|
|
|
|
|
(2001). Introduction to Algorithms, second edition, MIT Press and McGraw-Hill. |
637
|
|
|
|
|
|
|
ISBN 978-0-262-53196-2. |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
=item |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
Bentley, Jon L. (1990), "Experiments on traveling salesman heuristics", Proc. |
642
|
|
|
|
|
|
|
1st ACM-SIAM Symp. Discrete Algorithms (SODA), pp. 91-99, |
643
|
|
|
|
|
|
|
L. |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
=item |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
L |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
=item |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
L |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
=item |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
L |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
=item |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
L |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
=back |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
=head1 AUTHOR |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
John Trammell, C<< >> |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
=head1 BUGS |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
Please report any bugs or feature requests to C
|
670
|
|
|
|
|
|
|
rt.cpan.org>, or through the web interface at |
671
|
|
|
|
|
|
|
L. |
672
|
|
|
|
|
|
|
I will be notified, and then you'll automatically be notified of progress on |
673
|
|
|
|
|
|
|
your bug as |
674
|
|
|
|
|
|
|
I make changes. |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
=head1 SUPPORT |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
perldoc Algorithm::TravelingSalesman::BitonicTour |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
You can also look for information at: |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
=over 4 |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
L |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
L |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
=item * CPAN Ratings |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
L |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
=item * Search CPAN |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
L |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
=back |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
Copyright 2008 John Trammell, all rights reserved. |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
709
|
|
|
|
|
|
|
under the same terms as Perl itself. |
710
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
=cut |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
1; |
714
|
|
|
|
|
|
|
|