Mascasc has asked for the wisdom of the Perl Monks concerning the following question:

This question is sufficiently answered, I'm just an idiot that forgets to use references

I wrote a little module that I could use to approximate lagrange polynomials in Perl.

The only problem is, it doesn't work. I consistantly get a single value whenever I feed some test function into Lagrange's maw.

Any help would be appreciated, code below:

#! /usr/bin/perl use strict; use warnings; sub lagrange { #x is an array of x values, f is an array of function values, LC are t +he reduced lagrange coefficients. my @x = shift; my @f = shift; my ($i, $j); my @LC; my $point = @x; my $den = 1; for ($i=0; $i < $point; $i++) { for ($j = 0; $j < $point; $j++) { unless ($j == $i) { $den *= ($x[$i] - $x[$j]); } $LC[$i] = $f[$i]/$den; $den = 1; } } return \@LC; } sub lageval { my @x = shift; my @LC = shift; my $x0 = shift; my $point = @x; my @mult; my $sum = 0; my ($i, $j); for($i=0; $i<$point; $i++) { $mult[$i] = 1; } for ($i=0; $i<$point;$i++) { for ($j = 0; $j<$point; $j++) { unless ($j == $i) { $mult[$i] *= $x0 - $x[$j]; } } $sum += $mult[$i] * $LC[$i]; } return $sum; } 1;

Replies are listed 'Best First'.
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.
    "Science is about questioning the status quo. Questioning authority". I'm with torvalds on this
    In the absence of evidence, opinion is indistinguishable from prejudice. Agile (and TDD) debunked
      Does $scalar = @array produce a scalar containing the size of the array?
        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.
        "Science is about questioning the status quo. Questioning authority". I'm with torvalds on this
        In the absence of evidence, opinion is indistinguishable from prejudice. Agile (and TDD) debunked
        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.

        Je suis Charlie.
Re: Lagrange Polynomials in Perl
by Corion (Patriarch) on Apr 28, 2015 at 19:51 UTC

    Have you inspected the values your functions receive? How are you calling your functions?

    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.

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:  <%-(-(-(-<

      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.
Re: Lagrange Polynomials in Perl
by GotToBTru (Prior) on Apr 28, 2015 at 19:39 UTC

    You never invoke either subroutine.

    Dum Spiro Spero
      This is just the module containing the algorithm, I invoke them in specific programs.

        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.

        Dum Spiro Spero
Re: Lagrange Polynomials in Perl
by hdb (Monsignor) on Apr 29, 2015 at 06:48 UTC

    Does your program work now? What I find strange is that these lines

    $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";
      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.

        It should be exact for polynomials, correct? Have you tried that?

Re: Lagrange Polynomials in Perl
by oiskuu (Hermit) on Apr 29, 2015 at 16:15 UTC

    Or, you could go fancy with map and reduce:

    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.

      Well, this was more to challenge myself a little with Perl than to make something that I legit NEEDED. I've only been using it for about a month or so.

      I like this bigrat. CPAN to the rescue.