Perl中牛顿法

时间:2014-11-09 19:36:25

标签: perl calculus newtons-method

好的,对于我的数学课,我们被要求编写一个执行和打印牛顿方法的程序,直到值收敛并且我们有一个函数的根。起初我觉得这很容易。直到我无法获得第一次使用的第二次导出的值。我对语言的了解是基本的。真的很基本,所以你要看的东西可能并不漂亮。

#!usr/bin/perl

use PDL;

print "First guess? (this is x0)\n";
$xorig = <>;

do {
  &fx;
} until ($fex == 0);

sub fx {

  if ($xn == 0) {
    $x = $xorig;
  }
  else {
    $x = $xn;
  }

  print "What is the coefficient (for each factor) of your function?\n";
  $fcx = <STDIN>;
  push @coefficient_of_x, $fcx;

  print "... times x to the (enter exponent, if no exponent, enter 1. if no x, enter 0)?\n";
  $fex = <STDIN>;
  push @exponent_x, $fex;

  chomp ($fcx, $fex, $x, $xorig);

  $factor = $fcx * ($x ** $fex);
  push @fx, $factor;
}

my $fx = 0;
foreach my $variable (@fx) {
  $fx = $variable + $fx #THIS PROVIDES A VALUE FOR THE GIVEN F(X) WITH A GIVEN X VALUE
}
print "f($x)=$fx\n";

do {
  &fprimex;
} until ($fprimeex == 0);

sub fprimex {

  if ($xn == 0) {
    $x = $xorig;
  }
  else {
    $x = $xn;
  }

  print "What is the coefficient (for each factor) of your derivative function?\n";
  $fprimecx = <STDIN>;
  push @coefficient_of_fpx, $fprimecx; 

  print "... times x to the (enter exponent, if no exponent, enter 1. if no x, enter 0)?\n";
  $fprimeex = <STDIN>;
  push @exponent_fpx, $fprimeex;

  chomp ($fprimecx, $fprimeex, $x, $xorig);

  $factorprime = $fprimecx * ($x ** $fprimeex);
  push @fprimex, $factorprime;
}

$fprimex = 0;
foreach my $variableprime (@fprimex) {
  $fprimex = $variableprime + $fprimex #THIS PROVIDES A VALUE FOR THE GIVEN F'(X) WITH THAT SAME X VALUE
}
print "f'($x)=$fprimex\n";

sub x0 {
  $xn = $xorig - $fx / $fprimex; #THIS IS NEWTON'S METHOD EQUATION FOR THE FIRST TIME
  push @newxn, $xn;
  print "xn ia $xn\n";
}

&x0;

foreach $value (@exponent_x) {
  $exponent_x = $xn ** $value;
  push @part1, $exponent_x;
  $part1 = @part1;
}

foreach $value2 (@coefficient_of_x) {
  $part2 = $value2 * @part1;
  push @final1, $part2;
}

print "@part1\n";
print "@final1\n";

基本上它是什么,我首先要求第一个猜测。我使用这个值来定义系数和f(x)的指数,以便根据给定的x得到f(x)的值。我再次为f&#39;(x)做。然后我第一次执行牛顿方法并获得新值xn。但是我很难获得f(xn)和f&#39;(xn)的值,这意味着我无法获得x(n + 1)并且无法继续使用牛顿&# 39;方法。我需要帮助。

2 个答案:

答案 0 :(得分:4)

欢迎来到Perl。

