【算法】道格拉斯

news2025/2/28 17:06:59

一、引言

        道格拉斯算法是一种用于曲线拟合的数学方法,特别是在处理曲线插值问题时非常有用。道格拉斯-普克算法(Douglas-Peucker Algorithm),简称D-P算法,是一种用于简化多边形或折线的高效算法,由David Douglas和Thomas Peucker于1973年提出。该算法的核心思想是递归地将折线分割为两段,然后根据设定的阈值去除那些偏离直线距离小于阈值的点,从而达到简化折线的目的。

二、算法原理

        道格拉斯算法是一种基于节点和链表的曲线拟合方法。它通过将节点组织成链表,并使用插值条件来确定曲线。算法的基本思想是:

  1. 构建一个包含n个节点的链表,每个节点包含一个点(x, y)。
  2. 将链表分为两部分,第一部分包含链表的第一个节点,第二部分包含链表的其余节点。
  3. 对于链表的每一部分,使用插值条件计算出对应的插值曲线。
  4. 将两部分插值曲线合并,得到最终的插值曲线。

三、数据结构

道格拉斯算法主要涉及以下数据结构:

  • 点(Point):表示折线上的一个点,通常包含 x 和 y 坐标。
  • 线段(Line Segment):由两个点组成的线段。
  • 列表(List):用于存储折线上的所有点。
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

四、使用场景

道格拉斯算法适用于以下场景:

  • 曲线插值:用于在给定的点集上构建一条曲线。
  • 图形设计:在图形设计中,用于生成平滑的曲线和路径。
  • 数据可视化:用于在图表上绘制平滑的曲线,以更好地展示数据。
  • 地理信息系统(GIS):简化地理边界和路径,以提高可视化效果和处理速度。
  • 图形处理:在计算机图形学中,简化复杂的曲线以减少渲染负担。
  • 数据压缩:在数据传输和存储中,减少需要传输的点的数量。
  • 路径规划:在机器人导航和自动驾驶中,简化路径以提高计算效率。

五、算法实现

        道格拉斯-普克算法是一种用于简化折线(或曲线)的算法。其基本思想是通过减少点的数量来简化路径,同时尽可能保留原始形状。该算法的核心是通过递归地检测并去除不重要的点来实现简化。算法的步骤如下:

  1. 选择起始点和终止点:从折线的第一个点到最后一个点。
  2. 计算最远点:在起始点和终止点之间,找到距离这条线段最远的点。
  3. 判断最远点的距离:如果最远点到线段的距离大于设定的阈值,则将该点保留,并对起始点和最远点之间的部分,以及最远点和终止点之间的部分递归应用该算法。
  4. 终止条件:如果最远点到线段的距离小于等于阈值,则可以去掉所有的中间点。

以下是道格拉斯算法的伪代码实现:

function douglas(nodes):
    if nodes.length == 0:
        return []

    curve = []
    for i from 1 to nodes.length - 1:
        left_curve = douglas(nodes[0:i])
        right_curve = douglas(nodes[i:])

        # 计算插值条件
        interpolation_condition = calculate_interpolation_condition(left_curve, right_curve)

        # 合并插值曲线
        curve = merge_curves(curve, interpolation_condition)

    return curve

六、其他同类算法对比

  • 拉格朗日插值:通过在给定点上构建多项式,来实现曲线拟合。
  • 牛顿插值:通过构建差分表,来实现曲线拟合。
  • Ramer-Douglas-Peucker算法:道格拉斯-普克算法的变种,主要用于更高效的简化。
  • Visvalingam-Whyatt算法:基于面积的简化方法,通过删除对整体形状影响最小的点来简化曲线。
  • Bézier曲线简化:适用于平滑曲线,通常在图形设计和计算机动画中使用。

七、多语言实现

道格拉斯算法的简化版实现:

Java

import java.util.ArrayList;
import java.util.List;

class Point {
    double x, y;

    Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
}

