【算法】道格拉斯

一、引言

        道格拉斯算法是一种用于曲线拟合的数学方法,特别是在处理曲线插值问题时非常有用。道格拉斯-普克算法(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 = xself.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 leftresult.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 mathclass Point:def __init__(self, x, y):self.x = xself.y = ydef 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 distancenum = 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 / denomdef douglas_peucker(points, epsilon):# If the line is too short, return the endpointsif len(points) < 2:return pointsstart = points[0]end = points[-1]# Find the point with the maximum distance from the line segmentmax_distance = 0.0index = 0for i in range(1, len(points) - 1):distance = perpendicular_distance(points[i], start, end)if distance > max_distance:index = imax_distance = distance# If max distance is greater than epsilon, recursively simplifyif max_distance > epsilon:left = douglas_peucker(points[:index + 1], epsilon)right = douglas_peucker(points[index:], epsilon)return left[:-1] + rightelse: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 leftresult.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 mainimport ("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.0index := 0for i := 1; i < len(points)-1; i++ {distance := perpendicularDistance(points[i], start, end)if distance > maxDistance {index = imaxDistance = distance}}var result []Pointif maxDistance > epsilon {left := douglasPeucker(points[:index+1], epsilon)right := douglasPeucker(points[index:], epsilon)result = append(result, left[:len(left)-1]...) // Remove last point of leftresult = 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.5simplifiedPoints := douglasPeucker(points, epsilon)fmt.Println(simplifiedPoints)
}

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

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

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

