closest pair 알고리즘 문제 관련 질문했던 사람입니다.
글쓴이: canuyes / 작성시간: 일, 2013/06/16 - 4:35오후
저번 질문에 코드가 너무 정리가 안되어 있어서 나름대로 좀더 정리해서 코드를 올려봅니다.
이번에는 100000개의 인풋에 대해 1초 안에 실행되기는 하지만, 메모리 128mb 메모리를 초과하네요...
제 코드에서 128mb 이내 1초 이내에 10만개의 인풋을 모두 처리하려면 어디를 개선해야 할까요?.
#include<iostream> #include<cstdlib> #include<cstring> #define MAX_DIST 1000000000 using namespace std; struct DOT{int x;int y;}; DOT xet[100000]; DOT yet[100000]; //정렬시에 사용될 함수입니다. inline int comp_x(const void* A,const void* B){return ((DOT*)A)->x-((DOT*)B)->x;} inline int comp_y(const void* A,const void* B){return ((DOT*)A)->y-((DOT*)B)->y;} //두 값중 작은 값을 리턴하는 함수입니다. inline int smaller(const int A,const int B){if(A<B){return A;}return B;} //두점 사이의 거리를 계산하는 함수입니다. inline int DIST(const DOT A,const DOT B){return ((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));} //점의 갯수가 8개 이하일때, 단순하게 O(n^2)으로 답을 구합니다. inline int brute_force(DOT* arr,int size){ int i,j,ret=MAX_DIST; for(i=0;i<size-1;i++){for(j=i+1;j<size;j++){ret=smaller(ret,DIST(arr[i],arr[j]));}} return ret; } //답을 만들어내는 함수입니다. inline int mkANS(DOT* xet,DOT* yet,int size){ int ret=MAX_DIST; //점의 갯수가 8개 이하일 경우 brute_force를 호출합니다. if(size<9){ret=brute_force(xet,size);} else{ //pivot에 x축 기준으로 가운댓 점의 x좌표를 대입합니다. int i,dist,L=-1,R=size,pivot=xet[size/2].x; //yyet를 생성하여 pivot보다 x좌표가 작은 점은 yyet의 왼쪽에, //pivot보다 x좌표가 큰점은 오른 쪽에 저장합니다. //이때, y축 기준으로 정렬된 yet 순차적으로 저장하므로 //yyet의 왼,오른 쪽은 자동으로 y축 오름 차순으로 정렬됩니다. DOT* yyet=new DOT[size]; for(i=0;i<size;i++){ if(yet[i].x-pivot<=0){yyet[++L]=yet[i];} else{yyet[--R]=yet[i];} } //왼쪽 점의 갯수,오른쪽 점의 갯수를 알아 내었으므로 왼쪽 오른 쪽에 대해 다시 재귀 호출합니다. ret=smaller(mkANS(xet,yet,L+1),mkANS(xet+L+1,yet+L+1,size-L-1)); //위에서 구해진 왼쪽 점끼리로만 만들어진 최소거리와 오른 쪽점들로만 만들어진 최소거리중 더 작은 값으로 부터 //pivot에서 위의 값이내에 들어오는 점들을 yyet에 왼쪽부터 차례로 대입합니다. //이때 역시 yet를 순차적으로 탐색하므로 yyet에는 자동적으로 y축에 대해 정렬된 값들이 저장됩니다. L=-1; for(i=0;i<size;i++){ dist=(yet[i].x-pivot)*(yet[i].x-pivot); if(dist>=ret){continue;} else{yyet[++L]=yet[i];} } //왼쪽, 오른쪽을 가로 지르는 경우, y축 오름차순으로 이웃하는 점끼리만 고려해주면 되므로 //아래의 비교를 수행합니다. for(i=0;i<=L-1;i++){ret=smaller(ret,DIST(yyet[i],yyet[i+1]));} delete []yyet; } return ret; } int main(void){ int i,N;cin>>N; for(i=0;i<N;i++){cin>>xet[i].x>>xet[i].y;} memcpy(yet,xet,sizeof(DOT)*N); //xet에 x축 기준으로 정렬하여 저장합니다. qsort(xet,N,sizeof(DOT),comp_x); //yet에 y축 기준으로 정렬하여 저장합니다. qsort(xet,N,sizeof(DOT),comp_y); cout<<mkANS(xet,yet,N)<<endl; return 0; }
나름대로 주석을 달아 봤습니다.
아래는 실행시간 1초 128mb 메모리 이내에서 실행된 정답 코드입니다.
제가 작성한 코드가 아니기에 링크 남깁니다.
http://rosettacode.org/wiki/Closest-pair_problem/C
제가 작성한 코드와 큰 차이는 없어보이는데, 어디서 이러한 차이가 발생하는 것인가요?
궁금합니다.
Forums:
알고리즘이 어떻게 돌아가는지 모르겠습니다. 단,
알고리즘이 어떻게 돌아가는지 모르겠습니다.
단, 올려주신 코드와 정답(?) 코드에 차이는
mkANS 함수에서 smaller/closest를 찾을 때 입니다.
정답 코드는 이분해서 좌우를 찾지만, 올려주신 코드는 항상 L=-1인 상태로 전체 검색을 하는거 같습니다.
그리고, 그 부분만 수정되면 올려주신 코드가 아마도 훨씬 빠를겁니다.
이유는 sqrt를 사용하지 않아서 입니다.
(물론, int를 사용하셔서 거리^2가 int의 범위를 넘지 않을까 걱정도 됩니다.)
댓글 달기