레이블이 C언어인 게시물을 표시합니다. 모든 게시물 표시
레이블이 C언어인 게시물을 표시합니다. 모든 게시물 표시

2006년 12월 21일

고른 표본 추출을 통한 빠른 정렬

고른 표본 추출을 통한 병렬 정렬(Parallel Sorting by Regular Sampling: PSRS)은 n개의 프로세스에서 골고루 표본을 추출하여 이것을 주축으로 하여 값들을 정렬하는 병렬 정렬 알고리즘의 일종입니다. 균형이 잘 잡히고, 프로세스의 수가 2의 n제곱 꼴이 되지 않아도 된다는 등의 장점이 있습니다.

MPI를 배우면서 이것을 한번 C+MPI로 구현해 보았습니다. p개의 노드에서 n/p개만큼의 [0..1) 범위의 float형 난수를 발생한 다음에 정렬을 하는 과정입니다. 잘 작성된 코드는 아니지만, 참고할 수 있는 코드일 수도 있어서 실어 봅니다.

  1 /**
  2  * PSRS implementation using MPI.
  3  *
  4  * Date: 5th of December, 2006
  5  * Author: Jay
  6  *
  7  * Compile options:
  8  *     PRINT_MSG: print some messages to stdout.
  9  *     OUTPUT_FILE: write 'glist_nn.txt', and 'slist_nn.txt'.
 10  *                  'glist_nn' contains sorted generated numbers
 11  *                  of each node. 'slist_nn' is final result
 12  *                  of each node.
 13  */
 14 #include <stdlib.h>
 15 #include <stdio.h>
 16 #include <mpi.h>
 17 #include <limits.h>
 18 #include <time.h>
 19 #include <stddef.h>
 20 #include <string.h>
 21 
 22 /* Upper bound of generated floating point */
 23 #define UPPER_BOUND 1.0
 24 
 25 /* float comparision function */
 26 int float_comp(const void* aa, const void* bb)
 27 {
 28     const float* a = aa;
 29     const float* b = bb;
 30     if (*a == *b) return 0;
 31     if (*a < *b) return -1;
 32     return 1;
 33 }
 34 
 35 /* Generates random sequence into array A with size */
 36 void generate_random_sequence(float* A, size_t size)
 37 {
 38     size_t i;
 39     for (i=0; i<size; i++)
 40         A[i] = (float)rand()/RAND_MAX;
 41 }
 42 
 43 /* Returns pointer to the lower_bound,
 44  * which means the lowest position
 45  * that the element can be inserted
 46  * with sorted order. */
 47 float* lower_bound(float* first, float* last, float val)
 48 {
 49     ptrdiff_t len = last - first;
 50     while (len > 0)
 51     {
 52         ptrdiff_t half = len / 2;
 53         float* middle = first + half;
 54         if (*middle < val)
 55         {
 56             first = middle + 1;
 57             len = len - half - 1;
 58         }
 59         else
 60             len = half;
 61     }
 62     return first;
 63 }
 64 
 65 /* Writes each elements in float array to the file */
 66 void output(const char* filename, float* A, size_t size)
 67 {
 68     FILE* f = fopen(filename, "w");
 69     int i;
 70     for (i=0; i<size; i++)
 71     {
 72         fprintf(f, "%1.5f\n", A[i]);
 73     }
 74     fclose(f);
 75 }
 76 
 77 /*
 78  * First command-line argument: n
 79  */
 80 int main(int argc, char* argv[])
 81 {
 82     int n_number = 1000000;
 83     if (argc > 1) n_number = atoi(argv[1]);
 84     int id, p;
 85 
 86     MPI_Init(&argc, &argv);
 87 
 88     MPI_Barrier(MPI_COMM_WORLD);
 89     double elapsed_time = -MPI_Wtime();
 90 
 91     MPI_Comm_rank(MPI_COMM_WORLD, &id);
 92     MPI_Comm_size(MPI_COMM_WORLD, &p);
 93 
 94     /* Some nodes should control 1 more
 95      * element if n % p != 0. */
 96     float A[(n_number+(p-1))/p];
 97     float* single_list = A;
 98     int n_control = n_number/p;
 99     int n_sorted = n_control;
100     if (n_number%p > id) n_control++;
101     int i;
102 
103 #ifdef PRINT_MSG    
104     fprintf(stdout, "Process %d generates %d of %d elements.\n",
105             id, n_control, n_number);
106     fflush(stdout);
107 #endif
108 
109     /* For unique random number, add (id*1000) */
110     srand( time(NULL) + id * 1000);
111     generate_random_sequence(A, n_control);
112 
113     /* Phase 1 */
114     /* Each process quicksort their own list and each one picks samples */
115     qsort(A, n_control, sizeof(float), float_comp);
116 
117     if (p > 1)
118     {
119 
120         float samples[p];
121         for (i=0; i<p; i++)
122             samples[i] = A[i*n_control/p];
123 
124         /* Phase 2 */
125         /* Node 0 gathers samples, sorts them, and picks pivots. */
126         float all_samples[p*p];
127         MPI_Gather(samples, p, MPI_FLOAT, all_samples, p,
128                 MPI_FLOAT, 0, MPI_COMM_WORLD);
129         float pivots[p-1];
130         if (!id)
131         {
132             qsort(all_samples, p*p, sizeof(float), float_comp);
133 
134             for (i=0; i<p-1; i++)
135                 pivots[i] = all_samples[(i+1)*p+p/2-1];
136         }
137         /* Node 0 broadcasts pivots and each process 
138          * partitions its own list. */
139         MPI_Bcast(pivots, p-1, MPI_FLOAT, 0, MPI_COMM_WORLD);
140         int send_cnts[p], send_disp[p];
141         send_disp[0] = 0;
142         for (i=1; i<p; i++)
143         {
144             send_disp[i] =
145                 (float*)(lower_bound(A, A+n_control, pivots[i-1]))-A;
146             send_cnts[i-1] = send_disp[i] - send_disp[i-1];
147         }
148         send_cnts[p-1] = n_control - send_disp[p-1];
149 
150         /* Phase 3 */
151         /* First, exchanges the number of elements that 
152          * each one is going to exchange. */
153         int recv_cnts[p], recv_disp[p+1];
154         MPI_Alltoall(send_cnts, 1, MPI_FLOAT, recv_cnts, 1,
155                 MPI_FLOAT, MPI_COMM_WORLD);
156         recv_disp[0] = 0;
157         for (i=1; i<p; i++)
158             recv_disp[i] = recv_disp[i-1] + recv_cnts[i-1];
159         recv_disp[p] = recv_disp[p-1]+recv_cnts[p-1];
160         float partitions[recv_disp[p]];
161         /* Exchanges elements to appropriate nodes. */
162         MPI_Alltoallv(A, send_cnts, send_disp, MPI_FLOAT, partitions,
163                 recv_cnts, recv_disp, MPI_FLOAT, MPI_COMM_WORLD);
164 
165         /* Phase 4 */
166         /* Each node merges its own partitions into a single list. */
167         int j;
168         int merge_disp[p];
169         n_sorted = recv_disp[p];
170         single_list = malloc(n_sorted*sizeof(float));
171         memcpy(merge_disp, recv_disp, p*sizeof(int));
172         for (i=0; i<n_sorted; i++)
173         {
174             float min = UPPER_BOUND;
175             int min_pos = 0;
176             for (j=0; j<p; j++)
177                 if (merge_disp[j] < recv_disp[j+1]
178                         && min > partitions[merge_disp[j]])
179                 {
180                     min = partitions[merge_disp[j]];
181                     min_pos = j;
182                 }
183             single_list[i] = min;
184             merge_disp[min_pos]++;
185         }
186 
187     }
188     /* Synchronizes for checking maximum elapsed time among nodes. */
189     MPI_Barrier(MPI_COMM_WORLD);
190     elapsed_time += MPI_Wtime();
191 
192 #ifdef PRINT_MSG    
193     fprintf(stdout, "Process %d now has sorted the list that contains \
194 %d of %d elements.\n", id, n_sorted, n_number);
195     fflush(stdout);
196 #endif
197 
198     if (!id)
199         printf("Elapsed Time with %d processes: %10.6f\n",
200                 p, elapsed_time);
201 
202     /* Output (elapsed_time doesn't count for file output!) */
203 #ifdef OUTPUT_FILE      
204     char filename[100];
205     strcpy(filename, "glistxx.txt");
206     filename[5] = '0' + id / 10;
207     filename[6] = '0' + id % 10;
208     output(filename, A, n_control);
209     filename[0] = 's';
210     output(filename, single_list, n_sorted);
211 #endif
212     if (single_list != A) free(single_list);
213     MPI_Finalize();
214 
215     return 0;
216 }
217 

