replicant4 has asked for the
wisdom of the Perl Monks concerning the following question:
Mighty perl monks,
my question might sound amateurt but I have a perls script that runs through a loop 500 times and each time I calculate a value called mutual. From each loop I need the highest value, so I have a few lines of code that do just that . My code is
push (@holdmutual,$mutual);
print "vaggos3\n";
}
#print " $l,$j,$mutual\n";
}
#end of loop2 for each site2
#print " $l,$j,$mutual\n";
}
#getting always the highest mutual inforamtion value for each si
+te combination.
my $highestvalue=0;
foreach my $holdmutual(@holdmutual){
if ($holdmutual>$highestvalue){
$highestvalue=$holdmutual;
}print "vaggos4\n";
}
print "$highestvalue\n";
Does anyone has a suggestion of a simpler code that does that but it is not as memory intensive as mine? (for example I don't think that I need the array but I am not sure how to get rid of it.)
Another thing is that the values I get have a lot of decimals. Is there any way for me to get only the first three decimal values e.g 1.678 instead of what I actually get 1.2345678977885454?
Thanks in advance for anyone's help
replicant4
Re: getting the highest value in a simpler way
by borisz (Canon) on Nov 10, 2004 at 11:38 UTC

There are more ways to get the max of a array. You can calc to max value whenever you insert something, or use
use List::Util qw/max/;
my $highestvalue = max @holdmutual;
printf "%.3f", $highestvalue;
The other part of the question is that you might use sprintf or printf.
 [reply] [d/l] [select] 

Replicant4
What I am actually asking is that I don't really need to store all the values I just need the maximum. So is there a way that instead of using an array, I can just store mutual and every time that my program goes through the loop, if mutual is higher then it will be replaced. My actual problem is that for every round ~1500x1500 calculations are being processed and this takes up a very large amount of time.
Thanks
 [reply] 

package XX::Maximum;
sub new { my ( $class, $value ) = @_; bless \$value, $class }
sub set { ${$_[0]} = $_[1] }
sub get { ${$_[0]} }
sub insert {
return ${$_[0]} unless defined $_[1];
return ${$_[0]} = ( !defined(${$_[0]} ) ? $_[1] : ( ${$_[0]} > $_[1]
+ ? ${$_[0]} : $_[1] ));
}
package main;
my $t = XX::Maximum>new;
$t>insert(1);
$t>insert(0);
$t>insert(undef);
print $$t, "\n";
$t>insert(10);
print $$t, "\n";
$t>insert(10);
print $$t, "\n";
print $t>get, "\n";
$t>set(undef);
 [reply] [d/l] 
Re: getting the highest value in a simpler way
by cLive ;) (Parson) on Nov 10, 2004 at 22:31 UTC

I like this gem (from Effective Perl Programming by Joseph Hall) for determining the highest value of $x and $y:
$highestvalue = [ $x => $y ] > [ $x <= $y ]
Very pretty :)
cLive ;)  [reply] [d/l] 

I realize this is a very old thread, but I just found it and was wondering about the efficiency of this.
I understand you could have a long, complicated expression with no room for if statements, with
[ $x => $y ] > [ $x <= $y ]
as just a small portion of that expression and the example of my $highestvalue is just a simple example.
However if you were in a loop, and just wanted to keep overwriting $highestvalue every time you find a higher value, wouldn't it be better to write it this way?:
my $x = 0;
foreach $y (@lots_of_values){
$x = $y if $y > $x;
}
return $x;
Is that just as efficient as writing it this way?:
my $x = 0;
foreach $y (@lots_of_values){
$x = [ $x => $y ] > [ $x <= $y ];
}
return $x;
The second way seems overly complicated in this case.  [reply] [d/l] [select] 

 [reply] 


...which is same as $max = $x <= $y ? $y : $x; and w/o dereferencing. (No, i did not benchmark.)
 [reply] [d/l] 

$highestvalue = [ $x => $y ] > [ $x <= $y ]
Anybody can explanin me this code? I'm a bit confused ;)
Uksza  [reply] [d/l] 

my $ary = ( $x, $y );
my $aryRef = \ @ary;
And $x <= $y is just a boolean expression that will give a false (0) result if $x > $y and true value (1) otherwise.
$aryRef>[ 0/1 ] will return either $ary[ 0 ] or $ary[ 1 ].
Putting it all together, you get an expression that will construct an anonymous array containing $x, $y, then dereferences that anonmous array and uses the boolean result of the comparison to select the greater of the two values before assigning it to the scalar.
"But you should never overestimate the ingenuity of the sceptics to come up with a counterargument." Myles Allen
"Think for yourself!"  Abigail
"Time is a poor substitute for thought"theorbtwo
"Efficiency is intelligent laziness." David Dunham
"Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side."  tachyon
 [reply] [d/l] [select] 

