closest pair 알고리즘 문제 관련 질문입니다.

canuyes의 이미지

분할정복을 이용한 closest pair를 공부중인 학생입니다.
고민 끝에 분할 정복을 이용하여 구현을 하기는 했습니다만,
원하는 실행시간을 얻지 못해 질문 올립니다.
100000(일십만)개의 인풋에 대해 1초 이내의 실행시간을 얻고 싶은데, 잘구현이 되질 않습니다.
아마도 제가 지금 비효율적으로 하고 있는 부분이
"분할된 왼쪽 평면과 오른쪽 평면을 가로지르는 직선의 값을 구하는 과정"
인것 같습니다.
discrete mathematics 6E, introduction to algorithm 3E , 구글링 등 여러가지를 참고해봤지만,
10시간째 실패해오고 있습니다.
제가 어려워하고 있는 부분에 대해 참고할 만한 문헌이나,
명쾌한 답변을 기다립니다..
감사합니다.

p.s.

CLOSEST PAIR

좌표 평면에 N개의 점이 주어질 때,이 점들중 가장 가까운 점들간의 거리를 구하는 것.

ex)
input : (0,0) (10,10) (0,10) (10,0) 4개의 점.
output : 10

익명 사용자의 이미지

10만개밖에 안되는데 n^2 + n / 2 알고리즘으로 1초에 안 나오나요?

canuyes의 이미지

아주 나이브한 추정이긴 하지만,
연산횟수가 100000000(일억) 회를 넘어가면 실행시간이 1초를 넘어가지 않나요?

snowall의 이미지

일단 시도해본 알고리즘을 올려주세요

피할 수 있을때 즐겨라! http://melotopia.net/b

canuyes의 이미지

최대한 주석은 달아보았지만..제가 봐도 너무 난잡합니다 ㅠㅠ 죄송합니다.
함수 mkSQUARE부분을 중점적으로 알려주신다면 감사 하겠습니다.
(실제로 문제를 가지고 있다고 생각하는 부분입니다.)

/*
dev cpp 4.9.9.2 에서 작성했습니다.
편의상 전체 평면을 P, 분할된 좌측 평면을 PL, 우측평면을 PR이라고 하겠습니다.
*/ 
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
//두점간의 거릴를 구하는 함수를 매크로함수로 구현했습니다. 
#define DIST(X,Y,P,Q) ((((X)-(P))*((X)-(P)))+(((Y)-(Q))*((Y)-(Q)))) 
//절대값을 반환하는 함수 입니다. MINOR(PL의 최소거리,PR의 최소거리) 안에 있는 square을 만들때 사용합니다. 
#define ABSOLUTE(A) ((A)<(0))?(-1*(A)):(A)  
//두값중 작은 값을 반환하는 함수입니다.
#define MINOR(A,B) ((A)<(B))?(A):(B) 
using namespace std;
struct POINT{long long xpos;long long ypos;};
 
long long CNT=0;
 
//점이 저장될 변수입니다. 
vector<POINT> SET;
//PL에서 1점, PR에서 한점을 뽑을때 그 점들이 위치할 수 있는 집합을 저장할 변수입니다. 
vector<POINT> SQUARE;
long long MIN=1000000000;
//SET을 x좌표 오름 차순으로 정렬하기 위한 함수 입니다.
bool comp(const POINT P1,const POINT P2){return P1.xpos<P2.xpos;} 
 
