我需要创建一个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.
你是否建议我使用不同的功能,或者如果我有更多的输入点,可以减少插值中的现有误差?我必须诚实地说我是一个贫穷的数学家,所以在代码中任何更准确的方法或示例的真实例子都将非常感激。
由于
答案 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)
看起来拉格朗日插值方法的当前算法提供了正确的结果。为了纠正计算中的错误,可以提供更多的基点。另外,为了将数学函数中的未知变量相乘,在一个答案中留下了一个函数示例。
谢谢大家。