c++ 凸包 分治算法_算法(2)——分治算法

分治算法

分治 divide and conquer

要点:

子问题与原问题性质一样,且可以独立求解。

-----------------------

分治算法的复杂度分析:递推方程;

常见递推方程的结论(看了这一部分可以不看主定理了):一类是每次减1,2,3这样子的,像那个什么什么塔;

一类是倍数的减少,n/2,n/3这样子,例如快排

对于后者:方程为:

d(n) = C:

a = 1 ------> logn

a!=1 ------>

d(n) = cn:

a=b -> nlogn

a>b ->

a n

----------------------------------------

一些超级经典的例子:芯片测试

快速排序(关键是划分这一步Partition(A,p,r))

冪乘问题

(真要写清楚,可以一天不干活了T^T)

1 芯片测试:

问题描述:给定n个芯片,(1)好芯片比坏芯片至少多一片;(2)两个芯片可以互相测出对方的好坏,好芯片可以测准,坏芯片不一定测准。从中选出一片好芯片。

思路分析:

(1)角度一:随机选一片芯片,与其他芯片比较:当芯片总数是偶数:好芯片数目大于等于 n/2+1 , 如果选中的芯片是好芯片,剩下的超过一半( n/2) 报好(结果一) ,如果选中的芯片是坏芯片,超过一半的报坏(结果二);结果不是一半(及以上)报好就是一半(及以上)报坏,因此可以检测出选中的单芯片的好坏;

如果选中的芯片是奇数:好芯片数目大于等于 (n+1)/2 ,如果选中的是好芯片,剩下的至少芯片一半报好;如果选中的是坏芯片,超过一半报坏;结果不是一半(及以上)报好就是一半(及以上)报坏,因此可以检测出选中的单芯片的好坏;

仔细想一想,由于好芯片比坏芯片多,抽出一片好芯片,剩下的至少还有一半好芯片,根据他们测定的结果,可以得出判断;抽出一片坏芯片,那剩下的好芯片就更多了,也可以得出判断。

因此,采取蛮力算法:随机选一片,判断其好坏,如果是坏的,重复进行;最坏的情况是每一片都与其他片比较一遍,需要 O(n^2) 。

(2)角度二:随机的两片比较有四种可能:

A片 B片

B好 A好 要么都好,要么都坏

B好 A坏 至少坏一片

B坏 A好 至少坏一片

B坏 A坏 至少坏一片

每种情况考虑四种情形:AB都好、 AB都坏 、一片好一片坏

第一种情况:设AB都好吻合,设AB都坏吻合,一片好一片坏矛盾,所以要么都好,要么都坏;

第二种情况:AB都好矛盾,AB都坏吻合,A坏B好吻合,A好B坏矛盾,所以至少坏一片;

其他两种情况同理,也是至少坏一片;

----------

如果我们给芯片随便两两配对测量(先假设芯片数是偶数),都报好的任选一片,其他情况的全不选,从而选出新子集,如果新子集还满足原来集合的性质,就可以逐步缩小筛选的范围,不用一片一片去比。这时满足表达式 W(n) = W(n/2) + O(n) ,可以看出这个就是典型的分治算法——寻找相同的子问题,将原来的大问题分解后递归求解。复杂度为:W(n) =O(n)

新子集还满足原来集合的性质?

原来集合的性质:好芯片比坏芯片多;由于选取的芯片组有两种类型:都是好的,都是坏的,可以知道好的芯片组的数目多于坏的芯片组的数目,因此子集中好芯片还是比坏芯片多,因此满足条件。

那如果芯片数目是奇数怎么办?

当遇到芯片数目是奇数时,将轮空的(未分到组的)芯片采取角度一方法判断,好的话,结束程序,坏的话就丢弃该芯片。

----------------------------

下面是伪码表示的算法:

n

while n > 3 then:

将 n 分为 n/2(默认向下取整) 组

if n是奇数 then:

轮空的芯片检测其好坏,好的话结束程序,坏的话丢弃

对每个分组进行元素抽取,测试结果都好的随机抽一个,其余的丢弃

n

if n == 3 then:

随机选取一片芯片比较一次

if 都好 then 选择一片

else 选择剩下的一片

if n<3 then:随机选择一片

return

写一个C++程序贴在这里

-------------------------------------------------------------------------------------------------------------------

2.快速排序:

10/20 待续。

-------

计算几何里面的一个例子:

1.求点集的最小凸包:

/*d[]是一个Point的数组,Point有两个两个属性x和y,同时支持减法操作和det(叉积)。convex数组保存被选中的凸包的点的编号,cTotal是凸包中点的个数*/

bool cmpPoint(const Point &a, const Point &b) //比较坐标序所用的比较函数{

if (a.x!=b.x) return a.x

return a.y

}

void get_convex_hull()

{

sort(d,d+N,cmpPoint);

int Total=0,tmp;

for (int i=0;i

while ( (Total>1) &&

((d[convex[Total-1]]-d[convex[Total-2]]).det( //获得凸包中最后两个点的向量 d[i]-d[convex[Total-1]])<=0) ) Total--; //获得准备插入的点和凸包中最后一点的向量,计算叉积 convex[Total++]=i;

}

tmp=Total;

for (int i=N-2;i>=0;--i) //扫描上凸壳 {

while ( (Total>tmp) &&

((d[convex[Total-1]]-d[convex[Total-2]]).det(

d[i]-d[convex[Total-1]])<=0) ) Total--;

convex[Total++]=i;

}

cTotal=Total;

}

2.作者:Milo Yip

链接:优化笔记:正n边形顶点 - Milo的编程 - 知乎专栏

来源:知乎

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

需求

要绘制圆形或圆盘时,通常需要用正n多边形去逼近圆形,那么要计算出正n多边形上的顶点。

优化前

对于 n 个顶点,算出角度

,然后直接计算

。这里我们用单精度浮点数及 Unity 的 Mathf 类。

void DrawCirlce(float r, int n)

{

float inc = 2.0f * Mathf.PI / n;

float alpha = 0.0f;

for (int i = 0; i < n; i++) {

float x = r * Mathf.Cos(alpha);

float y = r * Mathf.Sin(alpha);

/* Use (x, y) */

alpha += inc;

}

}

经性能检测,发现三角函数花了较多时间。

优化后

我们可以利用和角公式,用上一个迭代的结果(

)计算出本迭代的结果(

):

而且可以预先乘上半径:

void DrawCirlce(float r, int n)

{

float inc = 2.0f * Mathf.PI / n;

float y = 0.0f, x = r;

float sinb = Mathf.Sin(inc), cosb = Mathf.Cos(inc);

for (int i = 0; i < n; i++) {

/* Use (x, y) */

float ny = y * cosb + x * sinb;

float nx = x * cosb - y * sinb;

y = ny;

x = nx;

}

}

可以看出来,这其实等于不断旋转矢量。

但用这个方法,误差会累积,所以我们应该看一看误差的程度是否能接受。

设 r = 2.0f,n = {256, 1024, 4096},绘画优化前和代化后 x 的差。

可以看出 n 越大,误差越大。但这个误差范围对于我们的应用是可以接受的。而性能上,则从2个乘法、1个加法和2个三角函数求值,优化为4个乘法、1个加法和1个减法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值