基于c#的两种最小凸包的生成(三硬币法与串行算法)

       对简单多边形P而言,其小凸包pc是这样一个多边形,如果存在包含p的另一多边形p0,则pc是p0的子集,因此pc是包含p的最小的凸多边形。

  1. 硬币算法

首先介绍的算法称为硬币算法,具有逻辑简单,容易实现,并且效率较高的优点。其描述如下:

1.找一个必在凸包上的点,通常取纵坐标最小的点,记为P0。
2.连结 P0 与其他点,按照顺时针方向对这些线排序。(左转判定:这是经典的计算几何学问题,判断向量 p1=(x1,y1)到 p2=(x2,y2)是否做左转,只需要判断 x1*y2-x2*y1 的正负,如果结果为正,则从 p1 到 p2 做左转。)
   现在就看看斯卡兰斯奇三硬币算法:
   1.预处理:将各点排序。 
   2.在 P0、P1、P2 上分别放置一枚硬币;
    把这三枚硬币分别命名为“后” 、 “中” 、 “前”。
   3.反复
    如果三枚硬币按“后-中-前”的顺序“做左转”
      拿起“后”,放在“前”的前面;
        将原先的“后”改名为“前” ;
        将原先的“前”改名为“中” ;
        将原先的“中”改名为“后” ;
      否则
        拿起“中”,放在“后”的后面;
        移除刚才“中”所在的点;
        将原先的“中”改名为“后” ;
        将原先的“后”改名为“中” ;
    直到“前”盖在 P0 上,且三枚硬币“做左转”
4.按照编号大小顺此连结剩下的点(编号最大的点连回 P0),
  得到的多边形就是给定点集的凸包。
算法实现:
private void coin()//硬币算法实现
        {
            myar.Clear();//每次进行对myar的数据进行清零,myar为存储硬币算法的点数据的ArrayList
            int p = 0;
            for (int i = 0; i < thear.Count; i++)
            {
                if (((zuobiao)thear[p]).y > ((zuobiao)thear[i]).y)
                //找出y最小的极限点,zuobiao为一个包含int x和int y字段的类
                    p = i;
            }
            myar.Add((zuobiao)thear[p]);
            //将这个点最先存储进myar中,thear为从文件中读取的点数据的Arraylist
            foreach (zuobiao zb in thear)
            {
                myar.Add(zb);//将其他数据加入myar中
            }
            canshu.p0x = ((zuobiao)thear[p]).x;//记录极限点的数据,canshu为记录参数的公共变量
            canshu.p0y = ((zuobiao)thear[p]).y;
            myar.RemoveAt(p + 1);//将极限点被重复存储的删去
            CoinSort coinsort = new CoinSort();//对点按顺时针方向排序
            myar.Sort(coinsort);
            int hou, zhong, qian;//hou,zhong,qian代表硬币“后”,“中”,“前”
            hou = 0;
            zhong = 1;
            qian = 2;
            while (qian < myar.Count)//“前”达到最末则停止
            {
                if (isright(myar[hou], myar[zhong], myar[qian]))
                //判断是否在“前”是否与“后”“中”形成右拐
                {
                    hou++;//形成则全部向前移动
                    zhong++;
                    qian++;
                }
                else
                {
                    myar.RemoveAt(zhong);
   //否则删除“中”,因为队列中少了一项,因此原先“前”所表示的次序成为现在“前”的下一个,需要减回来
                   zhong--;//“后”“中”向后退
                    qian--;
                    hou--;
                }
            }
            zuobiao temp = new zuobiao();
            zuobiao temp2 = new zuobiao();
            temp.x = canshu.p0x;
            temp.y = canshu.p0y;
            myar.Add(temp);
            temp2.x = ((zuobiao)myar[0]).x;
            temp2.y = ((zuobiao)myar[0]).y;
            myar.Add(temp2);//将极限点和最终点写在myar最后使绘图时构成闭合图形
       }
为了判断能否形成“右拐”,既“前”点是否在由“中”“后”两点形成的向量的右侧。
我提供的方法如下:
private bool isright(object a, object b, object c)//判断点是否在右侧
        {
            if (((((zuobiao)b).x - ((zuobiao)a).x) * (((zuobiao)c).y - ((zuobiao)a).y) - (((zuobiao)c).x - ((zuobiao)a).x) * (((zuobiao)b).y - ((zuobiao)a).y)) < 0)
                return true;
            else
                return false;
        }