我强烈建议您对代码进行以下更改:

  1. 始终在每个Perl脚本中包含use strict;use warnings;
  2. 在您接受STDIN时,始终chomp输入您的信息:

    chomp( my $input = <STDIN> );
    
  3. 不要不必要地创建子程序,特别是对于像这样的一次性脚本。

  4. 我建议使用带有循环控制语句的无限statement modifier来退出,而不是使用dowhile形式:

    while (1) {
    
        last if COND;
    }
    
  5. 最后,由于多项式的系数都与X的指数相关,我建议使用%hash来方便地保存这些值。

  6. 正如所示:

    #!usr/bin/env perl
    use strict;
    use warnings;
    
    print "Build your Polynomial:\n";
    
    my %coefficients;
    
    # Request each Coefficient and Exponent of the Polynomial
    while (1) {
        print "What is the coefficient (for each factor) of your function? (use a bare return when done)\n";
        chomp( my $coef = <STDIN> );
    
        last if $coef eq '';
    
        print "... times x to the (enter exponent, if no exponent, enter 1. if no x, enter 0)?\n";
        chomp( my $exp = <STDIN> );
    
        $coefficients{$exp} = $coef;
    }
    
    print "\nFirst guess? (this is x0)\n";
    chomp( my $x = <> );
    
    # Newton's Method Iteration
    while (1) {
        my $fx  = 0;
        my $fpx = 0;
    
        while ( my ( $exp, $coef ) = each %coefficients ) {
            $fx += $coef * $x**$exp;
            $fpx += $coef * $exp * $x**( $exp - 1 ) if $exp != 0;
        }
    
        print "   f(x) = $fx\n";
        print "   f'(x) = $fpx\n";
    
        die "Slope of 0 found at $x\n" if $fpx == 0;
    
        my $new_x = $x - $fx / $fpx;
    
        print "Newton's Method gives new value for x at $new_x\n";
    
        if ( abs($x - $new_x) < .0001 ) {
            print "Accuracy reached\n";
            last;
        }
    
        $x = $new_x;
    }
    

答案 1 :(得分:3)

我无法弄清楚您的代码是什么意思。主要问题似乎是你的每个子程序没有清楚地知道每个子程序的作用,因为fxfprimex要求数据以及评估函数(除了求和之外)奇怪的是,这些术语是在子程序之外完成的。这根本不是你想要的,因为指数和系数在整个程序中保持不变,必须多次评估这些函数,而你真的不想每次都要求这些值。

以下是使Perl代码正常工作的一些指示

  • 将程序编写成小块 - 一次一行或两行 - 并在每次添加后检查程序是否编译并运行并产生预期结果。在您尝试运行它之前编写整个程序是灾难的一个方法

  • 始终 use strictuse warnings,并声明每个变量my尽可能接近首次使用的位置。你有许多未声明的变量因此是全局的,并且使用全局变量在代码段之间传递信息是一种在自己的代码中迷失自己的好方法。子程序使用传递给它的参数或在其中声明的变量是一个很好的规则

  • chomp变量一经从文件或终端读取即可。在源处修剪输入字符串的有用习惯是

    chomp(my $input = <>)
    

    这将确保数据中的任何位置都没有错误的换行符

至少应该让你开始。

我有两种想法来表达这一点。我希望它会对你有所帮助,但我真的不想把你拖到你不熟悉的Perl部分。

这是一个使用 Newton-Raphson 方法查找多项式根的程序。我现在跳过了终端输入,并对数据进行了硬编码。通过找到 x 2 - 3000 的正根,它找到了3000的平方根。

请注意,@f@f_prime按常规顺序保存函数的系数及其派生向后,因此{{1}是@f。该程序还计算导数函数的系数,因为这是一件简单的事情,并且比向用户询问另一组值更为可取。

只有一个子例程(-3000, 0, 1),它取一个 x 的值和一系列系数。这用于计算给定值 x 的函数及其导数的值。

算法步骤在

行中
polynomial

计算 x - f(x)/ f'(x)并将其分配给my $new_x = $x - polynomial($x, @f) / polynomial($x, @f_prime); 。每个连续的估计值都会打印到STDOUT,直到循环退出。

比较浮点值是否相等是一个坏主意。计算机浮点值的精度显然是有限的,并且序列可能永远不会收敛到序列的最后两个值相等的点。可以做的最好的事情是检查最后两个值之间的差值的绝对值是否低于合理的增量。对于32位浮点数,精度为10E12是合理的。我发现该系列可以非常可靠地收敛到10E14以内,但是如果你发现你的程序在无限循环中挂起,那么你应该增加余量。

$new_x

<强>输出

use strict;
use warnings;

my @f       = reverse (1, 0, -3000);
my @f_prime = map { $f[$_] * $_ } 1 .. $#f;

my $x = 0.5;
print $x, "\n";

while () {
  my $new_x = $x - polynomial($x, @f) / polynomial($x, @f_prime);
  last if abs($new_x - $x) < $x / 1e14;
  $x = $new_x;
  print $x, "\n";
}

sub polynomial {
  my ($x, @coeffs) = @_;

  my $total = 0;
  my $x_pow = 1;

  for my $coeff (@coeffs) {
    $total += $x_pow * $coeff;
    $x_pow *= $x;
  }

  $total;
}