Added general Neville's Algorithm

This commit is contained in:
Stephen McQuay 2014-01-12 23:05:25 -08:00
parent b761ad7ddc
commit 282bcb448f
2 changed files with 123 additions and 7 deletions

View File

@ -687,6 +687,22 @@ func TestNeville(t *testing.T) {
t float64 t float64
expected Point2d expected Point2d
}{ }{
{
points: []Point2d{
{0, 0},
{3, 3},
{5, 3},
{7, 0},
},
ts: []float64{
0,
1,
3,
4,
},
t: 2.0,
expected: Point2d{25.0 / 6.0, 4.0},
},
{ {
points: []Point2d{ points: []Point2d{
{0, 0}, {0, 0},
@ -694,13 +710,97 @@ func TestNeville(t *testing.T) {
{2, 2}, {2, 2},
}, },
ts: []float64{ ts: []float64{
0.0, 0,
1.0 / 3.0, 1.0 / 3.0,
2.0 / 3.0, 2.0 / 3.0,
}, },
t: 1.0, t: 1.0,
expected: Point2d{3, 3}, expected: Point2d{3, 3},
}, },
{
points: []Point2d{
{0, 0},
{1, 1},
{2, 1},
},
ts: []float64{
0,
1.0 / 3.0,
2.0 / 3.0,
},
t: 1.0,
expected: Point2d{3, 0},
},
{
ts: []float64{
0,
1.0 / 3.0,
2.0 / 3.0,
},
t: 1.0,
points: []Point2d{
{0, 0},
{1, 0},
{2, 0},
},
expected: Point2d{3, 0},
},
{
points: []Point2d{
{0, 0},
{0, 1},
{0, 2},
},
ts: []float64{
0,
1.0 / 3.0,
2.0 / 3.0,
},
t: 1.0,
expected: Point2d{0, 3},
},
{
ts: []float64{
0,
1.0 / 3.0,
2.0 / 3.0,
},
t: 1.0,
points: []Point2d{
{0, 0},
{0, 0},
{0, 0},
},
expected: Point2d{0, 0},
},
{
points: []Point2d{
{42, 42},
{42, 42},
{42, 42},
},
expected: Point2d{42, 42},
ts: []float64{
0,
1.0 / 3.0,
2.0 / 3.0,
},
t: 1.0,
},
{
points: []Point2d{
{104, 288},
{110, 270},
{120, 250},
},
expected: Point2d{134, 228},
ts: []float64{
0,
1.0 / 3.0,
2.0 / 3.0,
},
t: 1.0,
},
} }
for _, test := range tests { for _, test := range tests {
p, err := Neville(test.points, test.ts, test.t) p, err := Neville(test.points, test.ts, test.t)
@ -708,8 +808,13 @@ func TestNeville(t *testing.T) {
t.Errorf("yeah, this should've been nil: %s", err) t.Errorf("yeah, this should've been nil: %s", err)
} }
if p == nil { if p == nil {
} else if *p != test.expected { t.Errorf("nil point?")
t.Errorf("wrong: expected: %+v, actual: %+v", test.expected, p) } else {
deltaX := math.Abs(float64(p.X - test.expected.X))
deltaY := math.Abs(float64(p.Y - test.expected.Y))
if deltaX > 1e-4 || deltaY > 1e-4 { // HOLY SHIT!!!
t.Errorf("wrong: expected: %+v, actual: %+v", test.expected, p)
}
} }
} }
} }

View File

@ -6,9 +6,7 @@ import (
) )
// explicitly implements Neville's algorithm for three points at very // explicitly implements Neville's algorithm for three points at very
// particular parameter values. // particular parameter values: ts[]{0, 1/3, 2/3}, extrapolate to 1.
// TODO: update this to take an arbitrary parameter value, and arbitrary slice
// of Point2d
func Extrapolate(P []Point2d) (*Point2d, error) { func Extrapolate(P []Point2d) (*Point2d, error) {
if len(P) != 3 { if len(P) != 3 {
return nil, errors.New("only works for len(P) == 3") return nil, errors.New("only works for len(P) == 3")
@ -23,6 +21,8 @@ func Extrapolate(P []Point2d) (*Point2d, error) {
return &p012, nil return &p012, nil
} }
// Implements Neville's Algorithm for a slice of points (P) at parameter
// values(ts), to parameter value (t). Returns the point and an error
func Neville(P []Point2d, ts []float64, t float64) (*Point2d, error) { func Neville(P []Point2d, ts []float64, t float64) (*Point2d, error) {
if len(P) != len(ts) { if len(P) != len(ts) {
return nil, errors.New( return nil, errors.New(
@ -33,5 +33,16 @@ func Neville(P []Point2d, ts []float64, t float64) (*Point2d, error) {
), ),
) )
} }
return nil, errors.New("smb") Q := []Point2d(P)
order := len(P) - 1
for i := 0; i < order; i++ {
for j := 0; j < order-i; j++ {
var tLeft float32 = float32(ts[i+j+1] - t)
var tRight float32 = float32(t - ts[j])
Q[j] = Q[j].ToVector().Scale(tLeft).ToPoint().Add(P[j+1].ToVector().Scale(tRight))
Q[j] = Q[j].ToVector().Scale(float32(1.0 / (ts[i+j+1] - ts[j]))).ToPoint()
}
}
return &Q[0], nil
} }