Re: getting the highest value in a simpler way
by exussum0 (Vicar) on Nov 10, 2004 at 11:47 UTC

For finding the highest value of mutual, you aren't going to get much better than what you have now. You need to go through, element by element, either by using a perl module or by what you have done here. If you can keep track of mutual's calculated, you can save yourself a little time. i.e.
for( a bunch of values )
{
mutual = complexCalculation();
max = mutual if( max < mutual or max = 0 );
}
If you don't have such a loop, don't worry too much. The module suggested or what you are doing is fast enough. After all, searching for max or min in an unsorted list takes O(n) time. Oh, and btw, dont' think of sorting before hand unless you want it sorted for other reasons, as that is in the realm of O(n lg n) time.

Then B.I. said, "Hov' remind yourself
nobody built like you, you designed yourself"
 [reply] [d/l] 

The OP's node carries the title, "getting the highest value in a simpler way", but his followups seem to reveal that the primary concern is time efficiency, not necessarily simplicity. So it's a little uncertain what exactly is wanted.
But there is something else that hasn't been suggested, for keeping track of the max. That is to use a new datastructure altogether.
Your completely correct comment is "don't think of sorting beforehand .... as that is ... O(n log n) time." I agree, that as you have said, unless you want a sorted list for other reasons, don't sort just to find the max. But what if you always only want the max, or always only want the min, and don't really care about anything else? This is a job for a heap. Heaps are not all that frequently used in Perl because the builtin datatypes so conveniently handle all the more common needs, but heaps can still be helpful. The basic principle is that as you add items to the heap, the minimum item is always kept at the top of the heap where it can be retrieved in O(log n) time. Insertion is O(log n) time, which is faster than the O(n) time needed to do a linear search for a min. Though heaps are usually discussed in terms of placing the min at the top, it's easy enough to invert them and have the max at the top, so long as the decision is made up front, before the heap is generated.
There is a great module on CPAN called Heap::Simple that makes it really easy to implement a heap. Again, if all you're doing with your data is inserting values, or extracting the max, or all you're doing with your data is inserting values, or extracting the min, repeatedly, a heap is a great solution.
 [reply] 

Am I correct in saying that insertion is O(log n) for each insertion? Therefore, if you have 500 insertions, it would be something like (sum)O(log n) (for n=1>500)??
That is no longer O(log n), but something higher than that, I believe. I don't know what would dominate, though, the O(log n), or the (1>500) sum, but I suspect the latter.
 [reply] 


Re: getting the highest value in a simpler way
by tmoertel (Chaplain) on Nov 10, 2004 at 16:21 UTC

Here's a fun approach. We can encapsulate our maximumfinding logic
inside of a closurebased factory function that makes "maximum
finders":
sub make_max_finder {
my $max;
sub {
for (@_) { $max = $_ if !defined $max  $_ > $max }
$max;
}
}
Then we can create a new maximum finder whenever we need one by
calling the factory function:
my $max_finder = make_max_finder();
We can feed values to our newly made maximum finder, and it will remember the maximum
value it has seen:
$max_finder>(0);
print $max_finder>(); # 0
print $max_finder>(1); # 0
print $max_finder>(2); # 2
We can pass it more than one value at a time, too:
print $max_finder>(1, 3, 0); # 3
print $max_finder>(1, 0, 1); # 3
With this factory, we can solve your problem like so:
my $max_finder = make_max_finder();
while (my $next_val = get_next_value()) {
$max_finder>($next_val);
}
$max_finder>(); # retrieve maximum
Or, if you have the memory to hold all of your values in an array,
the "oneshot" option is available:
my $max_value = make_max_finder()>(@values);
Cheers, Tom
 [reply] [d/l] [select] 
Re: getting the highest value in a simpler way
by Forsaken (Friar) on Nov 10, 2004 at 15:53 UTC

why so difficult? start off with a highestvalue of 0 as you already do and then at the end of each loop simply compare mutual to highestvalue. if mutual is less than highestvalue you do nothing, if it is higher then highestvalue takes on the value of mutual.  [reply] 
Re: getting the highest value in a simpler way
by punch_card_don (Curate) on Nov 10, 2004 at 18:12 UTC

From the way the problem has been described, Foresaken's suggestion seems the best in the KISS category.
$max = 0;
for $i (1 ..500) {#your loop
$mutual = some_calculation;
if ($mutual > $max) {$max = $mutual;)
}
 [reply] [d/l] 
Re: getting the highest value in a simpler way
by cfreak (Chaplain) on Nov 10, 2004 at 15:29 UTC

Why all the extra modules stuff? Just sort the array and grab the first or last one depending on how you sorted it.
This should do everything you want:
@array = sort { $b <=> $a } @array;
printf("%03d",shift @array);
Update This way is simple but of course as sporty said, it takes longer
 [reply] [d/l] 

