You're right ysth, it does seem to work. :)
Advice to the OP: It is a good idea for this type of problem to devise your own test suite before you start coding a solution. To demonstrate, here is a test script that I created for ysth's solution. It's not exactly comprehensive, but I attempt to take care of the most obvious of boundary conditions.
The way it's coded, it would be easy for you to come up with a list of your own conditions that you want to test for. Or maybe rules that you would like to change. Such as is a point of inflection truly a local min or max? Your test suite can spell out what you expect or desire.
use Test::More;
use strict;
#################################
# Test Data
# Each element contains [ [Test Data], [Expected Min], [Expected Max]
+]
# Note: The reverse of any data, should simply reverse the expected re
+sults.
my @testdata = (
[# Empty
[], [], [],
],
[ # One Entry
[3], [3], [3],
],
[ # Two (Up slope)
[2, 4], [2], [4],
],
[ # Two (No slope)
[3, 3], [3], [3],
],
[ # Three (No slope)
[4, 4, 4], [4], [4],
],
[ # Three (Up slope, beginning point of inflection)
[4, 4, 6], [4], [6],
],
[ # Three (Up slope, ending point of inflection)
[4, 6, 6], [4], [6],
],
[ # Three (negative inflection)
[3,6,2], [3, 2], [6],
],
[ # Three (positive infection)
[10, 5, 10], [5], [10, 10],
],
[ # Mixed Data
# mi Mi m i M i m Mi
[qw(1 1 2 3 4 4 3 2 3 4 5 6 6 6 7 8 7 7 7 3 5 6 9 9)],
[qw(1 2 3)],
[qw(4 8 9)],
],
);
# One test each for min and max, and then times two for reverse.
plan tests => 2 * (2 * @testdata);
#################################
# Tests
foreach (@testdata) {
my ($data, $min, $max) = @$_;
my ($rmin, $rmax) = local_min_max(@$data);
is_deeply($rmin, $min, "min of [@$data]");
is_deeply($rmax, $max, "max of [@$data]");
my @reversed = reverse @$data;
($rmin, $rmax) = local_min_max(@reversed);
is_deeply($rmin, [reverse @$min], "min of [@reversed]");
is_deeply($rmax, [reverse @$max], "max of [@reversed]");
}
#################################
# Functions
sub local_min_max {
# Boundary Conditions
return ([], []) if @_ == 0;
return ([@_], [@_]) if @_ == 1;
my @array = @_;
my @minima;
my @maxima;
my $prev_cmp = 0;
for (0 .. $#array - 1) {
my $cmp = $array[$_] <=> $array[$_+1];
if ($cmp && $cmp != $prev_cmp) {
push @minima, $array[$_] if $cmp < 0;
push @maxima, $array[$_] if $cmp > 0;
$prev_cmp = $cmp;
}
}
push @minima, $array[-1] if $prev_cmp >= 0;
push @maxima, $array[-1] if $prev_cmp <= 0;
return (\@minima, \@maxima);
}
Ok, now back to less amusing problems.
- Miller | [reply] [d/l] |