2.串行算法

        串行算法的描述是:找出集中点x坐标值的最小值和最大值的两个极限点pmin和pmax,显然此二值极点位于土包边界上。用直线连接pmin和pmax,则点集中的点部分位于上方部分位于下方,显然点集的最小凸包右这两部分组成,既从pmin到pmax的上下凸壳。将两者相加就是点集的最小凸包。
        以计算上凸壳为例,下凸包算法相同。只考虑直线以上的点。上凸壳必然包含距离直线最远的居于直线上方的点,如果所有点都不在直线上方,则上凸壳由pmin和pmax直接确定。设位于直线上距离直线距离最大的点为pm,则类似的用直线连接pmin和pm以及pm和pmax,再次进行寻找线上距离最远点,若pmin到pm和pm到pmax都找不到,则凸壳由pmin,pm,pmax直接确定,否则,再重复以上步骤进行划分,直到所有直线上都不在有点,上凸壳就可以由这些点唯一确定。

上凸包算法如下:

 private void serialup(int a, int b)//计算上凸包
        {
            double maxdistence = -1;//初始化点到直线的最小距离为-1
            int maxnum = 0;//到直线距离最大点的编号
            int upnumber = 0;//在直线上的点数
            for (int i = 1; i < b - a; i++)
            {
                if (!(((zuobiao)myar[a + i]).y < ((zuobiao)myar[a]).y && ((zuobiao)myar[a + i]).y < ((zuobiao)myar[b]).y) && !isright(myar[a], myar[b], myar[a + i]))
                    //若点不在起终点下方并且在直线上方
                {
                    upnumber++;//计数器+1
                    double k = 1.0 * (((zuobiao)myar[b]).y - ((zuobiao)myar[a]).y) / (((zuobiao)myar[b]).x - ((zuobiao)myar[a]).x);//计算斜率
                    double nowdis = System.Math.Abs((((zuobiao)myar[a + i]).x) * k - ((zuobiao)myar[a + i]).y + ((zuobiao)myar[a]).y - k * ((zuobiao)myar[a]).x) / Math.Sqrt(k * k + 1);
                    //计算点到直线距离距离
                    if (nowdis > maxdistence)//记录最大距离和最远点编号
                    {
                        maxdistence = nowdis;
                        maxnum = a + i;
                    }
                }
            }
            if (upnumber > 1)//直线上不止一个点,则分割
            {
                serialup(a, maxnum);
                serialup(maxnum, b);
            }
            else if (upnumber == 1)//若直线上只有一个点,直接存储该点和终点
            {
                newar.Add((zuobiao)myar[maxnum]);
                newar.Add((zuobiao)myar[b]);
                return;
            }
            else//若直线上没有点,则直接存储终点
            {
                newar.Add((zuobiao)myar[b]);
                return;
            }
        }
 
 private void serialdown(int a, int b)//下凸包计算类似上凸包
        {
            double maxdistence = -1;
            int maxnum = 0;
            int upnumber = 0;
            for (int i = 1; i < b - a; i++)
            {
                if (!(((zuobiao)myar[a + i]).y > ((zuobiao)myar[a]).y && ((zuobiao)myar[a + i]).y > ((zuobiao)myar[b]).y) && isright(myar[a], myar[b], myar[a + i]))
                {
                    double k = 1.0 * (((zuobiao)myar[b]).y - ((zuobiao)myar[a]).y) / (((zuobiao)myar[b]).x - ((zuobiao)myar[a]).x);
                    double nowdis = System.Math.Abs((((zuobiao)myar[a + i]).x) * k - ((zuobiao)myar[a + i]).y + ((zuobiao)myar[a]).y - k * ((zuobiao)myar[a]).x) / Math.Sqrt(k * k + 1);
                    upnumber++;
                    if (nowdis > maxdistence)
                    {
                        maxdistence = nowdis;
                        maxnum = a + i;
                    }
                }
            }
            if (upnumber > 1)
            {
                serialdown(maxnum, b);//存点顺序同上凸包相反
                serialdown(a, maxnum);
            }
            else if (upnumber == 1)
            {
                newar.Add((zuobiao)myar[b]);
                newar.Add((zuobiao)myar[maxnum]);
                return;
            }
            else
            {
                newar.Add((zuobiao)myar[b]);
                return;
            }
        }

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值