Reputation: 352
I am looking to distinguish the difference between side_i and side_j to find the smallest value ... following on... to then multiply the smallest and largest by a given number.
That's pretty simple to do. However, I want to still be able to distinguish between i and j in such a way that I know which one is which after they have been multiplied.
This is what I have so far. Please note , I am a very new user of Perl and general coding:
use strict;
use Getopt::Long;
use List::Util qw(max);
use File::Find;
use warnings 'all';
my $side_i = 24.56;
my $side_j = 3.56;
my $maxi = 10;
my $maxj = 10;
my $threshold = 0.05;
my $small_side;
(my $sside, my $bside) = smallestTest($side_i, $side_j);
sub smallestTest{
my $sside;
my $bside;
print "$_[0] $_[1] /n";
if ($_[0]<$_[1]){
$sside = $_[0];
$bside = $_[1];
} else {
$sside = $_[1];
$bside = $_[0];
}
return($sside, $bside);
}
print "Biggest side is $bside /n";
my $newSide_i = $.....*20;
my $newSide_j = $.....*21;
Upvotes: 0
Views: 873
Reputation: 118148
In math, what you are referring to are called argmax and argmin. You can get their functionality in various ways.
First, given a list of values, you can find their maximum and minimum, and then select the indices which correspond to elements of the list with the maximum and minimum values, respectively. This is implemented in the using_only_max_min
function below. Note that this method makes two passes over the array for each extremum: Once to find the extremum and another time to find the corresponding indices.
Or, given a list of values, you can find the indices which corresponding to the extrema in the list, and then select the corresponding value for any of them to get the value of the extremum. This is implemented in the using_by_functions
routine below.
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
use List::AllUtils qw(max min max_by min_by);
my @n = (1, 3, 5, 8, 8, 3, 2, 4, 4, 6);
print Dumper using_only_max_min(\@n);
print Dumper using_by_functions(\@n);
sub using_only_max_min {
my $n = shift;
my $max = max @$n;
my @argmax = grep $n->[$_] == $max, 0 .. $#$n;
my $min = min @$n;
my @argmin = grep $n->[$_] == $min, 0 .. $#$n;
return {
max => $max,
argmax => \@argmax,
min => $min,
argmin => \@argmin,
};
}
sub using_by_functions {
my $n = shift;
my @argmax = max_by { $n->[$_] } 0 .. $#$n;
my $max = $n->[$argmax[0]];
my @argmin = min_by { $n->[$_] } 0 .. $#$n;
my $min = $n->[$argmin[0]];
return {
max => $max,
argmax => \@argmax,
min => $min,
argmin => \@argmin,
};
}
Output:
$VAR1 = { 'argmin' => [ 0 ], 'max' => 8, 'min' => 1, 'argmax' => [ 3, 4 ] }; $VAR1 = { 'argmax' => [ 3, 4 ], 'min' => 1, 'max' => 8, 'argmin' => [ 0 ] };
You can also do this without using any libraries, and find the two extrema in one go. The function below returns a hash reference which contains two entries, max
and min
. Each of those point to a list whose first value is the value of the extremum, and the second value is the list of indices corresponding to the extremum:
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my @n = (1, 3, 5, 8, 8, 3, 2, 4, 4, 6);
print Dumper find_extrema(\@n, sub { $_[0] <=> $_[1]});
sub find_extrema {
my ($n, $cmp) = @_;
my ($max, $min) = ([$n->[0], [0]], [$n->[0], [0]]);
for my $i (1 .. $#$n) {
my $v = $n->[$i];
my $r = $cmp->($v, $max->[0]);
if ($r >= 0) {
$r ? $max = [$v, [$i]] : push @{ $max->[-1] }, $i;
next;
}
my $s = $cmp->($v, $min->[0]);
if ($s <= 0) {
$s ? $min = [$v, [$i]] : push @{ $min->[-1] }, $i;
}
}
return {
max => $max,
min => $min,
};
}
Output:
$VAR1 = { 'min' => [ 1, [ 0 ] ], 'max' => [ 8, [ 3, 4 ] ] };
Upvotes: 1
Reputation: 126742
I suggest that you use a hash %side
to contain the values. Then the $sside
and $bside
variables can hold the identities i
and j
of the smallest and biggest values
You don't explain what you want to do after you've determined which is which, so my multiplications at the end are probably way off
use strict;
use warnings 'all';
my %side = ( i => 24.56, j => 3.56 );
my ($sside, $bside) = $side{i} < $side{j} ? qw/ i j / : qw/ j i /;
$side{$sside} *= 20;
$side{$bside} *= 21;
If you prefer, you could use sort
instead to order the hash keys. The result is identical
my ($sside, $bside) = sort { $side{$a} <=> $side{$b} } keys %side;
Upvotes: 4