from flask import Flask, request, jsonify
import mathapp = Flask(__name__)class Point:def __init__(self, x, y):self.x = xself.y = ydef 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 / denomdef douglas_peucker(points, epsilon):if len(points) < 2:return pointsstart = points[0]end = points[-1]max_distance = 0.0index = 0for i in range(1, len(points) - 1):distance = perpendicular_distance(points[i], start, end)if distance > max_distance:index = imax_distance = distanceif max_distance > epsilon:left = douglas_peucker(points[:index + 1], epsilon)right = douglas_peucker(points[index:], epsilon)return left[:-1] + rightelse:return [start, end]@app.route('/simplify', methods=['POST'])
def simplify_path():data = request.jsonpoints = [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.mzph.cn/pingmian/51772.shtml

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

相关文章

01 Python环境安装

1.进入官网选择合适的版本&#xff0c;进行下载。Welcome to Python.org&#xff0c;提供了支持不同操作系统的安装包&#xff0c;目前最新版本为Python3.12&#xff0c;但在学习阶段一般不推荐大家使用最新版本的软件&#xff0c;因为对于其他软件需要该环境时&#xff0c;还不…

超13万律师使用的工具,启信宝推出“司法大数据”功能

合合信息旗下的启信宝&#xff0c;作为行业领先的商业查询APP&#xff0c;依托其3亿企业及机构的实时动态数据&#xff0c;涵盖工商、股权、司法、知识产权等多维度信息&#xff0c;推出了“司法大数据”功能。 随着大数据、人工智能技术的发展&#xff0c;律师行业已转型为开…

python——joblib进行缓存记忆化-对计算结果缓存

问题场景 在前端多选框需要选取多个数据进行后端计算。 传入后端是多个数据包的对应路径。 这些数据包需要按一定顺序运行&#xff0c;通过一个Bag(path).get_start_time() 可以获得一个float时间值进行排序&#xff0c;但由于数据包的特性&#xff0c;这一操作很占用性能和时…

物联网服务器搭建及部署详细说明:掌握 Node.js、MongoDB、Socket.IO 和 JWT 的实用指南

关键知识点目录 1. 环境准备 1.1 硬件要求 1.2 软件要求 2. 搭建步骤 3. 数据处理与存储 3.1 数据存储 3.2 数据实时处理 3.2.1 安装 Socket.IO 3.2.2 修改服务器代码 4. 安全性 4.1 身份验证与授权 4.2 加密通信 4.2.1 生成自签名证书&#xff08;开发环境&#…

uniapp+vue微信小程序项目中实现手机号授权登录

效果图: 实现方式: <button open-type="getPhoneNumber" class="btn-normal" @getphonenumber="getPhoneNumber">手机号授权登录</button> open-type 有效值 值说明平台差异说明getPhoneNumber获取用户手机号,可以从@getphone…

LeetCode Hot100 将有序数组转换为二叉搜索树

给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡 二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#xff1a;[0,-10,5,null,-3,null,9] 也将被视为正确…

SpringBoot整合jasypt加密和解密yml配置文件

使用场景 在微服务架构中&#xff0c;配置管理是一个重要的问题。通常&#xff0c;我们会在配置文件中存放一些敏感信息&#xff0c;如数据库连接字符串、API 密钥等。这些敏感信息如果明文存储在配置文件中&#xff0c;存在较大的安全隐患。为了提高安全性&#xff0c;我们需…

Linux安装与配置

下载VMware 首先我们需要下载一个叫VMware的软件&#xff1a; 进入官方下载&#xff0c;地址&#xff1a;https://www.vmware.com/cn/products/workstation-pro/workstation-pro-evaluation.html选择与自己电脑版本适配的VMware版本【 输入许可证密钥 MC60H-DWHD5-H80U9-6V85…

Python | “IndexError: tuple index out of range” 【已解决】

Python | “IndexError: tuple index out of range” 【已解决】 IndexError: tuple index out of range 深度解析与实战指南 在Python编程中&#xff0c;IndexError: tuple index out of range是一个常见的错误&#xff0c;它发生在尝试访问元组&#xff08;或其他可索引的数…

前端canvas——贝塞尔曲线

曲线之美&#xff0c;不在于曲线本身&#xff0c;而在于用的人。 所以就有了这期贝塞尔曲线。 新规矩&#xff0c;先上个GIT。 效果图 开局一张图&#xff0c;代码全靠编。 代码 画骨 先想着怎么画一个心形吧&#xff0c;等你想好了&#xff0c;就知道怎么画了。 首先就还…

iPhone 中阅读器模式是什么?怎么开启该模式?

什么是阅读器模式&#xff1f;这是一个浏览器功能&#xff0c;可以为用户提供简洁、干净的阅读界面。当你在 Safari 浏览器中启用阅读器模式时&#xff0c;网页上的广告、侧边栏和其他不相关的元素将被移除&#xff0c;仅保留主要的文字内容和相关图片。这使得用户可以专注于阅…

前端开发的十字路口,薪的出口会是AI吗?

前言 在数字化转型的浪潮中&#xff0c;前端开发一直扮演着至关重要的角色&#xff0c;它连接着用户与产品之间的桥梁。然而&#xff0c;随着技术的不断进步和社会经济环境的变化&#xff0c;前端开发领域也面临着前所未有的挑战和机遇。 前端开发的困境 前端开发领域的竞争…

Vue3多语言实现

1.首先安装i18n npm install vue-i18n 2.在项目下创建lang目录并创建en.ts,i18n1.ts,zh.ts en.ts export default {message: {home: home,appTitle:aa 3D Smart Measure}, Menus: {Measuer: Measure,},GlueMeasure: {Title: Camera 3D Glue Measure,}} zh.ts export …

android(安卓)最简单明了解释版本控制之MinSdkVersion、CompileSdkVersion、TargetSdkVersion

1、先明白几个概念 &#xff08;1&#xff09;平台版本&#xff08;Android SDK版本号&#xff09; 平台版本也就是我们平时说的安卓8、安卓9、安卓10 &#xff08;2&#xff09;API级别&#xff08;API Level&#xff09; Android 平台提供的框架 API 被称作“API 级别” …

什么是CAN的BUS-OFF,如何恢复BUS-OFF?

CAN作为一款强大的工业总线&#xff0c;其高性能和高可靠性让其应用特别广泛。一个小知识&#xff1a;汽车里面各个模块之间的通讯就是使用CAN来实现的哦。 既然是总线&#xff0c;那当然会有多个设备挂载在CAN上&#xff0c;当一个设备在发送数据的时候&#xff0c;其他设备也…

基于STC8H4K64TL单片机的RTC(即实时时钟)功能调试

基于STC8H4K64TL单片机的RTC(即实时时钟)功能调试 STC8H4K64TL单片机介绍STC8H4K64TL单片机管脚图(48个引脚)STC8H4K64TL单片机串口仿真与串口通信STC8H4K64TL单片机管脚图(32个引脚)STC8H4K64TL单片机管脚图(20个引脚)STC8H系列单片机管脚说明STC8H系列单片机I/O口STC…

大模型不会比大小和单词字母计数?日期计算也是一片混乱

9.9和9.11哪个大&#xff1f;13.8%和13.11%谁大谁小&#xff1f;这两个比大小的问题&#xff0c;前段时间难倒众多大模型&#xff0c;在网上掀起热议。 除此之外&#xff0c;向大模型提问“草莓&#xff08;strawberry&#xff09;这个单词有几个r时”&#xff0c;多家主流大模…

三目操作符

双目操作符有、-、*、/、% 单目操作符有--、、、- 三目操作符有表达式1&#xff1f;表达式2&#xff1a;表达式3 如果表达式1为真则表达式2计算否则表达式3计算&#xff0c;计算结果为整个表达式的结果 #include<stdio.h> int main() {int a 0;int b 0;scanf(&quo…

Doris-计算特性

1 全新优化器 1.1 如何开启1.2 统计信息 1.2.1 使用ANALYZE语句手动收集1.2.1 自动收集1.2.3 作业管理1.3 会话变量及配置项调优参数2 Join相关 2.1 支持的Join算子2.2 支持的shuffle方式 2.2.1 Broadcast Join2.2.2 Shuffle Join2.2.3 Bucket Shuffle Join 2.2.3.1 原理2.2.3.…

PHP反序列化漏洞从入门到深入8k图文介绍,以及phar伪协议的利用

文章参考&#xff1a;w肝了两天&#xff01;PHP反序列化漏洞从入门到深入8k图文介绍&#xff0c;以及phar伪协议的利用 前言 本文内容主要分为三个部分&#xff1a;原理详解、漏洞练习和防御方法。这是一篇针对PHP反序列化入门者的手把手教学文章&#xff0c;特别适合刚接触PH…