2006년 12월 20일

기사의 여행

기사의 여행은 체스판 위의 임의의 위치에서 기사가 출발하여 각 위치를 오직 한 번만 방문하면서 모든 위치를 방문하는 순서를 구하는 문제입니다. 오일러 등의 많은 수학자들이 이 문제를 다루었으며, 다양한 해법과 변형된 문제들이 있습니다. 가장 일반적인 해법은 되추적을 이용한 방법입니다.

19세기의 H. C. Warnsdorff는 기사의 여행 문제를 푸는 실용적인 방법을 제시하였습니다. 기사가 움직이면서 어느 곳으로도 움직일 수 없는 막다른 곳에 다다르지 않게 하는 것이 목적입니다. 막다른 곳에 다다르지 않게 하기 위하여 Warnsdorff가 제시한 규칙은 현재 기사가 한 번에 갈 수 있는 곳 중에서 다음 번 수에 갈 수 있는 곳이 가장 적은 곳으로 간다는 규칙입니다. 이 방법은 휴리스틱한 방법입니다만, 8 x 8의 체스판 공간에서 기사의 여행 문제를 잘 풀어 줍니다. 체스판의 공간이 넓어지면 제대로 풀리지 않는 경우가 생길 수 있습니다.

Warnsdorff의 규칙을 이용하여 기사의 여행 문제를 푸는 간단한 프로그램을 작성해 보았습니다. C언어로 작성되어 있고 시작행과 시작열의 위치를 기본 입력에서 읽어서 기본 출력으로 해를 출력해 줍니다.

  1 /*
  2  * Knight's Tour.
  3  *
  4  * Author: Jay
  5  * Date: 20th of December, 2006
  6  */
  7 #include <stdio.h>
  8 
  9 /* definitions */
 10 #define ROW_SIZE 8
 11 #define COL_SIZE 8
 12 #define NUM_WAYS 8
 13 typedef int board_t[ROW_SIZE][COL_SIZE];
 14 int dr[NUM_WAYS] = {-2, -1, 1, 2, 2, 1, -1, -2};
 15 int dc[NUM_WAYS] = {1, 2, 2, 1, -1, -2, -2, -1};
 16 
 17 /**
 18  * Set every element to -1
 19  */
 20 void initialize_board(board_t board)
 21 {
 22     int i, j;
 23     for (i=0; i<ROW_SIZE; i++)
 24         for (j=0; j<COL_SIZE; j++)
 25             board[i][j] = -1;
 26 }
 27 
 28 /**
 29  * Print the board out.
 30  */
 31 void print_board(board_t board)
 32 {
 33     int i, j;
 34     for (i=0; i<ROW_SIZE; i++)
 35     {
 36         for (j=0; j<COL_SIZE; j++)
 37             printf("%d\t", board[i][j]);
 38         printf("\n");
 39     }
 40 }
 41 
 42 /**
 43  * Check if (r,c) is inside board.
 44  * @return true if (r,c) is inside board, false otherwise.
 45  */
 46 int is_inside_board(int r, int c)
 47 {
 48     return r >= 0 && r < ROW_SIZE && c >= 0 && c < COL_SIZE;
 49 }
 50 
 51 /**
 52  * Check if (r,c) is available in board.
 53  * @return true if (r,c) is available, that is has value -1,
 54  *         false otherwise.
 55  */
 56 int is_available(board_t board, int r, int c)
 57 {
 58     return is_inside_board(r, c) && board[r][c] == -1;
 59 }
 60 
 61 /**
 62  * @return number of next moves of (r,c) in board.
 63  */
 64 int num_next_moves(board_t board, int r, int c)
 65 {
 66     int i, result=0;
 67     for (i=0; i<NUM_WAYS; i++)
 68         if (is_available(board, r+dr[i], c+dc[i]))
 69             result++;
 70     return result;
 71 }
 72 
 73 /**
 74  * Get next way id from (r,c) in board.
 75  * Next way is the way whose destination has minimal number of next moves.
 76  * @return next way id, which is in [0, NUM_WAYS).
 77  */
 78 int next_way_of(board_t board, int r, int c)
 79 {
 80     int i, min = NUM_WAYS, result=0;
 81     for (i=0; i<NUM_WAYS; i++)
 82         if (is_available(board, r+dr[i], c+dc[i])
 83                 && num_next_moves(board, r+dr[i], c+dc[i]) < min)
 84         {
 85             min = num_next_moves(board, r+dr[i], c+dc[i]);
 86             result = i;
 87         }
 88     return result;
 89 }
 90 
 91 /**
 92  * Get r, c from user and solve knight tour problem.
 93  * Print result out.
 94  * @return 0 for successful moves, 1 otherwise.
 95  */
 96 int main()
 97 {
 98     int r, c, move, next_way;
 99     board_t board;
100 
101     initialize_board(board);
102     while (1)
103     {
104         printf("Input start position r c: ");
105         scanf("%d %d", &r, &c);
106         fflush(stdin);
107         if (is_inside_board(r, c)) break;
108         printf("Please put them again.\n");
109     }
110     board[r][c] = 0;
111 
112     for (move=1; move<ROW_SIZE*COL_SIZE; move++)
113     {
114         if (num_next_moves(board, r, c) == 0)
115         {
116             printf("Failed.\n");
117             print_board(board);
118             return 1;
119         }
120         next_way = next_way_of(board, r, c);
121         r = r + dr[next_way];
122         c = c + dc[next_way];
123         board[r][c] = move;
124     }
125     print_board(board);
126 
127     return 0;
128 }

참고할 수 있는 URL:
Warnsdorff's rule - 영문 페이지