从PHP

时间:2017-09-09 09:26:21

标签: php math interpolation

我需要创建一个PHP脚本,从这组点生成插值函数。

我决定使用拉格朗日插值,因为我最容易找到从输入点列表生成函数的示例。其他方法的问题是我找不到生成函数的示例 - >所有其他插值的所有其他示例仅生成附加点,而不是现有点之外的函数。

我用来查找拉格朗日插值示例的来源是:http://www2.lawrence.edu/fast/GREGGJ/Math420/Section_3_1.pdf

我决定在我的PHP代码中复制这个例子。

/**
 * Generate one basis polynomial function
 * @param type $points array of points
 * @param type $basisPolynomial Each basis polynomial will be stored in the array of values so that it can be appended to the final function
 * @param type $allXValues all x values for the point
 * @param type $i current index of the basis polynomial
 */
function generateBasisPolynomial(&$basisPolynomial, $allXValues, $i) {
    $basisPolynomial[$i] = "(";
    $divisor = "(";
    for ($j = 0; $j < count($allXValues); $j++) {
        if ($j == $i) {
            continue;
        }
        $basisPolynomial[$i] .= "(x-$allXValues[$j])*";
        $divisor .="($allXValues[$i]-$allXValues[$j])*";
    }
    //multiply the divisor by 1, because the previous loop has * at the end of the equation
    $divisor .="1)";
    $basisPolynomial[$i] .="1)/$divisor";
}

/**
 * Function that generates the Lagrange interpolation from the list of points
 * @param type $points
 * @return string
 */
function generateLagrangeInterpolation($points) {
    $numberOfPoints = count($points);
    if ($numberOfPoints < 2) {
        return "NaN";
    } else {
        //source http://www2.lawrence.edu/fast/GREGGJ/Math420/Section_3_1.pdf
        //for each point, construct the basis polynomial
        //for a sequence of x values, we will have n basis polynomials,
        //Example:
        //if we, for example have a sequence of four points, with their sequence of x values being {x0,x1,x2,x3}
        //then we construct the basis polynomial for x0 by doing the following calculation:
        //F(x) = ((x-x1)*(x-x2)*(x-x3))/((x0-x1)*(x0-x2)*(x0-x3)) -> where x is an unknown variable.
        $basisPolynomial = array();
        //get all x values from the array of points so that we can access them by index
        $allXValues = array_keys($points);
        $allYValues = array_values($points);
        //Because the Y values are percentages, we want to divide them by 100.
        $allYValues = array_map(function($val) {
            return $val / 100;
        }, $allYValues);

        $returnFunction = "";
        for ($i = 0; $i < $numberOfPoints; $i++) {
            generateBasisPolynomial($basisPolynomial, $allXValues, $i);
            //multiply this basis polynomial by y value
            $returnFunction .="$allYValues[$i]*$basisPolynomial[$i]+";
        }
        //Append 0 to the end of the function because the above loop returns a function with a +
        //at the end so we want to make it right
        $returnFunction .="0";
        echo $returnFunction;
    }
}

//$points = array("4.1168" => "0.213631", "4.19236" => "0.214232", "4.20967" => "0.21441", "4.46908" => "0.218788");
$points = array("0.1" => "5", "0.3" => "10", "0.5" => "30", "0.6" => "60", "0.8" => "70");
generateLagrangeInterpolation($points);

我得到的结果是以下功能:

0.05*((x-0.3)*(x-0.5)*(x-0.6)*(x-0.8)*1)/((0.1-0.3)*(0.1-0.5)*(0.1-0.6)*(0.1-0.8)*1)+0.1*((x-0.1)*(x-0.5)*(x-0.6)*(x-0.8)*1)/((0.3-0.1)*(0.3-0.5)*(0.3-0.6)*(0.3-0.8)*1)+0.3*((x-0.1)*(x-0.3)*(x-0.6)*(x-0.8)*1)/((0.5-0.1)*(0.5-0.3)*(0.5-0.6)*(0.5-0.8)*1)+0.6*((x-0.1)*(x-0.3)*(x-0.5)*(x-0.8)*1)/((0.6-0.1)*(0.6-0.3)*(0.6-0.5)*(0.6-0.8)*1)+0.7*((x-0.1)*(x-0.3)*(x-0.5)*(x-0.6)*1)/((0.8-0.1)*(0.8-0.3)*(0.8-0.5)*(0.8-0.6)*1)+0

我并不关心表达式是否被完全简化和计算(但是如果你有任何建议或代码可以为我做这件事,那将是一个巨大的优势。)

如果我查看简化表达式,它看起来像这样:

(47500*x^4-79300*x^3+42245*x^2-8699*x+480)/(-840) 

但是,如果我尝试将该功能粘贴到http://fooplot.com - &gt;我得到图表正在通过定义为输入参数的点,但是,我不确定其他点的图是否正确,因为看起来它的Y值在进入负值时变为负值X <= 0或x> = 1.

你是否建议我使用不同的功能,或者如果我有更多的输入点,可以减少插值中的现有误差?我必须诚实地说我是一个贫穷的数学家,所以在代码中任何更准确的方法或示例的真实例子都将非常感激。

由于

3 个答案:

答案 0 :(得分:1)

