明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 2000|回复: 10

[经验] 分治策略在查找最近点问题中的应用一例

[复制链接]
发表于 2015-1-31 19:48 | 显示全部楼层 |阅读模式
本帖最后由 vectra 于 2015-1-31 20:10 编辑

问题提出见原贴 http://bbs.mjtd.com/thread-112884-1-1.html

逐一检测的时间复杂度是O(n^2),当n较大时,时间开销不能接受。

采用分治方法,先将对象按X坐标排序,并对对象进行分组,每次只对在X坐标正负指定间距内的对象逐一检测,时间复杂度接近O(n)


  1. (defun stop-watch-start  (/)
  2.   (setq *stop-watch-start* (getvar "millisecs"))
  3. )


  4. (defun stop-watch-elapse (/)
  5.   (- (getvar "millisecs") *stop-watch-start*)
  6. )

  7. (defun report-time-consuming (msg)
  8.   (princ
  9.     (strcat "\n" msg " (" (itoa (stop-watch-elapse)) " ms)")
  10.   )
  11.   (stop-watch-start)
  12. )

  13. (defun sort-point-ent-pair (pep)
  14.   (setq
  15. ;;;    pep (vl-sort pep '(lambda (e1 e2) (< (caddar e1) (caddar e2))))
  16. ;;;  pep (vl-sort pep '(lambda (e1 e2) (< (cadar e1) (cadar e2))))
  17.     pep
  18.      (vl-sort pep '(lambda (e1 e2) (< (caar e1) (caar e2))))
  19.   )
  20.   pep
  21. )


  22. (defun find-closest-bruteforce (pep1 pep2 / tmp)
  23.   (setq tmp pep1)
  24.   (while pep2
  25.     (while tmp
  26.       (if (<= (distance (caar tmp) (caar pep2)) *gap*)
  27.   (vla-put-color (vlax-ename->vla-object (cadar tmp)) 1)
  28.       )
  29.       (setq tmp (cdr tmp))
  30.     )
  31.     (setq tmp  pep1
  32.     pep2 (cdr pep2)
  33.     )
  34.   )
  35. )


  36. (defun get-point-ent-pair (ent / dxf)
  37.   (setq dxf (entget ent))
  38.   (cond
  39.     ((= "TEXT" (cdr (assoc 0 dxf)))
  40.      (setq pep1 (cons (list (cdr (assoc 10 dxf)) ent) pep1))
  41.     )
  42.     ((= "LINE" (cdr (assoc 0 dxf)))
  43.      (setq pep2
  44.       (cons
  45.         (list
  46.     (mapcar
  47.       '(lambda (e) (/ e 2.0))
  48.       (mapcar '+ (cdr (assoc 10 dxf)) (cdr (assoc 11 dxf)))
  49.     )
  50.     ent
  51.         )
  52.         pep2
  53.       )
  54.      )
  55.     )
  56.   )
  57. )


  58. (defun get-test-data ()
  59.   (setq  ss    (ssget '((0 . "LINE,TEXT")))
  60.   i     0
  61.   *gap* 50.0
  62.   )
  63.   (stop-watch-start)

  64.   (repeat (sslength ss)
  65.     (get-point-ent-pair (ssname ss i))
  66.     (setq i (1+ i))
  67.   )
  68.   (report-time-consuming (strcat "构建点实体表 " (itoa (sslength ss)) "个对象"))
  69. )


  70. (defun c:tt (/ i pep1 pep2 ss)

  71.   (get-test-data)

  72.   (stop-watch-start)

  73.   (vla-startundomark
  74.     (vla-get-activedocument (vlax-get-acad-object))
  75.   )
  76.   (find-closest-bruteforce pep1 pep2)
  77.   (vla-endundomark
  78.     (vla-get-activedocument (vlax-get-acad-object))
  79.   )
  80.   (report-time-consuming "两次循环")

  81.   (princ)
  82. )


  83. (defun c:tt2 (/ cur i pep1 pep2 rh1 rh2 ss tmp)

  84.   (get-test-data)

  85.   (setq  pep1 (sort-point-ent-pair pep1)
  86.   pep2 (sort-point-ent-pair pep2)
  87.   )
  88.   (report-time-consuming "排序")


  89.   (vla-startundomark
  90.     (vla-get-activedocument (vlax-get-acad-object))
  91.   )
  92.   (while (and pep1 pep2)
  93.     ;; 取线段表中第一个元素为游标
  94.     (setq cur  (caar pep2)
  95.     rh2  (list (car pep2))
  96.     pep2 (cdr pep2)
  97.     tmp  pep1
  98.     )

  99.     ;; 将游标附近X坐标差值在*gap*范围内的线段归集到rh2
  100.     (while (and  pep2
  101.     (<= (abs (- (car (caar pep2)) (car cur))) *gap*)
  102.      )
  103.       (setq rh2   (cons (car pep2) rh2)
  104.       pep2 (cdr pep2)
  105.       )
  106.     )
  107.     ;; 将游标附近X坐标差值在*gap*范围内的文字归集到rh1
  108.     (while tmp
  109.       (if (<= (abs (- (car (caar tmp)) (car cur))) *gap*)
  110.   (setq rh1 (cons (car tmp) rh1))
  111.   (if (< (car (caar tmp)) (car cur))
  112.     (setq pep1 (cdr pep1))
  113.     (setq tmp nil)
  114.   )
  115.       )
  116.       (setq tmp (cdr tmp))
  117.     )

  118.     (if  (and rh1 rh2)
  119.       (find-closest-bruteforce rh1 rh2)
  120.     )

  121.     (while rh1
  122.       (setq pep1 (cdr pep1)
  123.       rh1   (cdr rh1)
  124.       )
  125.     )
  126.   )
  127.   (vla-endundomark
  128.     (vla-get-activedocument (vlax-get-acad-object))
  129.   )
  130.   (report-time-consuming "完成符合条件的对象标记")


  131.   (princ)
  132. )



命令: tt
构建点实体表 9202个对象 (297 ms)
两次循环 (34093 ms)

命令:  TT2
构建点实体表 9202个对象 (140 ms)
排序 (31 ms)
完成符合条件的对象标记 (766 ms)

命令: tt2
构建点实体表 31598个对象 (563 ms)
排序 (109 ms)
完成符合条件的对象标记 (2719 ms)

命令:  TT2
构建点实体表 46010个对象 (1907 ms)
排序 (203 ms)
完成符合条件的对象标记 (5781 ms)

命令:  TT2
构建点实体表 55212个对象 (2266 ms)
排序 (250 ms)
完成符合条件的对象标记 (6281 ms)

通过测试发现,E3 1230V2上每秒可处理近1万个对象。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x

评分

参与人数 2明经币 +2 收起 理由
mub001 + 1 赞一个!
USER2128 + 1 很给力!

查看全部评分

"觉得好,就打赏"
还没有人打赏,支持一下
发表于 2015-1-31 22:01 | 显示全部楼层
顶一个,算法确实对效率有很大影响
发表于 2015-2-1 00:17 | 显示全部楼层
很赞,支持一个
发表于 2015-2-1 10:40 | 显示全部楼层
确实很实用,请问一下“millisecs”是什么变量
发表于 2015-2-1 19:19 | 显示全部楼层
提点个人意见
如有冒犯楼主勿怪

就原求助帖的要求而言
我认为楼主的方法可能还不是最合适的
昨天看此贴时
总感觉楼主的思路绕了弯
今天测试了一把
测试对比结果放在下贴

我的方法是直接以线段中点
构建ssget 'cp的 cp-polist、filter-list
再逐一检测选择集图元是否满足要求

个人感觉楼主的程序
或许还能在以下几个方面优化
1.
开始的点对构建
直接按line、text分别获取选择集
cond判别是要走流程的
2.
仅以x排序不合适
应同时以x、y排序
3.
mapcar、lambda的应用
纯粹个人感觉啊
它们能让程序看起来很简洁
但并不能减少代码执行的时间
需要反复调用的功能
还是做成子函数比较合适

发表于 2015-2-1 19:21 | 显示全部楼层
●●●●简单线字关系●●●●数量较少————————————————直线27606,文字共27606,改色13932

◆vectra◆
命令: tt2
构建点实体表 55212个对象 (2750 ms)
排序 (531 ms)
完成符合条件的对象标记 (6484 ms)
|
程序总耗时 9506 毫秒

◆masterlong◆
命令: tt
【以直线搜文字】
|
程序总耗时 9829 毫秒



●●●●简单线字关系●●●●数量较多(9倍)—————————直线248454,文字共248454,改色125388

◆vectra◆
命令: tt2
构建点实体表 496908个对象 (43875 ms)
排序 (7250 ms)
完成符合条件的对象标记 (167218 ms)
|
程序总耗时 219453 毫秒

◆masterlong◆
命令: tt
【以直线搜文字】
|
程序总耗时 98938 毫秒





●●●●复杂线字关系●●●●数量较少————————————————直线37249,文字共46010,改色9159

◆vectra◆
命令: tt2
构建点实体表 83259个对象 (4750 ms)
排序 (1109 ms)
完成符合条件的对象标记 (12687 ms)
|
程序总耗时 18703 毫秒

◆masterlong◆
命令: tt
【以直线搜文字】
|
程序总耗时 11500 毫秒



●●●●复杂线字关系●●●●数量较多(6倍)—————————直线223494,文字共276060,改色54954

◆vectra◆
命令: tt2
构建点实体表 499554个对象 (82031 ms)
排序 (7750 ms)
完成符合条件的对象标记 (158406 ms)
|
程序总耗时 249218 毫秒

◆masterlong◆
命令: tt
【以直线搜文字】
|
程序总耗时 141172 毫秒

评分

参与人数 1明经币 +1 收起 理由
vectra + 1 用数字说话

查看全部评分

发表于 2015-2-1 19:32 | 显示全部楼层










本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x

评分

参与人数 1明经币 +1 收起 理由
USER2128 + 1 赞一个!

查看全部评分

 楼主| 发表于 2015-2-1 20:15 | 显示全部楼层
框选法核心还是空间数据索引、分治等应用,只不过是CAD内置功能,代码实现是dll级别的,效率高lisp 不知道N倍。

加入对Y的分治可以进一步提高速度,只是代码会更加复杂。复杂的代码,可靠性难保证。

框选法最大的问题是对视口范围依存度很高,比如视口不显示的对象无法处理,当处理区域缩放比率过小时,选择结果不可预测。这会导致实际应用中产生不期望的结果或性能损失。

楼上可以试下下面这个图,不要更改缩放比率运行tt,时间明显比一个合理的绽放比率时要长许多倍。。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
发表于 2015-2-1 20:45 | 显示全部楼层
你说得对
框选法有较大的局限性
而纯数学的方法会比较稳定

在两者之间如何取舍
编程中我也经常为之纠结
发表于 2015-2-2 19:45 | 显示全部楼层
如果能通过数学方法解决,尽可能不要依赖CAD本身的功能,虽然我们离不开CAD,但并不妨碍用数学方法解决一些问题
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-4-28 11:26 , Processed in 0.279169 second(s), 33 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表