public class DouglasPeucker {
    public static double perpendicularDistance(Point point, Point start, Point end) {
        if ((start.x == end.x) && (start.y == end.y)) {
            return Math.sqrt(Math.pow(point.x - start.x, 2) + Math.pow(point.y - start.y, 2));
        }

        double num = Math.abs((end.y - start.y) * point.x - (end.x - start.x) * point.y + end.x * start.y - end.y * start.x);
        double denom = Math.sqrt(Math.pow(end.y - start.y, 2) + Math.pow(end.x - start.x, 2));
        return num / denom;
    }

    public static List<Point> douglasPeucker(List<Point> points, double epsilon) {
        if (points.size() < 2) {
            return points;
        }

        Point start = points.get(0);
        Point end = points.get(points.size() - 1);

        double maxDistance = 0.0;
        int index = 0;

        for (int i = 1; i < points.size() - 1; i++) {
            double distance = perpendicularDistance(points.get(i), start, end);
            if (distance > maxDistance) {
                index = i;
                maxDistance = distance;
            }
        }

        List<Point> result;
        if (maxDistance > epsilon) {
            List<Point> left = douglasPeucker(points.subList(0, index + 1), epsilon);
            List<Point> right = douglasPeucker(points.subList(index, points.size()), epsilon);
            result = new ArrayList<>(left);
            result.remove(result.size() - 1); // Remove the last point of left
            result.addAll(right);
        } else {
            result = new ArrayList<>();
            result.add(start);
            result.add(end);
        }

        return result;
    }

    public static void main(String[] args) {
        List<Point> points = new ArrayList<>();
        points.add(new Point(0, 0));
        points.add(new Point(1, 1));
        points.add(new Point(2, 0));
        points.add(new Point(3, 1));
        points.add(new Point(4, 0));

        double epsilon = 0.5;
        List<Point> simplifiedPoints = douglasPeucker(points, epsilon);
    }
}

Python

import math

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

def perpendicular_distance(point, start, end):
    if (start.x == end.x) and (start.y == end.y):
        return math.sqrt((point.x - start.x) ** 2 + (point.y - start.y) ** 2)

    # Calculate the distance
    num = abs((end.y - start.y) * point.x - (end.x - start.x) * point.y + end.x * start.y - end.y * start.x)
    denom = math.sqrt((end.y - start.y) ** 2 + (end.x - start.x) ** 2)
    return num / denom

def douglas_peucker(points, epsilon):
    # If the line is too short, return the endpoints
    if len(points) < 2:
        return points

    start = points[0]
    end = points[-1]

    # Find the point with the maximum distance from the line segment
    max_distance = 0.0
    index = 0
    for i in range(1, len(points) - 1):
        distance = perpendicular_distance(points[i], start, end)
        if distance > max_distance:
            index = i
            max_distance = distance

    # If max distance is greater than epsilon, recursively simplify
    if max_distance > epsilon:
        left = douglas_peucker(points[:index + 1], epsilon)
        right = douglas_peucker(points[index:], epsilon)
        return left[:-1] + right
    else:
        return [start, end]

# Example usage
points = [Point(0, 0), Point(1, 1), Point(2, 0), Point(3, 1), Point(4, 0)]
epsilon = 0.5
simplified_points = douglas_peucker(points, epsilon)

C++ 

#include <iostream>
#include <vector>
#include <cmath>

struct Point {
    double x, y;
    Point(double x, double y) : x(x), y(y) {}
};

double perpendicularDistance(Point point, Point start, Point end) {
    if (start.x == end.x && start.y == end.y) {
        return sqrt(pow(point.x - start.x, 2) + pow(point.y - start.y, 2));
    }

    double num = fabs((end.y - start.y) * point.x - (end.x - start.x) * point.y + end.x * start.y - end.y * start.x);
    double denom = sqrt(pow(end.y - start.y, 2) + pow(end.x - start.x, 2));
    return num / denom;
}

