Re: Lagrange Polynomials in Perl
by BrowserUk (Patriarch) on Apr 28, 2015 at 19:51 UTC
|
You cannot shift an array off the argument stack: my @x = shift;
Assuming you are calling this as: lagrange( \@vals, \@funcs );
What you are shifting of the argument stack is an array reference; and to do that properly you need to assign it to a scalar variable:
my $refX = shift;
And then indirect through that to get your values: $den *= ( $refX->[$i] - $refX->[$j] );
Do that for both your parameter arrays -- athough you do not seem to ever use @f anywhere in that function?
Also, what do you think this does: my $point = @x;?
With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
| [reply] [d/l] [select] |
|
|
Does $scalar = @array produce a scalar containing the size of the array?
| [reply] |
|
|
Does $scalar = @array produce a scalar containing the size of the array?
Yes. But $point is a strange name for the size of an array!
If that is meant to be short for $lastPoint, it is still deceptive as with zero-based arrays, the index of the last point in the array is one less than the size of the array.
(Which incidentally, can be obtained using my $lastPoint = $#array;)
With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
| [reply] [d/l] [select] |
|
|
|
|
|
|
|
Yes, it does, but I would rather write:
my $array_count = scalar @array;
or, omitting the scalar built-in unnecessary in scalar context:
my $array_count = @array;
But I often prefer to add the scalar built-in, even when not necessary, just to make my intent clearer to the maintainer (which, incidentally, one year from now, might be me, and such indication might prove useful to help me understand the intent of the code).
Update: removed the scalar word which I had forgotten to omit in the second snippet. Thanks to Athanasius for pointing it out.
| [reply] [d/l] [select] |
Re: Lagrange Polynomials in Perl
by Corion (Patriarch) on Apr 28, 2015 at 19:51 UTC
|
my @x = shift;
This is unlikely to do what you might think it does.
Perl flattens arrays when passing them to a function:
sub foo {
my( @x, @y )= @_;
};
foo( @a, @b );
The above code will always end up with @x containing all elements of @a and @b, and @y will always be empty. | [reply] [d/l] [select] |
Re: Lagrange Polynomials in Perl
by AnomalousMonk (Archbishop) on Apr 28, 2015 at 21:23 UTC
|
And, of course, the obligatory NB about C-style for- or foreach-loops versus Perl-style: IMHO, the loops in the OPed code are better as:
my $arrayref = ...;
for my $i (0 .. $#$arrayref) {
for my $j (0 .. $#$arrayref) {
do_something_with($i, $j);
}
}
Give a man a fish: <%-(-(-(-<
| [reply] [d/l] [select] |
|
|
I actually didn't realize that this was an option. I've only seen the C-style before, but this has a certain Fortran flair, I appreciate that.
| [reply] |
Re: Lagrange Polynomials in Perl
by GotToBTru (Prior) on Apr 28, 2015 at 19:39 UTC
|
| [reply] |
|
|
This is just the module containing the algorithm, I invoke them in specific programs.
| [reply] |
|
|
Well, yeah. And your question is why your program doesn't work when used. Do you suppose an example of how you use it might be helpful in diagnosing the problem? The problem could lie with the program itself (and several others have found issues), but it can also be how it is invoked.
| [reply] |
Re: Lagrange Polynomials in Perl
by hdb (Monsignor) on Apr 29, 2015 at 06:48 UTC
|
$LC[$i] = $f[$i]/$den;
$den = 1;
are within the "$j" loop which looks wrong to me. I would write your code like this (ignoring some of the stylistic advice given so far):
use strict;
use warnings;
sub lagrange {
my $x = shift;
my $f = shift;
my @LC;
my $n = @$x;
for (my $i=0; $i < $n; $i++) {
my $den = 1;
for (my $j=0; $j < $n; $j++) {
next if $j == $i;
$den *= $x->[$i] - $x->[$j];
}
$LC[$i] = $f->[$i]/$den;
}
return \@LC;
}
sub lageval {
my $x = shift;
my $LC = shift;
my $x0 = shift;
my $n = @$x;
my $sum = 0;
for (my $i=0; $i<$n;$i++) {
my $mult = $LC->[$i];
for (my $j = 0; $j<$n; $j++) {
next if $j == $i;
$mult *= $x0 - $x->[$j];
}
$sum += $mult;
}
return $sum;
}
my @x = map { $_ - 2 } 0..4;
my @f = map {$_**2} @x;
my $LC = lagrange \@x, \@f;
my $x0 = 0.5;
my $y = lageval \@x, $LC, $x0;
print "$x0 $y\n";
| [reply] [d/l] [select] |
|
|
It works, once I changed the arrays to array references it worked. I tested it by approximating a sine function, and was able to get very close to the small angle approximation.
| [reply] |
|
|
| [reply] |
|
|
|
|
|
Re: Lagrange Polynomials in Perl
by oiskuu (Hermit) on Apr 29, 2015 at 16:15 UTC
|
use strict;
use warnings;
use List::Util q(reduce);
sub xdi {
my ($x, $d, $i) = @_;
reduce{ $a*$b }map{ $_==$i || $d - $x->[$_] }keys @$x
}
sub lagc {
my ($x, $f) = @_;
[map{ $f->[$_] / xdi($x,$x->[$_],$_) }keys @$x]
}
sub lagx {
my ($x, $L, $x0) = @_;
reduce{ $a+$b }map{ $L->[$_] * xdi($x,$x0,$_) }keys @$x
}
Consider also using bigrat if exact interpolation is sought.
Update. Same thing with sample gnuplot.
Final note: Math::Polynomial can perform Lagrange interpolation, so there's probably little reason to roll your own module. | [reply] [d/l] [select] |
|
|
| [reply] |
|
|
I like this bigrat. CPAN to the rescue.
| [reply] |