区域内随机布点的方法

本文研究如何在区域内随机均匀布点, 来源于 <算法可视化 Visualizing Algorithms>, 这在CAE, 图形学中都有重要意义. 
http://www.bilibili.com/video/av2182749/

比如在 3*1 的区域内随机布点 1000 个, 如果每个点都是坐标都是随机给的, 结果可能并不均匀.
如 randomPoints.m 所演示的.

 1 function randomPoints()
 2 xmax = 3;
 3 ymax = 1;
 4 N = 500;
 5 x = xmax*rand(N,1);
 6 y = ymax*rand(N,1);
 7 figure(1);
 8 plot(x,y,‘.‘,‘markerSize‘,5,‘markerEdgeColor‘,‘r‘); hold on;
 9 title(‘Random Sampling‘);
10 axis equal;
11 axis([0,xmax,0,ymax]);
12 set(gca,‘xTick‘,[],‘yTick‘,[]);
13 end

randomPoints.m

接下来介绍一种方法称为 Best-Candidate Sampling, 基本思路是, 没加一个点需要在若干 (numCandidates) 点 (比如 10)中寻找和已知点最近距离最大的那个点.

每加一个点的伪代码如下:

bestCandidate= 0;
bestDistance = 0;
loop i=1:numCandidates{
  c = [rand(),rand()]; % 随机点 c 的坐标
  d = distance( findClosest(samples,c), c ); % 获取 c 和已知点中最近的距离
  if (d > bestDistance){ % 如果 c 点的距离大于任何点
    bestDistance = d; % 更新最大距离
    bestCandidate = c; % 更新最佳点
  }
}

这种方法可以获得比较均匀的分布, 但是当数据点比较多时, 需要很多的计算量. 另外, numCandidates 越大, 分布也越均匀. 但是同样带来的计算量增长也是不可忽略的. 计算结果可以参考代码 bestCandidate.m

 1 function bestCandidate()
 2 xmax = 3;
 3 ymax = 1;
 4 N = 500;
 5 x = zeros(N,1); y = zeros(N,1);
 6 x(1) = rand(1,1)*xmax; % 初始布置一个点
 7 y(1) = rand(1,1)*ymax; % 或许可以布置多个点在边界上哦
 8 numCandidates = 10;
 9 for ii = 2:N
10     xCand = rand(numCandidates,1)*xmax; % 随机选择 numCand.. 个候选点
11     yCand = rand(numCandidates,1)*ymax;
12     bestDistance = 0;
13     for jj = 1:numCandidates
14         dists = sqrt( (x(1:ii-1)-xCand(jj)).^2 + (y(1:ii-1)-yCand(jj)).^2 ); % 已知点和 jj 号候选点的距离
15         dist = min(dists);     % 获取最小的距离
16         if (dist > bestDistance) % 如果 c 点的距离大于任何点
17             bestDistance =  dist; % 更新最大距离
18             bestCandidate = jj;   % 更新最佳点
19         end
20     end
21     x(ii) = xCand(bestCandidate); y(ii) = yCand(bestCandidate); % 插入一个点
22 end
23 figure(1);
24 plot(x,y,‘.‘,‘markerSize‘,5,‘markerEdgeColor‘,‘r‘); hold on;
25 title(‘Best-Candidate Sampling‘);
26 axis equal;
27 axis([0,xmax,0,ymax]);
28 set(gca,‘xTick‘,[],‘yTick‘,[]);
29 end

bestCandidate

结果如下:

最后介绍的方法称为 泊松碟采样 Poisson Disk Sampling, 06~07 年提出. 这种方法效果更佳, 因为它排除了两个点半径小于固定值 r 的情况. 基本思路如下:


1. 设定随机初始点和背景网格. 为保证每个网格内至多只有一个点, 网格尺寸为 r/2pi, r 为任意两点间的最小距离.
2. 循环直到没有可选点
a) 从已知可选点中选择一个, 在其外生成一个圆环 (内径 r, 外径 2r)
b) 从圆环中选择候选点(典型地, k=30个). 检测候选点和已知点之间的最小距离(可以利用背景网格减少计算量) 假如距离小于设定值 r, 则放弃该候选点; 否则加入一个新点.
c) 假如无法增加候选点, 那么可以将中心点变为不可选.

