使用Golang实现Douglas Peuker算法

请以中文将下述内容改述,只需要提供一个选项:

内容

    • Douglas Peuker アルゴリズムでの曲線間引きがどのくらい時間がかかるのか確認したかった

 

    • ので、簡単に golang で書いてみた

 

    • こちらを参考にさせていただいた http://www.trail-note.net/tech/thinout/

だいたいの目安を見るための書捨てコードなのでツッコミどころが多いのはご愛嬌

基准测试

$ go test -bench .
goos: darwin
goarch: amd64
pkg: xxxxxx
Benchmark10-4             200000           7125 ns/op  ->  7.1 us
Benchmark100-4              1000        1234958 ns/op  ->  1.2 ms
Benchmark1000-4               10      129394222 ns/op  ->  0.1 ms
Benchmark10000-4               1     2101666871 ns/op  ->  2.1  s
Benchmark100000-4              1    12936923196 ns/op  -> 12.9  s
PASS
ok      xxxxxx  25.437s
    • 1万点 → 1000点 で 秒のオーダー

 

    動的に計算するのは結構キツイですね

源代码

package dp_test

import (
    "math"
    "math/rand"
    "testing"
)

type Setting struct {
    i1, i2 int
}

type Point struct {
    x       float64
    y       float64
    setting *Setting
}

type Max struct {
    dist   float64
    i      int
    i1, i2 int
}

func Benchmark10(b *testing.B) {
    bench(b, 10)
}

func Benchmark100(b *testing.B) {
    bench(b, 100)
}

func Benchmark1000(b *testing.B) {
    bench(b, 1000)
}

func Benchmark10000(b *testing.B) {
    bench(b, 10000)
}

func Benchmark100000(b *testing.B) {
    bench(b, 100000)
}

func bench(b *testing.B, num int) {
    limit := 1000

    for j := 0; j < b.N; j++ {

        // 初期化
        points := make([]Point, num)
        for i := range points {
            points[i].x = rand.Float64()
            points[i].y = rand.Float64()
        }

        if len(points) < 2 {
            return
        }

        for i := range points {
            points[i].setting = &Setting{
                i1: 0,
                i2: len(points) - 1,
            }
        }
        points[0].setting = nil
        points[len(points)-1].setting = nil

        if len(points) < limit {
            limit = len(points)
        }

        b.StartTimer()
        for num := 2; num < limit; num++ {
            var max Max
            for i, p := range points {
                if p.setting == nil {
                    continue
                }
                x, y := points[i].x, points[i].y

                s := p.setting
                x1, x2 := points[s.i1].x, points[s.i2].x
                y1, y2 := points[s.i1].y, points[s.i2].y

                numerator := math.Abs((y2-y1)*x - (x2-x1)*y + y1*x2 - y2*x1)
                denominator := math.Sqrt(math.Pow((y2-y1), 2) + math.Pow((x2-x1), 2))
                dist := numerator / denominator

                if max.dist < dist {
                    max = Max{
                        dist: dist,
                        i:    i,
                        i1:   s.i1,
                        i2:   s.i2,
                    }
                }
            }
            points[max.i].setting = nil

            for i := max.i1 + 1; i < max.i; i++ {
                points[i].setting.i2 = max.i
            }

            for i := max.i + 1; i < max.i2; i++ {
                points[i].setting.i1 = max.i
            }
        }
        b.StopTimer()
    }
}

给我一个附赠品

稍微优化后,分数从10万点降低到了1000点,时间大约为150毫秒。

$ go test -bench .
goos: darwin
goarch: amd64
pkg: xxxxxx
Benchmark10-4             500000          3341 ns/op ->   3us
Benchmark100-4             50000         35322 ns/op ->  35us
Benchmark1000-4             2000       1003985 ns/op ->   1ms
Benchmark10000-4             100      13531037 ns/op ->  13ms
Benchmark100000-4             10     152950556 ns/op -> 152ms
Benchmark1000000-4             1    2231156196 ns/op ->   2s
PASS
ok      xxxxxx  26.530s
package dp_test

import (
    "math"
    "math/rand"
    "testing"
)

type Setting struct {
    i1, i2 int
}

type Point struct {
    x        float64
    y        float64
    setting  *Setting
    distance float64
}

type Max struct {
    dist   float64
    i      int
    i1, i2 int
}

func Benchmark10(b *testing.B) {
    bench(b, 10)
}

func Benchmark100(b *testing.B) {
    bench(b, 100)
}

func Benchmark1000(b *testing.B) {
    bench(b, 1000)
}

func Benchmark10000(b *testing.B) {
    bench(b, 10000)
}

func Benchmark100000(b *testing.B) {
    bench(b, 100000)
}

func Benchmark1000000(b *testing.B) {
    bench(b, 1000000)
}

func bench(b *testing.B, num int) {
    limit := 1000

    for j := 0; j < b.N; j++ {

        // 初期化
        points := make([]Point, num)
        for i := range points {
            points[i].x = rand.Float64()
            points[i].y = rand.Float64()
            points[i].distance = math.NaN()
        }

        if len(points) < 2 {
            return
        }

        for i := range points {
            points[i].setting = &Setting{
                i1: 0,
                i2: len(points) - 1,
            }
        }
        points[0].setting = nil
        points[len(points)-1].setting = nil

        if len(points) < limit {
            limit = len(points)
        }

        b.StartTimer()
        for num := 2; num < limit; num++ {
            var max Max
            for i, p := range points {
                if !math.IsNaN(p.distance) {
                    continue
                }

                if p.setting == nil {
                    continue
                }

                s := p.setting
                x, y := points[i].x, points[i].y
                x1, x2 := points[s.i1].x, points[s.i2].x
                y1, y2 := points[s.i1].y, points[s.i2].y

                numerator := math.Abs((y2-y1)*x - (x2-x1)*y + y1*x2 - y2*x1)
                denominator := math.Sqrt(math.Pow((y2-y1), 2) + math.Pow((x2-x1), 2))
                dist := numerator / denominator
                points[i].distance = dist

                if max.dist < dist {
                    max = Max{
                        dist: dist,
                        i:    i,
                        i1:   s.i1,
                        i2:   s.i2,
                    }
                }
            }
            points[max.i].setting = nil

            for i := max.i1 + 1; i < max.i; i++ {
                points[i].setting.i2 = max.i
                points[i].distance = math.NaN()
            }

            for i := max.i + 1; i < max.i2; i++ {
                points[i].setting.i1 = max.i
                points[i].distance = math.NaN()
            }
        }
        b.StopTimer()
    }
}
广告
将在 10 秒后关闭
bannerAds