std::vector<Point> douglasPeucker(std::vector<Point> points, double epsilon) {
    if (points.size() < 2) return points;

    Point start = points.front();
    Point end = points.back();

    double maxDistance = 0.0;
    int index = 0;

    for (int i = 1; i < points.size() - 1; i++) {
        double distance = perpendicularDistance(points[i], start, end);
        if (distance > maxDistance) {
            index = i;
            maxDistance = distance;
        }
    }

    std::vector<Point> result;
    if (maxDistance > epsilon) {
        std::vector<Point> left = douglasPeucker(std::vector<Point>(points.begin(), points.begin() + index + 1), epsilon);
        std::vector<Point> right = douglasPeucker(std::vector<Point>(points.begin() + index, points.end()), epsilon);
        result.insert(result.end(), left.begin(), left.end() - 1); // Remove last point of left
        result.insert(result.end(), right.begin(), right.end());
    } else {
        result.push_back(start);
        result.push_back(end);
    }

    return result;
}

int main() {
    std::vector<Point> points = {Point(0, 0), Point(1, 1), Point(2, 0), Point(3, 1), Point(4, 0)};
    double epsilon = 0.5;
    std::vector<Point> simplifiedPoints = douglasPeucker(points, epsilon);
    return 0;
}

Go

package main

import (
	"fmt"
	"math"
)

type Point struct {
	x, y float64
}

func perpendicularDistance(point, start, end Point) float64 {
	if start.x == end.x && start.y == end.y {
		return math.Sqrt(math.Pow(point.x-start.x, 2) + math.Pow(point.y-start.y, 2))
	}

	num := math.Abs((end.y-start.y)*point.x - (end.x-start.x)*point.y + end.x*start.y - end.y*start.x)
	denom := math.Sqrt(math.Pow(end.y-start.y, 2) + math.Pow(end.x-start.x, 2))
	return num / denom
}

func douglasPeucker(points []Point, epsilon float64) []Point {
	if len(points) < 2 {
		return points
	}

	start := points[0]
	end := points[len(points)-1]

	maxDistance := 0.0
	index := 0

	for i := 1; i < len(points)-1; i++ {
		distance := perpendicularDistance(points[i], start, end)
		if distance > maxDistance {
			index = i
			maxDistance = distance
		}
	}

	var result []Point
	if maxDistance > epsilon {
		left := douglasPeucker(points[:index+1], epsilon)
		right := douglasPeucker(points[index:], epsilon)
		result = append(result, left[:len(left)-1]...) // Remove last point of left
		result = append(result, right...)
	} else {
		result = append(result, start, end)
	}

	return result
}

func main() {
	points := []Point{{0, 0}, {1, 1}, {2, 0}, {3, 1}, {4, 0}}
	epsilon := 0.5
	simplifiedPoints := douglasPeucker(points, epsilon)
	fmt.Println(simplifiedPoints)
}

八、实际服务应用场景代码框架

应用场景:地图路径简化服务

一个简单的服务框架,使用Python Flask实现地图路径简化的API。

from flask import Flask, request, jsonify
import math

app = Flask(__name__)

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

def perpendicular_distance(point, start, end):
    if (start.x == end.x) and (start.y == end.y):
        return math.sqrt((point.x - start.x) ** 2 + (point.y - start.y) ** 2)

    num = abs((end.y - start.y) * point.x - (end.x - start.x) * point.y + end.x * start.y - end.y * start.x)
    denom = math.sqrt((end.y - start.y) ** 2 + (end.x - start.x) ** 2)
    return num / denom

def douglas_peucker(points, epsilon):
    if len(points) < 2:
        return points

    start = points[0]
    end = points[-1]

    max_distance = 0.0
    index = 0
    for i in range(1, len(points) - 1):
        distance = perpendicular_distance(points[i], start, end)
        if distance > max_distance:
            index = i
            max_distance = distance

    if max_distance > epsilon:
        left = douglas_peucker(points[:index + 1], epsilon)
        right = douglas_peucker(points[index:], epsilon)
        return left[:-1] + right
    else:
        return [start, end]