以下是您可以尝试的内容:

 function basisPolynomial($points, $j, $x) {
        $xj = $points[$j][0]; //Assume a point is an array of 2 numbers
        $partialProduct = 1;
         //Product loop
        for ($m = 0;$i < count($points);$m++) { 
             if ($m === $j) { continue; }                 
             $partialProduct *= ($x - $points[$m][0])/($xj-$points[$m][0]);
        }
        return $partialProduct;
 }

 function lagrangePolynomial($points,$x) {
        $partialSum = 0;
        for ($j = 0;$j < count($points);$j++) {
            $partialSum += $points[$j][1]*basisPolynomial($points,$j,$x);
        }
        return $partialSum;
 }

现在,如果你需要绘制它,你可以生成一个可以在绘图函数中使用的点列表,例如。

$points = <my points>; 
$plotPoints = [];
for ($i = 0;$i < 10;$i+= 0.1) { //for example 
    $plotPoints[] = [ $i, lagrangePolynomial($points,$i) ];
}

如果你只想使用直接绘图,你需要使用像gnuplot这样的绘图工具来定义函数并让它确定如何绘制它们。

更新:http://www.physics.brocku.ca/Courses/5P10/lectures/lecture_10_handout.pdf似乎有一个gnuplot示例,正是你所需要的,但感觉就像作弊一样

答案 1 :(得分:0)

我对php的面向对象实现不是很熟悉,但在java中我做了这个小宝贝;)

import java.util.*;
import java.util.Arrays;
import java.util.List;

public class Run{
  public static void main(String[] args){
    int[] parameters = new int[]{-1, 2, 4, 3};
    Binom b = new Binom(1, 1, parameters[1], 0);
    Polinom p = new Polinom(b);
    for(int i = 2; i < parameters.length; i++)
      p.joinBinom(new Binom(1, 1, -1 * parameters[i], 0));
    System.out.println(p.toString() + " / (" + getDenominator(parameters) + ")");
  }

  public static int getDenominator(int[] params){
    int result = 1;
    for(int i = 1; i < params.length; i++)
      result *= params[0] - params[i];
    return result;
  }
}

class Monomial{
  private int constant = 1;
  private int pow = 0;

  public int getConstant(){
    return this.constant;
  }

  public void sumConstant(int value){
    this.constant += value;
  }

  public boolean hasVariable(){
    return this.pow > 0;
  }

  public int getPow(){
    return this.pow;
  }

  public Monomial(int constant, int pow){
    this.constant = constant;
    this.pow = pow;
  }

  public ArrayList<Monomial> multiply(Binom a){
    Monomial first = new Monomial(this.constant * a.getFirst().getConstant(), this.pow + a.getFirst().getPow());
    Monomial second = new Monomial(this.constant * a.getSecond().getConstant(), this.pow + a.getSecond().getPow());
    System.out.print("\t" + this.toString() + "\t* (" + a.getFirst().toString() + " " + a.getSecond().toString() + ")");
    System.out.print("\t= " + first.toString() + "\t");
    System.out.println(second.toString());
    return (new Binom(first, second)).toList();
  }

  public String toString(){
    String result = "";
    if(this.constant == 1){
      if(!this.hasVariable())
        result += this.constant;
    }
    else
      result += this.constant;
    if(this.hasVariable()){
      result += "X";
      if(this.pow > 1)
        result += "^" + this.pow;
    }
    return result;
  }
}

class Binom{
  private Monomial first;
  private Monomial second;

  public Monomial getFirst(){
    return this.first;
  }

  public Monomial getSecond(){
    return this.second;
  }

  public Binom(int constant1, int pow1, int constant2, int pow2){
    this.first = new Monomial(constant1, pow1);
    this.second = new Monomial(constant2, pow2);
  }

  public Binom(Monomial a, Monomial b){
    this.first = a;
    this.second = b;
  }

  public ArrayList<Monomial> toList(){
    ArrayList<Monomial> result = new ArrayList<>();
    result.add(this.first);
    result.add(this.second);
    return result;
  }
}

class Polinom{
  private ArrayList<Monomial> terms = new ArrayList<>();

  public Polinom(Binom b){
    this.terms.add(b.getFirst());
    this.terms.add(b.getSecond());
  }

  private void compact(){
    for(int i = 0; i < this.terms.size(); i++){
      Monomial term = this.terms.get(i);
      for(int j = i + 1; j < this.terms.size(); j++){
        Monomial test = this.terms.get(j);
        if(term.getPow() == test.getPow()){
          term.sumConstant(test.getConstant());
          this.terms.remove(test);
          j--;
        }
      }
    }
  }

  public void joinBinom(Binom b){
    ArrayList<Monomial> result = new ArrayList<>();
    for(Monomial t : this.terms){
      result.addAll(t.multiply(b));
    }
    this.terms = result;
    this.compact();
  }

  public String toString(){
    String result = "";
    for(Monomial t : this.terms)
      result += (t.getConstant() < 0 ? " " : " +") + t.toString();
    return "(" + result + ")";
 }

}

返回:

X   * (X -4)    = X^2   -4X
2   * (X -4)    = 2X    -8
X^2 * (X -3)    = X^3   -3X^2
-2X * (X -3)    = -2X^2 6X
-8  * (X -3)    = -8X   24
( +X^3 -5X^2 -2X +24) / (-60)

答案 2 :(得分:0)

看起来拉格朗日插值方法的当前算法提供了正确的结果。为了纠正计算中的错误,可以提供更多的基点。另外,为了将数学函数中的未知变量相乘,在一个答案中留下了一个函数示例。

谢谢大家。