Poisson Disk 方法有很多改进和扩展, 其计算量已经为 O(N lg N), 甚至有快速算法到了 O(N); 但这里为了代码演示, 我们不考虑优化, 也不采用背景网格. 如 poissonDisk.m 所演示的.

 1 function poissonDisk()
 2 close all;clear all;clc;
 3 xmax = 3;
 4 ymax = 1;
 5 N = 5000;          % 一个比较大的数, 足以存放点
 6 r = sqrt(3*1/500/pi*2); % 最小半径, 这样设计大约有 500 个点
 7 x = zeros(N,1); y = zeros(N,1);
 8 ava = zeros(N,1);
 9 x(1) = rand(1,1)*xmax; % 初始布置一个点
10 y(1) = rand(1,1)*ymax; % 或许可以布置多个点在边界上哦
11 ava(1) = 1; cnt = 1;
12 numCandidates = 30;
13 while(sum(ava)~=0) % 可选点判断
14     cava = find(ava(1:cnt)); % 找出可选点
15     cnum = cava( floor( rand()*length(cava)+1 ));  % 从可选点中选一个
16     rCand = ( rand(numCandidates,1)+1 )*r; % 随机选择 numCand.. 个候选点
17     thetaCand = rand(numCandidates,1)*2*pi;
18     xCand = rCand.*cos(thetaCand) + x(cnum);
19     yCand = rCand.*sin(thetaCand) + y(cnum);
20     % 寻找是否有合适点
21     flag = 0;
22     for jj = 1:numCandidates
23         dists = sqrt( (x(1:cnt)-xCand(jj)).^2 + (y(1:cnt)-yCand(jj)).^2 ); % 已知点和 jj 号候选点的距离
24         dist = min(dists);       % 获取最小的距离
25         if (dist > r && xCand(jj) <= xmax && yCand(jj) <= ymax ...
26                      && xCand(jj) >= 0    && yCand(jj) >= 0   ) % 如果 c 点的距离大于任何点
27             x(cnt+1) = xCand(jj); y(cnt+1) = yCand(jj);
28             ava(cnt+1) = 1;
29             cnt = cnt + 1;
30             flag = 1;
31             break;
32         end
33     end
34     % 以下代码能运行表示在 numCandidates 候选点中没有可用的, 故将点 cnum 变为不可选
35     if flag == 0
36         ava(cnum) = 0;
37     end
38 end
39 figure(1);
40 plot(x,y,‘.‘,‘markerSize‘,5,‘markerEdgeColor‘,‘r‘); hold on;
41 title(‘Poisson Disk Sampling‘);
42 axis equal;
43 axis([0,xmax,0,ymax]);
44 set(gca,‘xTick‘,[],‘yTick‘,[]);
45 end

PoissonDisk.m

扩展阅读:
Jittered Grid: 将区域划分成一个个方块, 然后在方块里任意填充点. 同样效果不太好.
http://devmag.org.za/2009/05/03/poisson-disk-sampling/

时间: 2024-08-06 13:09:08

区域内随机布点的方法的相关文章

实现一种快速查找Richedit中可见区域内OLE对象的方法

Richedit是一个OLE容器,使用Richedit来显示IM聊天内容时,通常使用OLE对象来实现在Richedit中播放表情动画. 触发表情的绘制有两种途径: 1.来自Richedit的刷新消息. 2.来自表情动画定时器的刷新消息. 要刷新表情的显示首先需要知道表情的显示位置. 第一种刷新过程中,绘制消息参数里已经给出绘制位置,直接在指定的位置绘制即可. 但是表情主动刷新时如何获取表情的显示位置确是一个问题. 网上有不少代码演示了如何获通过枚举Richedit中的OLE对象获取表情的代码.

百度地图多边形画区域、获取节点经纬度坐标、判断某一点是否在此区域内

创建可绘画map: 1 <!DOCTYPE html> 2 3 <html> 4 <head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1, user-scalable=no"> 7 <meta name=&