@app.route('/simplify', methods=['POST'])
def simplify_path():
    data = request.json
    points = [Point(p['x'], p['y']) for p in data['points']]
    epsilon = data['epsilon']
    simplified_points = douglas_peucker(points, epsilon)
    result = [{'x': p.x, 'y': p.y} for p in simplified_points]
    return jsonify(result)

if __name__ == '__main__':
    app.run(debug=True)

使用说明

  1. 启动Flask应用。
  2. 发送POST请求到/simplify,请求体包含要简化的点和阈值,例如:
{
    "points": [{"x": 0, "y": 0}, {"x": 1, "y": 1}, {"x": 2, "y": 0}, {"x": 3, "y": 1}, {"x": 4, "y": 0}],
    "epsilon": 0.5
}

返回简化后的点。

        道格拉斯-佩克算法(Douglas-Peucker algorithm)是一种用于简化多边形或折线的算法。虽然 SQL 本身并不直接支持复杂的几何运算,但可以使用一些扩展库(如 PostGIS)来处理地理数据。

以下是一个使用 PostGIS 的示例,展示如何在 SQL 中实现道格拉斯-佩克算法来简化几何图形。

安装 PostGIS

确保您的 PostgreSQL 数据库中已安装 PostGIS 扩展:

CREATE EXTENSION postgis;

创建表和插入数据

创建一个表来存储几何数据,并插入一些示例数据:

CREATE TABLE geometries (
    id SERIAL PRIMARY KEY,
    geom GEOMETRY(LineString, 4326)  -- 使用 WGS 84 坐标系
);

INSERT INTO geometries (geom) VALUES
(ST_GeomFromText('LINESTRING(0 0, 1 1, 2 0, 3 1, 4 0)', 4326));

使用道格拉斯-佩克算法简化几何

使用 PostGIS 提供的 ST_Simplify 函数来简化几何。这个函数可以使用道格拉斯-佩克算法来减少点的数量。

SELECT 
    id,
    ST_AsText(geom) AS original_geom,
    ST_AsText(ST_Simplify(geom, 1.0)) AS simplified_geom  -- 1.0 是简化的容差
FROM 
    geometries;

结果

执行上述查询后,您将看到原始几何和简化后的几何。ST_Simplify 函数的第二个参数是简化的容差值,您可以根据需要调整这个值来获得不同程度的简化。

示例结果

idoriginal_geomsimplified_geom
1LINESTRING(0 0, 1 1, 2 0, 3 1, 4 0)LINESTRING(0 0, 2 0, 4 0)

注意

  • ST_Simplify 函数的性能和结果会受到容差值的影响,较大的容差会导致更少的点和更大的形状变形。
  • 确保在执行这些查询之前,PostGIS 已正确安装并启用。

        道格拉斯-普克算法是一种高效的折线简化算法,广泛应用于GIS、图形处理和数据压缩等领域。通过合理的实现和应用,可以有效地提高系统的性能和用户体验。希望本文能够帮助您理解并实现该算法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1959562.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何优化PyTorch以加快模型训练速度?

PyTorch是当今生产环境中最流行的深度学习框架之一。随着模型变得日益复杂、数据集日益庞大&#xff0c;优化模型训练性能对于缩短训练时间和提高生产力变得至关重要。 本文将分享几个最新的性能调优技巧&#xff0c;以加速跨领域的机器学习模型的训练。这些技巧对任何想要使用…

【ROS 最简单教程 002/300】ROS 集成开发环境安装 (虚拟机版): Noetic

&#x1f497; 有遇到安装问题可以留言呀 ~ 当时踩了挺多坑&#xff0c;能帮忙解决的我会尽力 &#xff01; 1. 安装操作系统环境 Linux ❄️ VM / VirtualBox Ubuntu20.04 &#x1f449; 保姆级图文安装教程指路&#xff0c;有经验的话 可以用如下资源自行安装 ITEMREFERENCE…

