【问题标题】:Newton's Method in perlperl 中的牛顿法
【发布时间】:2015-01-06 02:01:24
【问题描述】:

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

#!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'(x) 再做一次。然后我第一次执行牛顿法并得到新值xn。但是我很难获得 f(xn) 和 f'(xn) 的值,这意味着我无法获得 x(n+1) 并且无法继续使用牛顿法。我需要帮助。

【问题讨论】:

    标签: perl calculus newtons-method


    【解决方案1】:

    欢迎使用 Perl。

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

    1. 在每个 Perl 脚本中始终包含 use strict;use warnings;
    2. 始终chomp您从 STDIN 输入作为您的输入:

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

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

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

    如图所示:

    #!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;
    }
    

    【讨论】:

    • 谢谢。很多。这比我以前做的更有意义。要求用户输入两次真的很烦人,我只是一遍又一遍地测试它而感到恼火。您的建议非常有帮助,当我尝试了解更多信息时,它们会帮助我。正如您可能知道的那样,我的知识和经验很少。我才意识到还有一个问题。我必须使用它的问题之一涉及三角函数。有没有一种简单的方法可以解决这个问题?
    【解决方案2】:

    我无法确定您对代码的意图。主要问题似乎是你的头脑中并不清楚你的每个子例程做什么,因为fxfprimex 要求数据以及评估函数(除了对这些术语求和,奇怪的是, 在子程序之外完成)。这根本不是您想要的,因为指数和系数在整个程序中保持不变,必须多次评估这些函数,而且您真的不想每次都再次询问这些值。

    这里有一些让 Perl 代码正常工作的指针

    • 以小块的形式编写程序——一次一两行——并在每次添加后检查程序是否编译和运行并产生预期的结果。在尝试运行之前编写整个程序是灾难的根源

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

    • chomp 变量,一旦从文件或终端读取。从源头修剪输入字符串的一个有用的习惯用法是

      chomp(my $input = <>)
      

      这将确保数据中的任何地方都没有杂散的换行符

    这至少应该让你开始。

    我对展示这一点有两种看法。希望对您有所帮助,但我真的不想将您拖入您不熟悉的 Perl 部分。

    这是一个使用 Newton–Raphson 方法求多项式根的程序。我暂时跳过了终端输入,并对数据进行了硬编码。就目前而言,它通过找到 x2 - 3000 的正根来找到 3,000 的平方根。

    注意@f@f_prime 持有函数的系数 及其导数向后 从通常的顺序,所以@f(-3000, 0, 1)。该程序还计算导数函数的系数,因为这是一件简单的事情,而且比向用户询问另一组值要好得多。

    只有一个子程序polynomial,它接受一个x的值和一个系数列表。这用于计算给定 x 值的函数及其导数的值。

    算法步骤在行

    my $new_x = $x - polynomial($x, @f) / polynomial($x, @f_prime);
    

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

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

    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;
    }
    

    输出

    0.5
    3000.25
    1500.62495833681
    751.312062703027
    377.652538627869
    192.798174296885
    104.179243809523
    66.4878834504349
    55.8044433107163
    54.7818016853582
    54.7722565822241
    54.7722557505166
    

    【讨论】:

    • 这也很棒。我不确定我能做的唯一一件事就是让用户轻松输入,而不需要大量信息。有一次我迷失了方向,以至于我只是在制作子程序,如果我有另一个我认为可以提供帮助的想法,我可以调用它。
    • @Lucas:处理用户输入应该相当简单。以我的代码为例,只需编写一些允许手动输入@f 中的值的代码,而不是分配文字列表。您可能希望也可能不希望保留区分系数的线。在那之后,IMO 最好仅在您的算法中有很大一部分可以被分割为do this,或者如果有多次出现可以参数化的类似进程时,才编写子例程。我想这需要经验以及风格的选择,才能轻松做到这一点。
    • @Lucas:也许你应该在没有子程序的情况下编写整个东西?这将是一个完全可以接受的解决方案,如果您厌倦了重复相同的代码,我相信您会注意到!不要相信你的导师everything 应该在一个子程序中,或者你应该对你的代码进行大量注释。一个好的程序应该通过正确选择标识符和明智地使用将功能分成块的空白来表达其含义。只有偶尔您才需要添加最少的 cmets 来解释为什么需要某行代码。
    • 所以在你的代码中,如果我询问用户输入并使用push 将这些变量推送到@f 我理论上可以使用用户给定的输入吗?我刚刚尝试过,但没有成功,我想我可以将其归因于我缺乏知识。
    • 你知道Data::Dumper怎么用吗?在帮助您修复代码方面,它比我说的任何话都更有价值。 Data::Dump 再好不过了,但它不是核心模块,您可能需要安装它。
    猜你喜欢
    • 1970-01-01
    • 2013-10-24
    • 2019-06-19
    • 2018-07-30
    • 2013-10-17
    • 2017-05-05
    • 1970-01-01
    • 2018-02-26
    相关资源
    最近更新 更多