Local Maxima
February 16, 2021
We keep track of both the previous element and the current direction, either climbing up or falling down, and compute the maxima in a single scan of the list:
(define (lmax xs) (if (null? xs) (error 'lmax "empty") (let loop ((xs (cdr xs)) (prev (car xs)) (dir 'up) (maxs (list))) (cond ((null? xs) (reverse (if (equal? dir 'up) (cons prev maxs) maxs))) ((= prev (car xs)) (loop (cdr xs) prev dir maxs)) ((< prev (car xs)) (loop (cdr xs) (car xs) 'up maxs)) ((equal? dir 'up) (loop (cdr xs) (car xs) 'down (cons prev maxs))) (else (loop (cdr xs) (car xs) 'down maxs))))))
Here is the sample problem:
> (lmax '(1 2 1 2 3 4 3 2 3 2 1)) (2 4 3)
You can run the program at https://ideone.com/RduLyR.
Perl version…
Here’s a solution in Python. It’s not particularly efficient, as it creates multiple copies of the input list.
Output:
Cache version
[sourcecode lang="css"]
test ;
q
;
localmaxima(list) ; Output local maxima
n i,next,previous,this
s list=$e(list)-1_” “list” “($e(list,$l(list)-1)-1)
s output=””
f i=2:1:$l(list,” “)-1 {
s this=$p(list,” “,i),previous=$p(list,” “,i-1),next=$p(list,” “,i+1)
i this>previous, this>next s output=output_this” ”
}
q $e(output,1,$l(output)-1)
Here is a solution in a somewhat functional style using standard R7RS
Scheme and a few well-known helpers form SRFI 1. It provides both
values and locations of the local maxima.
Output:
Cache version – Fix formatting
s list=”1 2 1 2 3 4 3 2 3 2 1″
w !,list,!,$$localmaxima(list)
1 2 1 2 3 4 3 2 3 2 1
2 4 3
@James Curtis-Smith
I’m unfamiliar with Perl. I tried typing your program into a file and executing it:
$ perl -e localmaxima.pl
Steve@DESKTOP-0SFLH1S MINGW64 ~/Desktop
$ perl -E localmaxima.pl
Steve@DESKTOP-0SFLH1S MINGW64 ~/Desktop
$ perl localmaxima.pl
syntax error at localmaxima.pl line 1, near “say join”
Execution of localmaxima.pl aborted due to compilation errors.
I also tried typing it into the interpreter, but couldn’t figure out how to execute it.
Any tips?
Thanks, Steve
Changed the program (localmaxima.pl) to the following, and executing “./localmaxima.pl” returned the expected values:
#!/usr/bin/perl
print join ‘ ‘, maxima( qw(1 2 1 2 3 4 3 2 3 2 1) );
sub maxima {
my @X = @_;
push @X, my $left = -1e32;
my @sol;
while(@X>1) {
push @sol,$X[0] if $left < $X[0] && $X[0] > $X[1];
$left = shift @X;
}
return @sol;
}
Sure enjoy learning new languages and seeing new solutions.
Thanks, all.
Steve
@James Curtis-Smith and @bookofstevegraham: What do your programs return when given (1 3 3 1) as input? They should return a local maximum of (3).
@programmingpraxis: Good catch – Didn’t know you read Cache/Mumps! I was allowing for mountain peaks as maxima, but not buttes.
Revised program and tests
“1 2 1 2 3 4 3 2 3 2 1”
“2 4 3”
“1 3 3 1”
“3”
“”
“”
“1”
“1”
“1 1”
“1”
“7 7 7”
“7”
“7 6 7”
“7 7”
“7 7 6 7”
“7 7”
“7 7 7 7 6 7”
“7 7”
“7 7 7 7 6 7 7”
“7 7”
“7 7 5 5 6 7 7 6 7 5 7”
“7 7 7 7”
“7 7 5 5 6 7 7 6 7 5 7 7”
“7 7 7 7”
“7 7 5 5 6 7 7 6 7 5 7 6”
“7 7 7 7”
@bookofstevegraham: Actually, I don’t speak cache/mumps. but it’s not too hard to figure out that you have a statement “if this > previous and this > next then output this” which handles peaks but not plateaus.
The problem is my fault, not yours. The description mentions plateaus, but not clearly, and I didn’t give an example of an input that contains plateaus. If my example had been (1 2 1 2 3 4 3 2 3 3 3 2 1) the situation would have been obvious.
@programmingpraxis: Good points. Thanks for your efforts: They have provided many hours of enjoyment and learning.
Here’s a Haskell version. We take advantage of
Nothing
being less thanJust x
for allx
(assumingx
is comparable). Also, the functiondivvy
is such thatdivvy 3 1 [1..5]
equals[[1,2,3],[2,3,4],[3,4,5]]
.A solution in Racket:
Examples: