Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Re^5: Why does my get_max_index function return zero? (High Water Mark Algorithm)

by bliako (Monsignor)
on Jun 04, 2019 at 11:47 UTC ( [id://11100944]=note: print w/replies, xml ) Need Help??


in reply to Re^4: Why does my get_max_index function return zero? (High Water Mark Algorithm)
in thread Why does my get_max_index function return zero? (High Water Mark Algorithm)

I like to plant binary trees:

Edit: for inserting it does a lot more comparisons than a linear search does for searching. For finding min and max there are no comparisons, just traversal. It's use is when one wants to search repeatedly the tree. I have added a search() to the code (30min after).

Edit: after 8hrs, fixed synopsis' testing of min/max, there was a comparator mishap ([0]<=>[1] instead of [1]<=>[0]))!

use My::Bintree; my $T = My::Bintree->new(3, sub { return $_[0] <=> $_[1] }); $T->add(5); $T->add(2); $T->add(8); $T->add(8); $T->add(8); print "maxi: ".join(",",$T->maxi())."\nmini: ".join(",",$T->mini())."\ +n"; print "searched for number 8: ".join(",", $T->search(8))."\n"; # maxi: 8 8 8 # mini: 2 # searched for number 8: 8,8,8 # or add complex data, e.g. (x,y) points and compare their distance fr +om origin $T = My::Bintree->new( # a first point: [1,2], # the comparator: sub { return $_[0]->[0]**2 + $_[0]->[1]**2 <=> $_[1]->[0]**2 + $_[ +1]->[1]**2 } ); $T->add([3,8]); $T->add([5,5]); $T->add([3,4]); $T->add([3,2]); $T->add([3,6]); $T->add([2,3]); $T->add([3,2]); $T->add([2,1]); print "furthest point(s): ".join(',', map { '(x: '.$_->[0].', y: '.$_- +>[1].')' } $T->maxi())."\n"; print "nearest point(s): ".join(',', map { '(x: '.$_->[0].', y: '.$_- +>[1].')' } $T->mini())."\n"; print "searched for points with same distance from origin as point (2, +3): ".join(',', map { '(x: '.$_->[0].', y: '.$_->[1].')' } $T->search +([2,3]))."\n"; # furthest point(s): (x: 3, y: 8) # nearest point(s): (x: 1, y: 2),(x: 2, y: 1) # searched for points with same distance from origin as point (2,3): ( +x: 3, y: 2),(x: 2, y: 3),(x: 3, y: 2) # here is how you find the max index (and value) of an array # even if there are several max/min values my @arr = (3,4,5,100,4,5,100,100, 0, 1, 2); # the comparator must return -1 if 1st arg < 2nd arg!!! (re: edit abov +e) $T = My::Bintree->new([0, shift @arr], sub { $_[0]->[1] <=> $_[1]->[1] + }); $T->add([$_,$arr[$_]]) for 0..$#arr; print "max: ".join(',', map { '(index: '.$_->[0].', value: '.$_->[1].' +)' } $T->maxi())."\n"; print "min: ".join(',', map { '(index: '.$_->[0].', value: '.$_->[1].' +)' } $T->mini())."\n"; print "searched for value of 5: ".join(',', map { '(index: '.$_->[0].' +, value: '.$_->[1].')' } $T->search([undef,5]))."\n"; # max: (index: 2, value: 100),(index: 5, value: 100),(index: 6, value: + 100) # min: (index: 7, value: 0) # searched for value of 5: (index: 1, value: 5),(index: 4, value: 5)

The package and tests:

# construct a binary tree and search for max/min values given custom d +ata and comparator # by bliako for https://perlmonks.org/?node_id=11100889 # 04/06/2019 { # begin package My::Bintree package My::Bintree; use strict; use warnings; use Data::Dumper; sub new { my $class = shift; my $dat = shift; my $parent = shift; my $dat_comparator = shift; if( ref $parent eq 'CODE' ){ ($parent, $dat_comparator) = ($dat_comparator, $parent); } return $parent->add($dat) if $parent; my $self = { 'data' => $dat, # the comparator sub takes 2 args, it must return -1,0,1 **IF +1st arg lt,eq,gt 2nd arg** 'cmp' => $dat_comparator ? $dat_comparator : sub { $_[0] cmp $ +_[1] }, 'parent' => $parent, 'children' => [undef, [], undef], }; my $ret = bless $self => $class; return $ret; } sub add { my $self = shift; my $dat = shift; my $current = $self; while( 1 ){ my $dat_comparator = $current->{'cmp'}; my $cmpr = $current->compare($dat); if( $cmpr == 0 ){ my $achild = My::Bintree->new($dat, $dat_comparator); $current->_add_equ_child($achild); return $achild; } else { my $achild = \ $current->{'children'}->[$cmpr+1]; if( defined $$achild ){ $current = $$achild; } else { $$achild = My::Bintree->new($dat, $dat_comparator); $$achild->{'parent'} = $current; return $$achild; } } } } sub mini { my $self = shift; my $current = $self; $current = $current->lth() while $current->lth(); my @ret = ($current->data()); push @ret, $_->data() foreach @{$current->equ()}; return @ret; } sub maxi { my $self = shift; my $current = $self; $current = $current->gth() while $current->gth(); my @ret = ($current->data()); push @ret, $_->data() foreach @{$current->equ()}; return @ret; } sub search { my $self = shift; my $dat = shift; my $current = $self; while( 1 ){ my $cmpr = $current->compare($dat); if( $cmpr == 0 ){ return map { $_->data() } $current, @{$curre +nt->equ()} } my $achild = \ $current->{'children'}->[$cmpr+1]; return () unless defined $$achild; $current = $$achild; } } sub parent { return $_[0]->{'parent'} } sub data { return $_[0]->{'data'} } # compare self to input sub compare { my $self = $_[0]; return $self->{'cmp'}->($_[1], $self->data()) } sub _add_lth_child { $_[0]->{'children'}->[0] = $_[1]; $_[1]->{'parent +'} = $_[0]; } sub _add_gth_child { $_[0]->{'children'}->[2] = $_[1]; $_[1]->{'parent +'} = $_[0]; } sub _add_equ_child { push @{$_[0]->{'children'}->[1]}, $_[1]; ; $_[1]- +>{'parent'} = $_[0]; } sub lth { return $_[0]->{'children'}->[0] } sub gth { return $_[0]->{'children'}->[2] } sub equ { return $_[0]->{'children'}->[1] } 1; } # end package My::Bintree package main; use strict; use warnings; use List::Util qw(shuffle); do_synopsis(); do_benchmarks(); do_tests(); sub do_synopsis { # synopsis # create a tree my $T = My::Bintree->new(3, sub { return $_[0] <=> $_[1] }); $T->add(5); $T->add(2); $T->add(8); $T->add(8); $T->add(8); print "maxi: ".join(",",$T->maxi())."\nmini: ".join(",",$T->mini() +)."\n"; print "searched for number 8: ".join(",", $T->search(8))."\n"; # or add complex data, e.g. (x,y) points and compare their distanc +e from origin # or add complex data, e.g. (x,y) points and compare their distanc +e from origin $T = My::Bintree->new( # a first point: [1,2], # the comparator: sub { return $_[0]->[0]**2 + $_[0]->[1]**2 <=> $_[1]->[0]**2 + + $_[1]->[1]**2 } ); $T->add([3,8]); $T->add([5,5]); $T->add([3,4]); $T->add([3,2]); $T->add([3,6]); $T->add([2,3]); $T->add([3,2]); $T->add([2,1]); print "furthest point(s): ".join(',', map { '(x: '.$_->[0].', y: ' +.$_->[1].')' } $T->maxi())."\n"; print "nearest point(s): ".join(',', map { '(x: '.$_->[0].', y: ' +.$_->[1].')' } $T->mini())."\n"; print "searched for points with same distance from origin as point + (2,3): ".join(',', map { '(x: '.$_->[0].', y: '.$_->[1].')' } $T->se +arch([2,3]))."\n"; # here is how you find the max index (and value) of an array # even if there are several max/min values my @arr = (3,4,5,100,4,5,100,100, 0, 1, 2); $T = My::Bintree->new([0, shift @arr], sub { $_[1]->[1] <=> $_[0]- +>[1] }); $T->add([$_,$arr[$_]]) for 0..$#arr; print "max: ".join(',', map { '(index: '.$_->[0].', value: '.$_->[ +1].')' } $T->maxi())."\n"; print "min: ".join(',', map { '(index: '.$_->[0].', value: '.$_->[ +1].')' } $T->mini())."\n"; print "searched for value of 5: ".join(',', map { '(index: '.$_->[ +0].', value: '.$_->[1].')' } $T->search([undef,5]))."\n"; } # some benchmarks comparing to a linear search sub do_benchmarks { my $num_comparisons; my $cmpsub = sub { $num_comparisons++; return $_[0]->[1] <=> $_[1]->[1] }; my $N = 2; my $MAXI = 1000; my $nrb = 2*$MAXI+1; my $t = time; my ($cmp_insert, $cmp_search_maxi, $cmp_search_mini) = (0)x3; for (1..$N){ srand $t++; my @rb = shuffle -$MAXI .. $MAXI; my $imax=$MAXI-1; for (my $i=$MAXI; $i-->0; ){ $imax=$i if ($rb[$imax]<=$rb[$i]); } $num_comparisons = 0; my $T; for(0..$#rb){ my $dat = [$_, $rb[$_]]; if( defined $T ){ $T->add($dat); } else { $T = My::Bintree->new( $dat, # data to insert $cmpsub # comparator sub for $dat ); } } $cmp_insert += $num_comparisons; # maxi and mini do not do any comparisons they only traverse $num_comparisons = 0; my @maxi = $T->maxi(); $cmp_search_maxi += $num_comparisons; $num_comparisons = 0; my @mini = $T->mini(); $cmp_search_mini += $num_comparisons; print "comparisons: done trial $_ : insert: $cmp_insert, maxi: + $cmp_search_maxi, mini: $cmp_search_mini\n"; } print "linear search : #comparisons is ".($N*(2*$MAXI+1))."\nbinar +y search #comparisons is insert: $cmp_insert, maxi: $cmp_search_maxi, + mini: $cmp_search_mini\n"; } # Tests for finding max and min values and their indices from a 1d arr +ay sub do_tests { use Test::More; my $nt = 0; # the comparator sub must # return -1 if 1st is less than 2nd, 0 if equal, 1 if 1st is great +er than 2nd my $comparator = sub { $_[0]->[1] <=> $_[1]->[1] }; use constant MAXI => 10; for my $maxi (0 .. 2*MAXI+1){ for my $mini (0 .. 2*MAXI+1){ next if $maxi == $mini; my @rb = shuffle -MAXI..MAXI; $rb[$maxi] = 99; $rb[$mini] = -99; my $T = undef; my ($maxi, @rest) = map { $_->[0] } sort { $b->[1] <=> $a->[1] + } map { [ $_ , $rb[$_] ] } 0..$#rb; my $mini = pop @rest; for(0..$#rb){ my $dat = [$_, $rb[$_]]; if( defined $T ){ ok defined $T->add($dat), "add()"; $nt++; } else { $T = My::Bintree->new( $dat, # data to insert $comparator # comparator sub for $dat ); ok defined $T, "new()"; $nt++; } } my @maxifound = $T->maxi(); ok(1==scalar @maxifound, "found exactly 1 maximum"); $nt++; ok($maxifound[0]->[0]==$maxi, "max index agree (found @{$maxif +ound[0]}, expected $maxi)"); $nt++; my @minifound = $T->mini(); ok(1==scalar @minifound, "found exactly 1 minimum"); $nt++; ok($minifound[0]->[0]==$mini, "min index agree (found @{$minif +ound[0]}, expected $mini)"); $nt++; } # for mini } # for maxi # add many max min with same value for my $i (0 .. 2*MAXI+1){ my $t = time; srand $t; my @maxiput = (shuffle 0 .. 2*MAXI+1)[0..MAXI/5]; srand $t; my @miniput = (shuffle 0 .. 2*MAXI+1)[-MAXI/5..-1]; srand $t; my @rb = shuffle -MAXI..MAXI; @rb[@maxiput] = (99)x@maxiput; @rb[@miniput] = (-99)x@miniput; my $T = undef; for(0..$#rb){ my $dat = [$_, $rb[$_]]; if( defined $T ){ ok defined $T->add($dat), "add()"; $nt++; } else { $T = My::Bintree->new( $dat, # data to insert $comparator # comparator sub for $dat ); ok defined $T, "new()"; $nt++; } } my @maxifound = $T->maxi(); ok(scalar(@maxiput)==scalar(@maxifound), "found exactly ".scal +ar(@maxifound)." maximum"); $nt++; my %tmp1 = map { @$_ } @maxifound; ok(exists $tmp1{$_}, "max index agree ($_)"),$nt++ for @maxipu +t; my @minifound = $T->mini(); ok(scalar(@miniput)==scalar(@minifound), "found exactly ".scal +ar(@minifound)." minimum"); $nt++; %tmp1 = map { @$_ } @minifound; ok(exists $tmp1{$_}, "max index agree ($_)"),$nt++ for @minipu +t; } # for i # END done_testing($nt); }

bw, bliako

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11100944]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having an uproarious good time at the Monastery: (1)
As of 2024-04-24 23:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found