//PL,PR을 가로지르며 생성되는 직선을 조사해볼 집합을 생성하는 함수입니다.
//현재 점의 인덱스(CDEX)가 주어졌을때, CDEX로 부터 상하좌우로 MINOR(PL의 최소거리,PR의 최소거리)내의 점들을 조사하여 square에 삽입합니다. 
void mkSQUARE(int current,double min){
     int i,len=SET.size();
     for(i=current-1;i>=0;i--){
                               if((SET[current].xpos-SET[i].xpos)>min){break;}
                               if(ABSOLUTE(SET[current].ypos-SET[i].ypos)<=min){SQUARE.push_back(SET[i]);}
     }
     for(i=current+1;i<len;i++){
                                if((SET[i].xpos-SET[current].xpos)>min){break;}
                                if(ABSOLUTE(SET[i].ypos-SET[current].ypos)<=min){SQUARE.push_back(SET[i]);}
     }
     return;
}
//구해진 square에 대해 최소거리를 brute force로 계산합니다. 
void combi(int N){
     int i,j;
     for(i=0;i<N-1;i++){
                        for(j=i+1;j<N;j++){
                                           MIN=MINOR(MIN,DIST(SQUARE[i].xpos,SQUARE[i].ypos,SQUARE[j].xpos,SQUARE[j].ypos));
     }}
     return;
} 
 
//SET의 원소가 3개 이하일경우 BRUTEFORCE를 사용합니다. 
long long small(int LDEX,int RDEX){
     long long ret=1000000000;
     if((RDEX-LDEX)==1){ret=MINOR(ret,DIST(SET[LDEX].xpos,SET[LDEX].ypos,SET[LDEX+1].xpos,SET[LDEX+1].ypos));}
     if((RDEX-LDEX)==2){
                        ret=MINOR(ret,DIST(SET[LDEX].xpos,SET[LDEX].ypos,SET[LDEX+1].xpos,SET[LDEX+1].ypos));
                        ret=MINOR(ret,DIST(SET[LDEX+2].xpos,SET[LDEX+2].ypos,SET[LDEX+1].xpos,SET[LDEX+1].ypos));
     }
     return ret;
}
//답을 출력하는 함수입니다.
//구간의 시작 인덱스 LDEX와 끝 인덱스 RDEX를 매개변수로 취합니다. 
long long mkANS(int LDEX,int RDEX){
     long long ret=1000000000;
     //SET의 원소가 3개 이하인 경우 small을 호출합니다. 
     if((RDEX-LDEX)<3){return small(LDEX,RDEX);}
     //SET의 원소가 3개보다 많을때 다음을 호출합니다. 
     else if(LDEX<RDEX){
                        //ret에 MINOR(PL의 최소거리,PR의 최소거리)을 저장합니다. 
                        ret=MINOR(mkANS(LDEX,(LDEX+RDEX)/2),mkANS((LDEX+RDEX)/2+1,RDEX));
                        MIN=1000000000;SQUARE.clear();
                        //가운데 점의 인덱스 (LDEX+RDEX)/2와 MINOR(PL의 최소거리,PR의 최소거리)인 sqrt(ret)을 이용하여 SQUARE을 구성합니다. 
                        mkSQUARE((LDEX+RDEX)/2,sqrt(ret));
                        //구해진 square에 대해 brute force로 최소값을 구합니다. 
                        combi(SQUARE.size());
                        ret=MINOR(ret,MIN);
     }
     return ret;
}
int main(void){
    int i,N;
    POINT temp;
    cin>>N;
    for(i=0;i<N;i++){
                     cin>>temp.xpos>>temp.ypos;
                     SET.push_back(temp);
    }
    //입력받은 SET을 x오름 차순으로 정렬합니다. 
    sort(SET.begin(),SET.end(),comp);
    cout<<mkANS(0,SET.size()-1)<<endl;
    system("PAUSE");
    return 0;
}
gilgil의 이미지

"분할된 왼쪽 평면과 오른쪽 평면을 가로지르는 직선의 값을 구하는 과정" 이라고 하셨으니 divide and conquer 방식을 사용하신 거 같은데, recursive 방식을 사용했으면 O(N log N)이겠네요.

10만개라면 시간이 많이 걸리지는 않을 거 같은데요. 코드를 보여 주셔야 할 듯...

canuyes의 이미지

snowall님에게 단 댓글을 참고해주시면 감사하겠습니다 ㅠㅠ

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.