[三卷天书]记一个asp.net生成两个日期范围内生成随机时间的方法

想网上找个生成随机天数的方法找不到,后面只得自己写了,贴给大家方便使用 思路:算两个日期的相差天数,然后在0到相差天数的范围内生成随机数,再用结束时间的天数部分减去这个随机数,代码: /// <summary> /// (在两个时间范围内)生成随机日期 /// </summary> /// <param name="startime">开始时间</param> /// <param name="endtime"&

ios的hitTest方法以及不规则区域内触摸事件处理方法

ios的hitTest方法以及不规则区域内触摸事件处理方法 概述 在正常的使用场景中,我们处理了比较多的矩形区域内触摸事件,比如UIButton.UIControl.一般来说,这些控件的图形以及触摸区域都是矩形或者圆角矩形的.但是在一些特殊应用场景中我们有时不得不面对这样一种比较严苛的需求,比如要求程序只对某个圆形.五角形等非常规区域的点击事件进行处理,这就需要花点功夫了.本文以圆形为例子来介绍此类场景的处理方法. 先看下面一张图(附图1),我们的目标是实现如下自定义tabbar.中间带突起圆形

【原】ios的hitTest方法以及不规则区域内触摸事件处理方法

在正常的使用场景中,我们处理了比较多的矩形区域内触摸事件,比如UIButton.UIControl.一般来说,这些控件的图形以及触摸区域都是矩形或者圆角矩形的.但是在一些特殊应用场景中我们有时不得不面对这样一种比较严苛的需求,比如要求程序只对某个圆形.五角形等非常规区域的点击事件进行处理,这就需要花点功夫了.本文以圆形为例子来介绍此类场景的处理方法. 先看下面一张图(附图1),我们的目标是实现如下自定义tabbar.中间带突起圆形的自定义tabbar曾一度流行,今天我们来粗糙地实现一下. 在附图

过滤所有用户的行车轨迹查找在某一区域内的用户

// 判断经纬度是否在此区域内 public void selectDevice2() throws IOException{ String birthday = device.getBirthday(); String Position1 = device.getJingweidu1(); String Position2 = device.getJingweidu2(); // 创建表 String IMSIPositionTableName = "IMSIP_"+birthday

5.HCNP-R&amp;S-IERN——计算OSPF区域内路由

本次介绍OSPF如何计算区域内路由.内容包括如何使用Router-LSA和Network-LSA表示链路状态信息,以及如何计算最短路径树等. 学习目标: 1. 理解Router-LSA 2. 理解Network-LSA 3. 理解最短路径树的计算 LSDB通过描述一个有向线段图来描述网络拓扑结构,该有向图的端点有三种类型:路由器节点,Stub网段和Transit网段. Stub网段表示该网段只有数据入口,例如一个Loopback接口就是一个Stub网段. Cost表示从一个端点到另一个端点的开销

高德地图判断点的位置是否在浏览器可视区域内

业务场景如下: 1.在地图上点击企业位置mark时,地图不做缩放和移动操作(能点击mark,说明该位置肯定在可视区域内). 2.点击右侧企业列表的企业时,如果企业的位置不在当前可视区域内,就需要将地图平滑的移动到该企业位置,并且需要缩小地图,先查看到该企业位于哪个区域,再将地图放大到之前缩放的级别. 实现思路: 高德地图有几个关系判断的API:判断点是否在线上.点是否在多边形内.面与面的几何关系,可看下方链接示例 https://lbs.amap.com/api/javascript-api/e

JSP 的内置对象及方法,动作和作用,常用指令

JSP 的内置对象及方法:JSP 共有以下9 种基本内置组件:request:用户端请求,此请求会包含来自GET/POST 请求的参数:response:网页传回用户端的回应:pageContext:网页的属性是在这里管理:session:与请求有关的会话期,可以存贮用户的状态信息:application:servlet 正在执行的内容:out:用来传送回应的输出:config:servlet 的构架部件,用于存取servlet 实例的初始化参数:page:JSP 网页本身:exception: