﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-一路向北-文章分类-算法</title><link>http://www.cppblog.com/deane/category/5616.html</link><description>        前进......</description><language>zh-cn</language><lastBuildDate>Wed, 13 May 2009 08:06:04 GMT</lastBuildDate><pubDate>Wed, 13 May 2009 08:06:04 GMT</pubDate><ttl>60</ttl><item><title>堆排序</title><link>http://www.cppblog.com/deane/articles/82548.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Mon, 11 May 2009 03:57:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/82548.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/82548.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/82548.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/82548.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/82548.html</trackback:ping><description><![CDATA[<p><br>1、 堆排序定义<br>&nbsp;&nbsp;&nbsp; 　n个关键字序列Kl，K2，&#8230;，Kn称为堆，当且仅当该序列满足如下性质(简称为堆性质)：<br>&nbsp;&nbsp;&nbsp; 　(1) ki&#8804;K2i且ki&#8804;K2i+1 或(2)Ki&#8805;K2i且ki&#8805;K2i+1(1&#8804;i&#8804;&nbsp; )</p>
<p>&nbsp;&nbsp;&nbsp; 　若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构，则堆实质上是满足如下性质的完全二叉树：树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。<br>【例】关键字序列(10，15，56，25，30，70)和(70，56，30，25，15，10)分别满足堆性质(1)和(2)</p>
<p>2、大根堆和小根堆<br>&nbsp;&nbsp;&nbsp; 　根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆。<br>&nbsp;&nbsp;&nbsp; 　根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者，称为大根堆。<br>&nbsp; 注意：<br>&nbsp;&nbsp;&nbsp; 　①堆中任一子树亦是堆。<br>&nbsp;&nbsp; 　 ②以上讨论的堆实际上是二叉堆(Binary Heap)，类似地可定义k叉堆。</p>
<p>3、堆排序特点<br>&nbsp;&nbsp;&nbsp; 　堆排序(HeapSort)是一树形选择排序。<br>&nbsp;&nbsp;&nbsp; 　堆排序的特点是：在排序过程中，将R[l..n]看成是一棵完全二叉树的顺序存储结构，利用完全二叉树中双亲结点和孩子结点之间的内在关系【参见二叉树的顺序存储结构】，在当前无序区中选择关键字最大(或最小)的记录。</p>
<p>4、堆排序与直接插入排序的区别<br>&nbsp;&nbsp;&nbsp; 　直接选择排序中，为了从R[1..n]中选出关键字最小的记录，必须进行n-1次比较，然后在R[2..n]中选出关键字最小的记录，又需要做n-2次 比较。事实上，后面的n-2次比较中，有许多比较可能在前面的n-1次比较中已经做过，但由于前一趟排序时未保留这些比较结果，所以后一趟排序时又重复执 行了这些比较操作。<br>&nbsp;&nbsp;&nbsp; 　堆排序可通过树形结构保存部分比较结果，可减少比较次数。</p>
<p>5、堆排序<br>&nbsp;&nbsp;&nbsp; 堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征，使得在当前无序区中选取最大(或最小)关键字的记录变得简单。</p>
<p>（1）用大根堆排序的基本思想<br>① 先将初始文件R[1..n]建成一个大根堆，此堆为初始的无序区<br>② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换，由此得到新的无序区R[1..n-1]和有序区R[n]，且满足R[1..n-1].keys&#8804;R[n].key<br>③ 由于交换后新的根R[1]可能违反堆性质，故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区 间的最后一个记录R[n-1]交换，由此得到新的无序区R[1..n-2]和有序区R[n-1..n]，且仍满足关系R[1..n-2].keys&#8804;R [n-1..n].keys，同样要将R[1..n-2]调整为堆。<br>&nbsp;&nbsp;&nbsp; &#8230;&#8230;<br>直到无序区只有一个元素为止。</p>
<p>（2）大根堆排序算法的基本操作：<br>① 初始化操作：将R[1..n]构造为初始堆；<br>② 每一趟排序的基本操作：将当前无序区的堆顶记录R[1]和该区间的最后一个记录交换，然后将新的无序区调整为堆(亦称重建堆)。<br>&nbsp; 注意：<br>①只需做n-1趟排序，选出较大的n-1个关键字即可以使得文件递增有序。<br>②用小根堆排序与利用大根堆类似，只不过其排序结果是递减有序的。堆排序和直接选择排序相反：在任何时刻，堆排序中无序区总是在有序区之前，且有序区是在原向量的尾部由后往前逐步扩大至整个向量为止。</p>
<p>（3）堆排序的算法：<br>&nbsp; void HeapSort(SeqIAst R)<br>&nbsp;&nbsp; { //对R[1..n]进行堆排序，不妨用R[0]做暂存单元<br>&nbsp;&nbsp;&nbsp; int i；<br>&nbsp;&nbsp;&nbsp; BuildHeap(R)； //将R[1-n]建成初始堆<br>&nbsp;&nbsp;&nbsp; for(i=n;i&gt;1；i--){ //对当前无序区R[1..i]进行堆排序，共做n-1趟。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R[0]=R[1]；R[1]=R[i];R[i]=R[0]； //将堆顶和堆中最后一个记录交换<br>　&nbsp;&nbsp;&nbsp; Heapify(R，1，i-1)； //将R[1..i-1]重新调整为堆，仅有R[1]可能违反堆性质<br>&nbsp;&nbsp;&nbsp;&nbsp; } //endfor<br>&nbsp;&nbsp; } //HeapSort</p>
<p>（4） BuildHeap和Heapify函数的实现<br>　因为构造初始堆必须使用到调整堆的操作，先讨论Heapify的实现。<br>① Heapify函数思想方法<br>　 每趟排序开始前R[l..i]是以R[1]为根的堆，在R[1]与R[i]交换后，新的无序区R[1..i-1]中只有R[1]的值发生了变化，故除R [1]可能违反堆性质外，其余任何结点为根的子树均是堆。因此，当被调整区间是R[low..high]时，只须调整以R[low]为根的树即可。<br>"筛选法"调整堆<br>　 R[low]的左、右子树(若存在)均已是堆，这两棵子树的根R[2low]和R[2low+1]分别是各自子树中关键字最大的结点。若R[low]. key不小于这两个孩子结点的关键字，则R[low]未违反堆性质，以R[low]为根的树已是堆，无须调整；否则必须将R[low]和它的两个孩子结点 中关键字较大者进行交换，即R[low]与R[large](R[large].key=max(R[2low].key，R[2low+1]. key))交换。交换后又可能使结点R[large]违反堆性质，同样由于该结点的两棵子树(若存在)仍然是堆，故可重复上述的调整过程，对以R [large]为根的树进行调整。此过程直至当前被调整的结点已满足堆性质，或者该结点已是叶子为止。上述过程就象过筛子一样，把较小的关键字逐层筛下 去，而将较大的关键字逐层选上来。因此，有人将此方法称为"筛选法"。<br>&nbsp; 　 具体的算法【参见教材】</p>
<p>②BuildHeap的实现<br>　　要将初始文件R[l..n]调整为一个大根堆，就必须将它所对应的完全二叉树中以每一结点为根的子树都调整为堆。<br>　　显然只有一个结点的树是堆，而在完全二叉树中，所有序号 的结点都是叶子，因此以这些结点为根的子树均已是堆。这样，我们只需依次将以序号为 ，&nbsp; -1，&#8230;，1的结点作为根的子树都调整为堆即可。<br>&nbsp;&nbsp; 　 具体算法【参见教材】。</p>
<p>5、大顶堆排序实现</p>
<p>&nbsp;</p>
<p>void HeapSort ( Elem R[], int n ) ...{<br>// 对记录序列R[1..n]进行堆排序。<br>for ( i=n/2; i&gt;0; --i )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 把R[1..n]建成大顶堆<br>&nbsp;&nbsp; HeapAdjust ( R, i, n );<br>for ( i=n; i&gt;1; --i ) ...{<br>R[1]&#8592;&#8594;R;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 将堆顶记录和当前未经排序子序列<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // R[1..i]中最后一个记录相互交换<br>HeapAdjust(R, 1, i-1);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 将R[1..i-1] 重新调整为大顶堆<br>}<br>} // HeapSort<br>其中筛选的算法如下所示。为将R[s..m]调整为&#8220;大顶堆&#8221;，算法中&#8220;筛选&#8221;应沿关键字较大的孩子结点向下进行。<br>void HeapAdjust (Elem R[], int s, int m) ...{<br>// 已知R[s..m]中记录的关键字除R[s].key之<br>// 外均满足堆的定义，本函数调整R[s] 的关<br>// 键字，使R[s..m]成为一个大顶堆（对其中<br>// 记录的关键字而言）<br>rc = R[s];<br>for ( j=2*s; j&lt;=m; j*=2 ) ...{// 沿key较大的孩子结点向下筛选<br>&nbsp;&nbsp;&nbsp; if ( j&lt;m &amp;&amp; R[j].key&lt;R[j+1].key)&nbsp; ++j;<br>&nbsp;&nbsp;&nbsp; if ( rc.key &gt;= R[j].key )&nbsp; break; // rc应插入在位置s上<br>&nbsp;&nbsp;&nbsp; R[s] = R[j];&nbsp; s = j;<br>}<br>&nbsp;&nbsp;&nbsp; R[s] = rc; // 插入<br>} // HeapAdjust </p>
<p>6、 算法分析<br>&nbsp;&nbsp;&nbsp; 　堆排序的时间，主要由建立初始堆和反复重建堆这两部分的时间开销构成，它们均是通过调用Heapify实现的。<br>&nbsp; 　&nbsp; 堆排序的最坏时间复杂度为O(nlgn)。堆排序的平均性能较接近于最坏性能。<br>&nbsp;&nbsp;&nbsp; 　由于建初始堆所需的比较次数较多，所以堆排序不适宜于记录数较少的文件。<br>&nbsp;&nbsp;&nbsp; 　堆排序是就地排序，辅助空间为O(1)，<br>&nbsp;&nbsp;&nbsp; 　它是不稳定的排序方法。</p>
<p>&nbsp;<br></p>
<img src ="http://www.cppblog.com/deane/aggbug/82548.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2009-05-11 11:57 <a href="http://www.cppblog.com/deane/articles/82548.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>图形包含判定算法</title><link>http://www.cppblog.com/deane/articles/38111.html</link><dc:creator>李阳</dc:creator><author>李阳</author><pubDate>Sun, 09 Dec 2007 12:26:00 GMT</pubDate><guid>http://www.cppblog.com/deane/articles/38111.html</guid><wfw:comment>http://www.cppblog.com/deane/comments/38111.html</wfw:comment><comments>http://www.cppblog.com/deane/articles/38111.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/deane/comments/commentRss/38111.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/deane/services/trackbacks/38111.html</trackback:ping><description><![CDATA[<p align=justify><font face=楷体_GB2312 size=4><font size=3>&nbsp;&nbsp;<br></font>&nbsp;&nbsp;&nbsp;&nbsp; 在进行图形求交时，常常需要判定两个图形间是否有包含关系。如点是否包含在线段、平面区域、三维形体中，线段是否包含在平面区域、三维形体中，等等。许多包含判定问题可转化为点的包含判定问题，如判断线段是否在平面上可以转化为判断其两端点是否在平面上。因此下面主要讨论关于点的包含判定算法。</font></p>
<p align=justify><font face=楷体_GB2312 size=4>判断点与线段的包含关系，也就是判断点与线的最短距离是否位于容差范围内。造型中常用的线段有三种：</font></p>
<p align=justify><font face=楷体_GB2312 size=4>（1）直线段，（2）圆锥曲线段（主要是圆弧），（3）参数曲线（主要是Bezier，B样条与NURBS曲线）。点与面的包含判定也类似地分为三种情况。下面分别讨论。</font></p>
<p align=justify><font face=楷体_GB2312 size=4>1、点与直线段的包含判定</font></p>
<p align=justify><font face=楷体_GB2312 size=4>假设点坐标为P(x, y, z)，直线段端点为P<sub>1</sub>(x<sub>1</sub>, y<sub>1</sub>, z<sub>1</sub>)，P<sub>2</sub>(x<sub>2</sub>,y<sub>2</sub>,z<sub>2</sub>)，则点P到线段P<sub>1</sub>P<sub>2</sub>的距离的平方为</font></p>
<p align=justify><font face=楷体_GB2312 size=4>d<sup>2</sup>=(x-x<sub>1</sub>)<sup>2</sup>+(y-y<sub>1</sub>)<sup>2</sup>+(z-z<sub>1</sub>)<sup>2</sup>-[(x<sub>2</sub>-x<sub>1</sub>)(x-x<sub>1</sub>)+(y<sub>2</sub>-y<sub>1</sub>)(y-y<sub>1</sub>)+(z<sub>2</sub>-z<sub>1</sub>)(z-z<sub>1</sub>)]<sup>2</sup>/[(x<sub>2</sub>-x<sub>1</sub>)<sup>2</sup>+(y<sub>2</sub>-y<sub>1</sub>)<sup>2</sup>+(z<sub>2</sub>-z<sub>1</sub>)<sup>2</sup>]</font></p>
<p align=justify><font face=楷体_GB2312 size=4>当d<sup>2</sup>&lt;a<sup>2</sup>时，认为点在线段（或其延长线）上，这时还需进一步判断点是否落在直线段的有效区间内。只要对坐标分量进行比较，假设线段两端点的x分量不等（否则所有分量均相等，那么线段两端点重合，线段退化为一点），那么当x-x<sub>1</sub>与x-x<sub>2</sub>反号时，点P在线段的有效区间内。</font></p>
<p align=justify><font face=楷体_GB2312 size=4>2、点与圆锥曲线段的包含判定</font></p>
<p align=justify><font face=楷体_GB2312 size=4>以圆弧为例，假设点的坐标为(x, y, z)，圆弧的中心为（x<sub>0</sub>, y<sub>0</sub>, z<sub>0</sub>），半径为r，起始角a<sub>1</sub>，终止角a<sub>2</sub>。这些角度都是相对于局部坐标系x轴而言。圆弧所在平面为</font></p>
<p align=justify><font face=楷体_GB2312 size=4>　　　　ax+by+cz+d=0</font></p>
<p align=justify><font face=楷体_GB2312 size=4>先判断点是否在该平面上，若不在，则点不可能被包含。若在，则通过坐标变换，把问题转换到二维的问题。</font></p>
<p align=justify><font face=楷体_GB2312 size=4>给定中心为（x<sub>0</sub>, y<sub>0</sub>），半径为r，起始角a<sub>1</sub>，终止角a<sub>2</sub>的圆弧，对平面上一点P（x, y），判断P是否在圆弧上，可分二步进行。第一步判断P是否在圆心为（x<sub>0</sub>, y<sub>0</sub>），半径为r的圆的圆周上，即下式是否成立：</font></p>
<p align=justify><font face=楷体_GB2312 size=4>　　　　<img height=29 src="file:///F:/软件开发/游戏开发/数学/2.5.3包含判定算法.files/Image99.gif" width=197></font></p>
<p align=justify><font face=楷体_GB2312 size=4>第二步判断P是否在有效的圆弧段内。</font></p>
<p align=justify><font face=楷体_GB2312 size=4>3、点与参数曲线的包含判定</font></p>
<p align=justify><font face=楷体_GB2312 size=4>设点坐标为P(x, y, z)，参数曲线为Q(t)=(x(t), y(t), z(t))。点也参数曲线的求交计算包括三个步骤：</font></p>
<p align=justify><font face=楷体_GB2312 size=4>（1）计算参数t值，使P到Q(t)的距离最小；</font></p>
<p align=justify><font face=楷体_GB2312 size=4>（2）判断t是否在有效参数区间内（通常为[0，1]）；</font></p>
<p align=justify><font face=楷体_GB2312 size=4>（3）判断Q(t)与P的距离是否小于a 。若（2），（3）步的判断均为&#8220;是&#8221;，则点在曲线上；否则点不在曲线上。</font></p>
<p align=justify><font face=楷体_GB2312 size=4>第一步应计算t，使得|P-Q(t)|最小，</font></p>
<p align=justify><font face=楷体_GB2312 size=4>即 R(t)=(P-Q(t))(P-Q(t))=|P-Q(t)|<sup>2</sup>最小</font></p>
<p align=justify><font face=楷体_GB2312 size=4>根据微积分知识，在该处R'(t)=0即</font></p>
<p align=justify><font face=楷体_GB2312 size=4>Q'(t)[P-Q(t)]=0</font></p>
<p align=justify><font face=楷体_GB2312 size=4>用数值方法解出t值，再代入曲线参数方程可求出曲线上对应点的坐标。第（2）、（3）步的处理比较简单，不再详述。</font></p>
<p align=justify><font face=楷体_GB2312 size=4>4、点与平面区域的包含判定</font></p>
<p align=justify><font face=楷体_GB2312 size=4>设点坐标为P(x, y, z)，平面方程为ax+by+cz+d=0。则点到平面的距离为</font></p>
<p align=justify><font face=楷体_GB2312 size=4>　　　　d=<img height=45 src="file:///F:/软件开发/游戏开发/数学/2.5.3包含判定算法.files/Image100.gif" width=113></font></p>
<p align=justify><font face=楷体_GB2312 size=4>若d&lt;a ，则认为点在平面上，否则认为点不在平面上。在造型系统中，通常使用平面上的有界区域作为形体的表面。在这种情况下，对落在平面上的点还应进一步判别它是否落在有效区域内。若点落在该区域内，则认为点与该面相交，否则不相交。下面以平面区域多边形为例，介绍有关算法。</font></p>
<p align=justify><font face=楷体_GB2312 size=4>判断平面上一个点是否包含在同平面的一个多边形内，有许多种算法，这里仅介绍常用的三种：叉积判断法、夹角之和检验法以及交点计数检验法。</font></p>
<p align=justify><font face=楷体_GB2312 size=4>（1）叉积判断法</font></p>
<p align=justify><font face=楷体_GB2312 size=4>假设判断点为P<sub>0</sub>。多边形顶点按顺序排列为P<sub>1</sub>P<sub>2</sub>&#8230;P<sub>n</sub>。如图2.5.2所示。令V<sub>i</sub>=P<sub>i</sub>-P<sub>0</sub>, i=1, 2, &#8230;, n, V<sub>n+1</sub>=V<sub>1</sub>。</font></p>
<p align=justify><font face=楷体_GB2312 size=4>那么，P<sub>0</sub>在多边形内的充要条件是叉积V<sub>i</sub>XV<sub>i+1</sub>(i=1, 2, &#8230;, n)的符号相同。叉积判断法仅适用于凸多边形。当多边形为凹时，尽管点在多边形内也不能保证上述叉积符号都相同。这时可采用后面介绍的两种方法。</font></p>
<p align=center><font face=楷体_GB2312 size=4><img height=268 alt="2_5_2.gif (4875 bytes)" src="file:///F:/软件开发/游戏开发/数学/2.5.3包含判定算法.files/2_5_2.gif" width=607></font></p>
<p align=center><font face=楷体_GB2312 size=4>图2.5.2 叉积判断法</font></p>
<p align=justify><font face=楷体_GB2312 size=4>（2）夹角之和检验法</font></p>
<p align=justify><font face=楷体_GB2312 size=4>假设某平面上有点P<sub>0</sub>和多边形P<sub>1</sub>P<sub>2</sub>P<sub>3</sub>P<sub>4</sub>P<sub>5</sub>，如图2.5.3所示。将点P<sub>0</sub>分别与P<sub>i</sub>相连，构成向量V<sub>i</sub>=P-P<sub>0</sub>。假设角 P<sub>i</sub>P<sub>0</sub>P<sub>i+1</sub>=a<sub>i</sub>。如果<img height=45 src="file:///F:/软件开发/游戏开发/数学/2.5.3包含判定算法.files/Image101.gif" width=38>=0，则点P<sub>0</sub>在多边形之外，如图2.5.3(a)所示。如果<img height=45 src="file:///F:/软件开发/游戏开发/数学/2.5.3包含判定算法.files/Image102.gif" width=38>=2&#960;，则点P<sub>0</sub>在多边形之内，如图2.5.3(b)所示。a<sub>i</sub>可通过下列公式计算：令S<sub>i</sub>=V<sub>i? </sub>V<sub>i+1</sub>, C<sub>i</sub>=V<sub>i</sub>&#183;V<sub>i+1</sub>，则tg(a<sub>i</sub>)=S<sub>i</sub>/C<sub>i</sub>，所以a<sub>i</sub>=arctg(S<sub>i</sub>/C<sub>i</sub>)且</font></p>
<p><font face=楷体_GB2312 size=4>a<sub>i</sub>的符号即代表角度的方向。</font></p>
<p align=center><font face=楷体_GB2312 size=4><img height=236 alt="2_5_3.gif (5201 bytes)" src="file:///F:/软件开发/游戏开发/数学/2.5.3包含判定算法.files/2_5_3.gif" width=529></font></p>
<p align=center><font face=楷体_GB2312 size=4>图2.5.3 夹角之和检验法</font></p>
<p align=justify><font face=楷体_GB2312 size=4>在多边形边数不太多（&lt;44）的情况下，可以采用下列近似公式计算a<sub>i</sub>。</font></p>
<p><font face=楷体_GB2312 size=4><img height=45 src="file:///F:/软件开发/游戏开发/数学/2.5.3包含判定算法.files/Image103.gif" width=94>　　　　当 |S<sub>i</sub>|&#8804;|C<sub>i</sub>|</font></p>
<p><font face=楷体_GB2312 size=4><img height=45 src="file:///F:/软件开发/游戏开发/数学/2.5.3包含判定算法.files/Image104.gif" width=122>　　当 |S<sub>i</sub>|&gt;|C<sub>i</sub>|</font></p>
<p align=justify><font face=楷体_GB2312 size=4>其中d=0.0355573为常数。当<sub>&#931;&#945;i&#8805;&#960;</sub></font></p>
<p align=justify><font face=楷体_GB2312 size=4>时，可判P<sub>0</sub>在多边形内。当<sub>&#931;&#945;i＜&#960;</sub> 时，可判P<sub>0</sub>在多边形外。证明略。</font></p>
<p align=justify><font face=楷体_GB2312 size=4>（3）交点计数检验法</font></p>
<p align=justify><font face=楷体_GB2312 size=4>当多边形是凹多边形，甚至还带孔时，可采用交点计数法判断点是否在多边形内。具体做法是，从判断点作一射线至无穷远：</font><font face=System></p>
</font>
<p><font face=楷体_GB2312 size=4><img height=49 src="file:///F:/软件开发/游戏开发/数学/2.5.3包含判定算法.files/Image105.gif" width=121></font></p>
<p align=justify><font face=楷体_GB2312 size=4>求射线与多边形边的交点个数。若个数为奇数，则点在多边形内，否则，点在多边形外。</font></p>
<p align=justify><font face=楷体_GB2312 size=4>如图2.5.4所示，射线a, c分别与多边形交于二点和四点，为偶数，故判断点A，C在多边形外。而射线b, d与多边形交三点和一点，为奇数，所以B，D在多边形内。</font></p>
<p align=justify><font face=楷体_GB2312 size=4>当射线穿过多边形顶点时，必须特殊对待。如图2.5.4所示，射线f过顶点，若将交点计数为2，则会错误地判断F在多边形外。但是，若规定射线过顶点时，计数为1，则又会错误地判断点E在多边形内。正确的方法是，若共享顶点的两边在射线的同一侧，则交点计数加2，否则加1。按这种方法，E点计为2，所以在多边形外；F点计数为１，所以在多边形内。读者可以自己另取一些点来验证。</font></p>
<p align=center><font face=楷体_GB2312 size=4><img height=216 alt="2_5_4.gif (3218 bytes)" src="file:///F:/软件开发/游戏开发/数学/2.5.3包含判定算法.files/2_5_4.gif" width=287></font></p>
<p align=center><font face=楷体_GB2312 size=4>图2.5.4 交点计数法</font></p>
<p align=justify><font face=楷体_GB2312 size=4>5、点与二次曲面/参数曲面的包含判定</font></p>
<p align=justify><font face=楷体_GB2312 size=4>假设点坐标为P(x<sub>0</sub>, y<sub>0</sub>, z<sub>0</sub>)，二次曲面方程为Q(x, y, z)=0，则当|Q(x<sub>0</sub>, y<sub>0</sub>, z<sub>0</sub>)|&lt;? 时，认为点在该二次曲面上，在造型系统中，通常使用裁剪的二次曲面。在这种情况下，还要判断点是否在有效范围内。裁剪的二次曲面通常用有理Bezier或有理B样条的参数空间上的闭合曲线来定义曲面的有效范围，故要把点所对应的参数空间的参数坐标计算出来，再判断该参数坐标是否在参数空间有效区域上。</font></p>
<p align=justify><font face=楷体_GB2312 size=4>6、点与三维形体的包含判定</font></p>
<p align=justify><font face=楷体_GB2312 size=4>判断点是否被三维形体所包含，可先用前面的方法判断点是否在三维形体的表面上，然后判断点是否在形体内部，其方法因形体不同而异。下面以凸多面体为例说明。</font></p>
<p align=justify><font face=楷体_GB2312 size=4>设凸多面体某个面的平面方程为ax+by+cz+d=0，调整方程系数的符号，使当ax+by+cz+d&lt;0时，点(x,y,z)位于该平面两侧中包含该凸多面体的一侧。于是要检验一个点是否在凸多面体内部，只要检验是否它对凸多面体的每一个面均满足以上的不等式即可。</font></p>
<img src ="http://www.cppblog.com/deane/aggbug/38111.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/deane/" target="_blank">李阳</a> 2007-12-09 20:26 <a href="http://www.cppblog.com/deane/articles/38111.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>