遥感领域新方向!Mamba+RS论文汇总!

本文总结了将Mamba应用至遥感领域的相关论文&#xff08;14篇&#xff09;&#xff0c;涉及到的论文见文末链接&#xff0c;具体如下&#xff1a; 文章目录 1. 遥感图像处理2. 多/高光谱图像分类3. 变化检测/语义分割4. 遥感图像融合/超分辨率 1. 遥感图像处理 论文题目&#…

AWD神器—watchbird后台rce挖掘

简介 在传统的AWD攻防中&#xff0c;Waf扮演着重要的角色&#xff0c;Watchbird作为一款专门为AWD而生的PHP防火墙&#xff0c;具有部署简单&#xff0c;功能强大等特点&#xff0c;一出世便受到了广大CTFer的喜爱&#xff0c;目前在GitHub上已有600多star。本篇则详细介绍如果…

传输层UDP协议

传输层UDP协议 1. 再谈端口号2. UDP协议1.1 UDP协议字段1.2 将报头和数据进行分离 3. UDP的特点 1. 再谈端口号 端口号(Port)标识了一个主机上进行通信的不同的应用程序; 在 TCP/IP 协议中, 用 “源 IP”, “源端口号”, “目的 IP”, “目的端口号”, “协议号” 这样一个五元…

AI大模型评测方法总结!

大语言模型评测对应用和后续发展至关重要&#xff0c;其评测范式包括经典和新型两种。经典评测范式涵盖自然语言理解和生成任务&#xff0c;本文介绍了流行的经典评测基准及新型评测范式下的代表性基准和实例&#xff0c;并总结了现有评测的不足。随后&#xff0c;文章提出了全…

不入耳耳机和入耳耳机哪个好?四款不入耳蓝牙耳机推荐指南

那当然是不入耳耳机好啦&#xff0c;那先讲讲入耳和不入耳的区别&#xff1a; 佩戴舒适度&#xff1a; 入耳蓝牙耳机是直接插入耳道&#xff0c;所以同时也会堵塞耳道长期积攒耳垢、造成耳道不适。 不入耳蓝牙耳机则通常设计为耳挂式或耳廓式&#xff0c;挂在耳朵外部能保持…

antd pro实现后台管理系统的建立(一)

一、初始化项目 1、全局安装pro-cli初始化脚手架 # 使用 npm或者cnpm或tyarn npm i ant-design/pro-cli -g这里建议使用npm或者tyarn&#xff0c;cnpm和npm部分不兼容&#xff0c;创建项目时会报部分依赖缺失的问题 2、创建项目pro create myapp pro create myappsimple 是基…

本地部署 Llama 3.1:Ollama、OpenWeb UI 和 Spring AI 的综合指南

、 本文介绍如何使用 Ollama 在本地部署 Llama 3.1:8B 模型&#xff0c;并通过 OpenWeb UI 和 Spring AI 来增强模型交互体验和简化 API 的调用过程。 Ollama Ollama 是一个开源的大语言模型服务工具&#xff0c;旨在简化大模型的本地部署和运行过程。用户只需要输入一行命令&…

学习008-02-04-03 Group List View Data(组列表查看数据)

Group List View Data&#xff08;组列表查看数据&#xff09; This lesson explains how to group the Employee List View data by department and position. 本课介绍如何按部门和职位对员工列表视图数据进行分组。 Note Before you proceed, take a moment to review the …

从零开始使用YOLOv8——环境配置与极简指令(CLI)操作:1篇文章解决—直接使用:模型部署 and 自建数据集:训练微调

目录 一、Yolov8源码下载 二、虚拟环境创建与必要包的安装 1.虚拟环境 2.Pytorch安装 3.Ultralytics安装 3.环境测试 三、简单命令行指令&#xff08;CLI&#xff09;使用 1.模型配置&#xff08;关键指令&#xff09;——cfg &#xff08;1&#xff09;选择任务场景——ta…

