File Coverage

blib/lib/Acme/Sort/Bozo.pm
Criterion Covered Total %
statement 44 44 100.0
branch 11 12 91.6
condition n/a
subroutine 10 10 100.0
pod 2 4 50.0
total 67 70 95.7


line stmt bran cond sub pod time code
1             package Acme::Sort::Bozo;
2              
3 1     1   31068 use 5.010;
  1         4  
  1         39  
4              
5 1     1   87 use strict;
  1         2  
  1         38  
6 1     1   7 use warnings;
  1         13  
  1         34  
7              
8 1     1   1026 use parent qw/Exporter/;
  1         499  
  1         5  
9 1     1   66 use Carp 'croak';
  1         2  
  1         62  
10              
11 1     1   5 use List::Util qw/shuffle/;
  1         2  
  1         712  
12              
13             our @EXPORT = qw/bozo/;
14              
15             our $VERSION = '0.05';
16              
17              
18              
19             # bozo()
20             # Usage:
21             # Sort a list in standard string comparison order.
22             #
23             # my @sorted = bozo( @unsorted );
24             #
25             # Sort a list in ascending numerical order:
26             # sub compare { return $_[0] <=> $_[1] };
27             # my @sorted = bozo( \&compare, @unsorted );
28             #
29             # Warning: Average case is O( n! ).
30             # Warning: Worst case could approach O(INF).
31             #
32             # bozo() is exported automatically upon use.
33              
34             sub bozo {
35 2 100   2 1 1641 my $compare = ref( $_[0] ) =~ /CODE/
36             ? shift
37             : \&compare;
38 2 50       12 return @_ if @_ < 2;
39 2         9 my $listref = [ @_ ]; # Get a ref to a copy of @_.
40 2         7 $listref = swap( $listref ) while not is_ordered( $compare, $listref );
41 2         4 return @{ $listref };
  2         15  
42             }
43              
44              
45              
46             # Internal use, not exported. Verifies order based on $compare->().
47             sub is_ordered {
48 484     484 0 1887 my ( $compare, $listref ) = @_;
49 484 100       1647 ref( $compare ) =~ /CODE/
50             or croak "is_ordered() expects a coderef as first arg.";
51 483 100       1326 ref( $listref ) =~ /ARRAY/
52             or croak "is_ordered() expects an arrayref as second arg.";
53 482         532 foreach( 0 .. $#{$listref} - 1 ) {
  482         1330  
54 901 100       15949 return 0
55             if $compare->( $listref->[ $_ ], $listref->[ $_ + 1 ] ) > 0;
56             }
57 3         98 return 1;
58             }
59              
60             # Internal use, not exported. Simply swaps two random elements. The elements
61             # are guaranteed to be distinct.
62             sub swap {
63 479     479 0 16074 my $listref = shift;
64 479         469 my $elements = @{$listref};
  479         636  
65 479         2200 my $first = int( rand( $elements ) );
66 479         495 my $second;
67 479         465 do{ $second = int( rand( $elements ) ); } until $second != $first;
  611         1424  
68             # ( $listref->[$first], $listref->[$second] ) = ( $listref->[$second], $listref->[$first] );
69 479         525 @{$listref}[$first, $second] = @{$listref}[$second, $first];
  479         1071  
  479         718  
70 479         1362 return $listref;
71             }
72              
73              
74             # Default compare() is ascending standard string comparison order.
75             sub compare {
76 13 100   13 1 3446 croak "compare() requires two args."
77             unless scalar @_ == 2;
78 12         51 return $_[0] cmp $_[1];
79             }
80              
81              
82             =head1 NAME
83              
84             Acme::Sort::Bozo - Implementation of a Bozo sort algorithm.
85              
86             =head1 VERSION
87              
88             Version 0.05
89              
90             =head1 SYNOPSIS
91              
92             The Bozo is a sort that is based on a "swap and test" paradigm. It works by
93             first testing whether the input is in sorted order. If so, return the list. But if not,
94             randomly select two elements from the list, swap them, and test again. Repeat until
95             the shuffle comes back sorted.
96              
97             use Acme::Sort::Bozo;
98              
99             my @unsorted = qw/ E B A C D /;
100             my @ascending = bozo( @unsorted );
101            
102             my @descending = bozo(
103             sub{ return $_[1] cmp $_[0]; },
104             @unsorted
105             );
106              
107             The worst case for Bozo is difficult to determine, though one study suggests it probably approaches O(INF).
108             The good news is that, as time (and computation) approaches infinity the odds of not finding a solution decline
109             toward zero (assuming a good random number generator). So if you have an eternity to wait, you'll get your
110             results soon enough. The average case is O( n * n! ). However, there is no
111             guarantee that any particular sort will come in anywhere near average. Where the bogosort is a 'stateless'
112             sort, the bozo sort maintains a list state from one iteration to the next, but its decision mechanism for swaps I
113             stateless; it blindly swaps any random two elements.
114              
115             Keep in mind that a list of five items consumes an average of 5 * 5!, or 600 iterations. 10! is
116             36,288,000 iterations on average. The universe will either collapse or expand to the point that it cannot sustain
117             life long before the Bozo sort manages to sort a deck of cards, in the average case. In the worst case, all of the
118             background radiation from our universe will have decayed to the point that there is no longer any trace of our
119             existence before this sort manages to alphabetically sort your social networking friends list.
120              
121             Test with short (4 to 7 element) lists, and be prepared to kill the process if you mistakenly hand it more elements
122             than that.
123              
124             =head1 EXPORT
125              
126             Always exports one function: C.
127              
128             =head1 SUBROUTINES/METHODS
129              
130             =head2 bozo( @unsorted )
131              
132             Accepts a list as a parameter and returns a sorted list.
133              
134             If the first parameter is a reference to a subroutine, it will be used as the
135             comparison function.
136              
137             The Bozo is probably mostly useful as a teaching example of a "perversely awful" sort
138             algorithm. There are approximately 1e80 atoms in the universe. A sort list of
139             59 elements will gain an average case solution of 5.9e81 iterations, with a worst
140             case approaching infinite iterations to find a solution. Anything beyond just a
141             few items takes a considerable amount of work.
142              
143             Each iteration checks first to see if the list is in order. Here a comparatively
144             minor optimization is that the first out-of-order element will short-circuit the
145             check. That step has a worst case of O(n), and average case of nearly O(1).
146             That's the only good news. Once it is determined that the list is out
147             of order, a pair of elements (not necessarily adjacent) are chosen at random, and swapped.
148             Then the test happens all over again, repeating until a solution is happened across by chance.
149              
150             There is a potential for this sort to never finish, since a typical random number
151             synthesizer does not generate an infinitely non-repeating series. Because this
152             algorithm has the capability of producing O(INF) iterations, it would need an
153             infinite source of random numbers to find a solution in any given dataset.
154              
155             Small datasets are unlikely to encounter this problem, but as the dataset grows,
156             so does the propensity for running through the entire set of pseudo-random numbers
157             generated by Perl's rand() for a given seed. None of this really matters, of course,
158             as no sane individual would ever use this for any serious sorting work.
159              
160             Do you feel lucky today, chump?
161              
162              
163             =cut
164              
165              
166             =head2 compare( $a, $b )
167              
168             By passing a subref as the first parameter to C, the user is able to
169             manipulate sort orders just as is done with Perl's built in C< sort { code } @list >
170             routine.
171              
172             The comparison function is easy to implement using Perl's C<< <=> >> and C< cmp >
173             operators, but any amount of creativity is ok so long as return values are negative
174             for "Order is ok", positive for "Order is not ok", and 0 for "Terms are equal
175             (Order is ok)".
176              
177             =cut
178              
179              
180             =head1 AUTHOR
181              
182             David Oswald, C<< >>
183              
184             =head1 BUGS
185              
186             Please report any bugs or feature requests to C, or through
187             the web interface at L. I will be notified, and then you'll
188             automatically be notified of progress on your bug as I make changes.
189              
190              
191              
192              
193             =head1 SUPPORT
194              
195             You can find documentation for this module with the perldoc command.
196              
197             perldoc Acme::Sort::Bozo
198              
199              
200             You can also look for information at:
201              
202             =over 4
203              
204             =item * RT: CPAN's request tracker (report bugs here)
205              
206             L
207              
208             =item * AnnoCPAN: Annotated CPAN documentation
209              
210             L
211              
212             =item * CPAN Ratings
213              
214             L
215              
216             =item * Search CPAN
217              
218             L
219              
220             =back
221              
222              
223             =head1 SEE ALSO
224              
225             =over 4
226              
227             =item * The Bogosort (test and shuffle) - Another I sorting algorithm.
228              
229             L
230              
231             =back
232              
233              
234             =head1 ACKNOWLEDGEMENTS
235              
236             =over 4
237              
238             =item * Wikipedia article on the Bogosort and Bozo sort
239              
240             L
241              
242             =item * Sorting the Slow Way: An analysis of Perversely Awful Randomized Sorting Algorithms
243              
244             L
245              
246             =back
247              
248              
249             =head1 LICENSE AND COPYRIGHT
250              
251             Copyright 2011 David Oswald.
252              
253             This program is free software; you can redistribute it and/or modify it
254             under the terms of either: the GNU General Public License as published
255             by the Free Software Foundation; or the Artistic License.
256              
257             See http://dev.perl.org/licenses/ for more information.
258              
259              
260             =cut
261              
262             1; # End of Acme::Sort::Bozo