元始天尊 发表于 2014-7-29 13:11:08

二维空间的最近点对问题图解(动态规划算法实现)

在网上搜了一遍,貌似没找到这种实际代码,于是自己用MFC写了一下,当然这种问题适合用图形方式来直观展示,整个搜索过程一目了然
采用的算法是动态规划,应用算法之前需要对所有点先按照横坐标排下序,这样有利于区域判定

效果展示:
红色代表已找到的最近点,蓝色表示正在找的点


核心代码:
// FindNearestPointDlg.cpp : implementation file
//

#include "stdafx.h"
#include "FindNearestPoint.h"
#include "FindNearestPointDlg.h"

#include <math.h>
#include <vector>
#include <queue>
#include <stdlib.h>
#include <time.h>

using namespace std;

#define distance(a,b) sqrt(((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)))
#define abs(a,b) ((a>b)?a-b:b-a)

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CFindNearestPointDlg dialog

int showdata::curminindex1=INT_MAX;
int showdata::curminindex2=INT_MAX;
float showdata::mindis=INT_MAX;

CFindNearestPointDlg::CFindNearestPointDlg(CWnd* pParent /*=NULL*/)
        : CDialog(CFindNearestPointDlg::IDD, pParent)
{
        //{{AFX_DATA_INIT(CFindNearestPointDlg)
        //}}AFX_DATA_INIT
        // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
        m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CFindNearestPointDlg::DoDataExchange(CDataExchange* pDX)
{
        CDialog::DoDataExchange(pDX);
        //{{AFX_DATA_MAP(CFindNearestPointDlg)
        DDX_Control(pDX, IDC_VELOCITY, m_velocity);
        DDX_Control(pDX, IDC_PTNUM, m_ptnum);
        DDX_Control(pDX, IDC_DRAW, m_draw);
        //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CFindNearestPointDlg, CDialog)
        //{{AFX_MSG_MAP(CFindNearestPointDlg)
        ON_WM_SYSCOMMAND()
        ON_WM_PAINT()
        ON_WM_QUERYDRAGICON()
        ON_BN_CLICKED(IDC_MAKE, OnMake)
        ON_WM_TIMER()
        ON_NOTIFY(NM_CUSTOMDRAW, IDC_VELOCITY, OnCustomdrawVelocity)
        //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CFindNearestPointDlg message handlers

BOOL CFindNearestPointDlg::OnInitDialog()
{
        CDialog::OnInitDialog();

        // Add "About..." menu item to system menu.

        // IDM_ABOUTBOX must be in the system command range.
        ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
        ASSERT(IDM_ABOUTBOX < 0xF000);

        CMenu* pSysMenu = GetSystemMenu(FALSE);
        if (pSysMenu != NULL)
        {
                CString strAboutMenu;
                strAboutMenu.LoadString(IDS_ABOUTBOX);
                if (!strAboutMenu.IsEmpty())
                {
                        pSysMenu->AppendMenu(MF_SEPARATOR);
                        pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
                }
        }

        // Set the icon for this dialog.The framework does this automatically
        //when the application's main window is not a dialog
        SetIcon(m_hIcon, TRUE);                        // Set big icon
        SetIcon(m_hIcon, FALSE);                // Set small icon
       
        // TODO: Add extra initialization here

        srand(time(NULL));
        SetTimer(1,200,NULL);
        m_ptnum.SetRange(10,1000);
        m_ptnum.SetPos(10);
        m_velocity.SetRange(200,1000);
        m_velocity.SetPos(200);

        return TRUE;// return TRUEunless you set the focus to a control
}

void CFindNearestPointDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
        CDialog::OnSysCommand(nID, lParam);
}

// If you add a minimize button to your dialog, you will need the code below
//to draw the icon.For MFC applications using the document/view model,
//this is automatically done for you by the framework.

void CFindNearestPointDlg::OnPaint()
{
        if (IsIconic())
        {
                CPaintDC dc(this); // device context for painting

                SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

                // Center icon in client rectangle
                int cxIcon = GetSystemMetrics(SM_CXICON);
                int cyIcon = GetSystemMetrics(SM_CYICON);
                CRect rect;
                GetClientRect(&rect);
                int x = (rect.Width() - cxIcon + 1) / 2;
                int y = (rect.Height() - cyIcon + 1) / 2;

                // Draw the icon
                dc.DrawIcon(x, y, m_hIcon);
        }
        else
        {
                CDialog::OnPaint();

                if(show.empty())
                        return;

                showdata& cur=show.front();

                CClientDC dc(&m_draw);
                RECT rt;
                CBrush clearbrush(RGB(255,255,255));
                CDC MemDC;
                CBitmap MemBitmap;
                m_draw.GetClientRect(&rt);
                MemDC.CreateCompatibleDC(&dc);
                MemBitmap.CreateCompatibleBitmap(&dc,rt.right,rt.bottom);
                MemDC.SelectObject(&MemBitmap);
               
                MemDC.FillRect(&rt,&clearbrush);

       
                CBrush normalbrush(RGB(0,0,0)),stressbrush(RGB(0,255,0,));
                CPen normalpen(PS_SOLID,1,RGB(0,0,0)),stresspen(PS_SOLID,2,RGB(0,255,0));
                MemDC.SelectObject(&normalbrush);

                for(int i=0;i<cur.points.size();i++)
                {
                        pt& curpt=cur.points.at(i);
                        MemDC.Ellipse(curpt.x-5,curpt.y-5,curpt.x+5,curpt.y+5);
                }

                if(cur.type == 1)
                {
                        MemDC.SelectObject(&stressbrush);
                        pt& pt1=cur.points.at(cur.data.line.index1);
                        pt& pt2=cur.points.at(cur.data.line.index2);
                        MemDC.Ellipse(pt1.x-5,pt1.y-5,pt1.x+5,pt1.y+5);
                        MemDC.Ellipse(pt2.x-5,pt2.y-5,pt2.x+5,pt2.y+5);
                        MemDC.SelectObject(stresspen);
                        MemDC.MoveTo(pt1.x,pt1.y);
                        MemDC.LineTo(pt2.x,pt2.y);
                }
                else if(cur.type == 2)
                {
                        MemDC.SelectObject(&stressbrush);
                        pt& pt1=cur.points.at(cur.data.rect.leftindex);
                        pt& pt2=cur.points.at(cur.data.rect.rightindex);
                        MemDC.Ellipse(pt1.x-5,pt1.y-5,pt1.x+5,pt1.y+5);
                        MemDC.Ellipse(pt2.x-5,pt2.y-5,pt2.x+5,pt2.y+5);
                        MemDC.SelectObject(stresspen);
                        MemDC.MoveTo(pt1.x,pt1.y);
                        MemDC.LineTo(pt2.x,pt2.y);       
                        MemDC.SelectObject(normalpen);
                        int d=cur.data.rect.d;
                        pt& mid=cur.points.at(cur.data.rect.midindex);
                        MemDC.MoveTo(mid.x,mid.y-d);
                        MemDC.LineTo(mid.x+d,mid.y-d);
                        MemDC.LineTo(mid.x+d,mid.y+d);
                        MemDC.LineTo(mid.x,mid.y+d);
                        MemDC.LineTo(mid.x,mid.y-d);
                }

                if(cur.mindis < INT_MAX)//如果有最小距离点
                {
                        CBrush b(RGB(255,0,0));
                        CPen p(PS_SOLID,2,RGB(255,0,0));
                        pt& pt1=cur.points.at(cur.curminindex1);
                        pt& pt2=cur.points.at(cur.curminindex2);
                        MemDC.SelectObject(b);
                        MemDC.SelectObject(p);
                        MemDC.Ellipse(pt1.x-5,pt1.y-5,pt1.x+5,pt1.y+5);
                        MemDC.Ellipse(pt2.x-5,pt2.y-5,pt2.x+5,pt2.y+5);
                        MemDC.MoveTo(pt1.x,pt1.y);
                        MemDC.LineTo(pt2.x,pt2.y);
                }

                if(show.size() > 1)
                        show.pop();

                dc.BitBlt(0,0,rt.right,rt.bottom,&MemDC,0,0,SRCCOPY);
                MemDC.DeleteDC();
                MemBitmap.DeleteObject();
        }
}

// The system calls this to obtain the cursor to display while the user drags
//the minimized window.
HCURSOR CFindNearestPointDlg::OnQueryDragIcon()
{
        return (HCURSOR) m_hIcon;
}

void CFindNearestPointDlg::OnMake()
{
        // TODO: Add your control notification handler code here
        data.clear();
        while(!show.empty())
                show.pop();

        showdata::curminindex1=INT_MAX;
        showdata::curminindex2=INT_MAX;
        showdata::mindis=INT_MAX;

        RECT rt;
        m_draw.GetClientRect(&rt);

        int i,j;
        int size=m_ptnum.GetPos();
        TRACE("width:%d height:%d\n",rt.right,rt.bottom);
        for(i=0;i<size;i++)
        {
                int x=rand()%rt.right,y=rand()%rt.bottom;
                data.push_back(pt(x,y));
        }

        //先排序
        for(i=0;i < size-1;i++)
        {
                for(j=size-1;j > i;j--)
                {
                        if(data.x < data.x)
                        {
                                int tmpx=data.x;
                                int tmpy=data.y;
                                data.x=data.x;
                                data.y=data.y;
                                data.x=tmpx;
                                data.y=tmpy;
                        }
                }
        }

        for(i=0;i<size;i++)
        {
                TRACE("%d:(%d,%d)\n",i,data.at(i).x,data.at(i).y);
        }

        show.push(showdata(data));
        FindNear(0,size-1);
        show.push(showdata(data));
        pt& pt1=data.at(showdata::curminindex1);
        pt& pt2=data.at(showdata::curminindex2);
        TRACE("min:(%d,%d)->(%d,%d)=%f\n",pt1.x,pt1.y,pt2.x,pt2.y,showdata::mindis);

        Invalidate(FALSE);
}

void CFindNearestPointDlg::OnTimer(UINT nIDEvent)
{
        // TODO: Add your message handler code here and/or call default
        Invalidate(FALSE);
       
        CDialog::OnTimer(nIDEvent);
}

float CFindNearestPointDlg::FindnearIntersect(float& curmin,int begin,int end)
{
        int mid=(end-begin)/2;
        //找到所有在中位数左侧且横坐标距离中位数横坐标小于curmin的点
        for(int i=mid;i >= begin;i--)
        {
                if(data.x - data.x > curmin)
                        break;
               
                //对于每一个这样的点寻找右侧可能的6个邻点
                for(int j=mid+1;j <= end;j++)
                {
                        if(data.x - data.x > curmin)
                                break;
                        if(abs(data.y,data.y) > curmin)
                                continue;
                       
                        show.push(showdata(i,j,mid,curmin,data,curmin));
                        //对于在2d*d矩形内的点
                        float dis=distance(data,data);
                        if(dis < curmin)
                        {
                                curmin=dis;
                                show.push(showdata(i,j,data,curmin));
                        }
                }
        }
        return curmin;
}

float CFindNearestPointDlg::FindNear(int begin,int end)
{
        if(begin == end)//如果只有1个点,则距离设为无穷大
                return INT_MAX;
        else if(begin+1 == end)//如果有2个点,则返回他们的距离
        {
                float dis=distance(data,data);
                show.push(showdata(begin,end,data,dis));
                return dis;
        }
       
        int mid=(end+begin)/2;
       
        //(begin,end)点的最小距离=min[(begin,mid)点的最小距离,(mid+1,end)点的最小距离,(begin,mid)右侧点和(mid+1,end)左侧点最小距离]
        float d1=FindNear(begin,mid);//(begin,mid)点的最小距离
        float d2=FindNear(mid+1,end);//(mid+1,end)点的最小距离
        float curmin=(d1<d2)?d1:d2;
       
        return FindnearIntersect(curmin,begin,end);//(begin,mid)右侧点和(mid+1,end)左侧点最小距离
}

void CFindNearestPointDlg::OnCustomdrawVelocity(NMHDR* pNMHDR, LRESULT* pResult)
{
        // TODO: Add your control notification handler code here
        KillTimer(1);
        SetTimer(1,m_velocity.GetPos(),NULL);
        *pResult = 0;
}

元始天尊 发表于 2014-7-29 13:11:36

可以调整点数和搜索速度
工程:
页: [1]
查看完整版本: 二维空间的最近点对问题图解(动态规划算法实现)