收藏丨企业官网一般选择什么类型的SSL证书比较好?

企业官网在选择SSL证书时&#xff0c;应综合考虑网站的安全性需求、用户信任度、成本效益以及管理便捷性等因素。一般来说&#xff0c;企业官网比较适合选择以下几种类型的SSL证书&#xff1a; 1. 企业型SSL证书&#xff08;Organization Validated, OV&#xff09; 特点&…

初识MyBati s

J D B C 编 程 和 O R M 模 型 加 载 驱 动 导 入 J D B C 连 接 数 据 库 的 j a r 包 &#xff0c; 利 用 C L A S S . f o r N a m e 加 载 驱 动 &#xff1b; 获 取 连 接 利 用 D r i v e r M a n a g e r 获 取 C o n n e c t i o n &#xff0c; 然 后 创 建 S t a t e…

生成式人工智能的第一课,揭开她那神奇的面纱

一、人工智能&#xff08;Artificial Intelligence&#xff09; 从1940年代开始&#xff0c;科学家们在数学推理的基础上发明了可编程数字计算机&#xff0c;这一突破激发了他们对创建“电子大脑”的探索热情&#xff0c;为后续的人工智能发展奠定了坚实的基础。1956年&#x…

2024西安铁一中集训DAY23 ---- 模拟赛(类括号匹配dp + baka‘s trick 优化双指针 + 组合数学/高斯消元 + 图上性质题)

文章目录 前言时间安排及成绩题解A. 稻田灌溉&#xff08;类括号匹配dp&#xff09;B. 最长模区间&#xff08;bakas trick 优化双指针&#xff09;C. 三只小猪和狼&#xff08;组合数学&#xff0c;高斯消元&#xff09;D. 黑色连通块 前言 感觉是开始集训以来最难的一场了&a…

睿考网:中级会计师和注册会计师哪个难?

中级会计师和注册会计师两个资格证书对比下来&#xff0c;后者具有更高的挑战性&#xff0c;主要原因有以下几点&#xff1a; 1. 考试科目和内容&#xff1a; 注册会计师考试包含专业阶段与综合阶段&#xff0c;共涉及六个科目&#xff0c;考察的内容覆盖范围更广泛&#xff…

最新Yiso智云搜索引擎系统源码/开源PHP源码/修复版

源码简介&#xff1a; 最新Yiso智云搜索引擎系统源码/开源PHP源码/修复版。Yiso 是一个性能非常好的搜索引擎&#xff0c;不仅免费开源&#xff0c;还能当作收录网址的平台来用呢&#xff01;只需要输入关键词&#xff0c;就能轻松找到相关的搜索结果内容。 1、Yiso 用的是自…

脚本: 监控Oracle数据库中正在运行的SQL(Oracle DBA的工作利器)

英文原文网址&#xff1a;[Script: Monitoring Running SQL in Oracle Database in Real Time] (https://byte-way.com/2024/07/24/script-monitoring-running-sql-in-oracle-database-in-real-time/) 以下SQL查询活动会话及其正在执行的SQL语句的信息&#xff0c;并提供有关其…

生成式AI 未来发展的两大要素:数据和开发者

这一年来&#xff0c;生成式 AI 领域的发展可谓日新月异。大语言模型 (LLM) 已经从学术研究圈的新宠&#xff0c;变成了开发者、产品经理、IT 决策者、高管等所有人都密切关注和亲身参与的重要课题。 一年间&#xff0c;这类问题在新闻报道、技术大会、开发者闲聊、同事讨论、…

【Dash】Hello World

一、最简单的 Dash Building and launching an app with Dash can be done with just 5 lines of code. Open a Python IDE on your computer, create an app.py file with the code below and install Dash if you havent done so already. To launch the app, type into yo…