From bef36a93ede06a268b0ce5683392ffb23b111224 Mon Sep 17 00:00:00 2001 From: 13518104 Kevin Austin Stefano <13518104@std.stei.itb.ac.id> Date: Fri, 5 Mar 2021 01:05:58 +0700 Subject: [PATCH] Quick Sort with Kruskal Representation --- src/MPI_pointtopoint.c | 27 +++++++++++ src/MST_OpenMP.c | 11 +++++ src/MST_OpenMPI | Bin 0 -> 13200 bytes src/MST_OpenMPI.c | 108 ++++++++++++++++++++++++++++++++++++++--- src/MST_Serial.c | 4 -- src/a | Bin 0 -> 8624 bytes 6 files changed, 139 insertions(+), 11 deletions(-) create mode 100644 src/MPI_pointtopoint.c create mode 100755 src/MST_OpenMPI create mode 100755 src/a diff --git a/src/MPI_pointtopoint.c b/src/MPI_pointtopoint.c new file mode 100644 index 0000000..b3eb1b0 --- /dev/null +++ b/src/MPI_pointtopoint.c @@ -0,0 +1,27 @@ +#include <mpi.h> +#include <stdio.h> + +int main() +{ + int id, x; + + MPI_Init(NULL, NULL); + MPI_Comm_rank(MPI_COMM_WORLD, &id); + + // P0 mengirim pesan x ke P1 + // P1 menerima pesan dari P0, disimpan ke x + if (id == 0) { + x = 6; + MPI_Send(&x, 1, MPI_INT, 1, 0, MPI_COMM_WORLD); + MPI_Recv(&x, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, NULL); + } + if (id == 1) + MPI_Recv(&x, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, NULL); + x =7; + MPI_Send(&x, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + + printf("P[%d] x: %d\n", id, x); + + MPI_Finalize(); + return 0; +} \ No newline at end of file diff --git a/src/MST_OpenMP.c b/src/MST_OpenMP.c index 757d567..f71ec19 100644 --- a/src/MST_OpenMP.c +++ b/src/MST_OpenMP.c @@ -147,15 +147,21 @@ int find(Subset subsets[], int i) // find root and make root as parent of i // (path compression) if (subsets[i].parent != i) + #pragma omp single subsets[i].parent = find(subsets, subsets[i].parent); return subsets[i].parent; } void Union(Subset subsets[], int x, int y) { + #pragma omp task int xroot = find(subsets, x); + + #pragma omp task int yroot = find(subsets, y); + #pragma omp taskwait + // Attach smaller rank tree under root of high // rank tree (Union by Rank) if (subsets[xroot].rank < subsets[yroot].rank) @@ -205,11 +211,16 @@ void KruskalMST(Graph *graph, Edge result[], int *e) // Pick the smallest edge of the graph Edge next_edge = graph->edge[i++]; + #pragma omp task int x = find(subsets, next_edge.src); + #pragma omp task int y = find(subsets, next_edge.dest); + #pragma omp taskwait + if (x != y) { result[(*e)++] = next_edge; + Union(subsets, x, y); } } diff --git a/src/MST_OpenMPI b/src/MST_OpenMPI new file mode 100755 index 0000000000000000000000000000000000000000..556099513aad8e81d4c4db15ab75e054b12b7459 GIT binary patch literal 13200 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 V7nvLp$*QNykS^LJ{Wn+t{{ZTIWC{QP literal 0 HcmV?d00001 diff --git a/src/MST_OpenMPI.c b/src/MST_OpenMPI.c index ed877db..5ac42e2 100644 --- a/src/MST_OpenMPI.c +++ b/src/MST_OpenMPI.c @@ -24,6 +24,7 @@ typedef struct Edge *edge; } Graph; + // Create new Graph based on V vertices and E edges Graph *CreateGraph(int V, int E) { @@ -33,7 +34,95 @@ Graph *CreateGraph(int V, int E) graph->edge = (Edge *)(malloc(E * sizeof(Edge))); return graph; } +void PartisiSrc(Edge T[], int i, int j, int *k) +{ + Edge pivot = T[i - 1]; + int p = i; + int q = j; + while (p <= q) + { + while (T[p - 1].src < pivot.src) + { + p++; + } + while (T[q - 1].src > pivot.src) + { + q--; + } + if (p < q) + { + Edge temp = T[p - 1]; + T[p - 1] = T[q - 1]; + T[q - 1] = Edge; + p++; + q--; + } + else if (p == q) + { + p++; + } + } + *k = q; +} + +// Mengurutkan array dengan menggunakan Quick Sort +void QuickSortSrc(Edge T[], int i, int j) +{ + int k; + if (i < j) + { + PartisiSrc(T, i, j, &k); + // PrintArray(T, i, k); + // PrintArray(T, k + 1, j); + QuickSortSrc(T, i, k); + QuickSortSrc(T, k + 1, j); + } +} +void PartisiWeight(Edge T[], int i, int j, int *k) +{ + Edge pivot = T[i - 1]; + int p = i; + int q = j; + while (p <= q) + { + while (T[p - 1].weight < pivot.weight) + { + p++; + } + while (T[q - 1].weight > pivot.weight) + { + q--; + } + if (p < q) + { + Edge temp = T[p - 1]; + T[p - 1] = T[q - 1]; + T[q - 1] = temp; + p++; + q--; + } + else if (p == q) + { + p++; + } + } + *k = q; +} + +// Mengurutkan array dengan menggunakan Quick Sort +void QuickSortWeight(Edge T[], int i, int j) +{ + int k; + if (i < j) + { + PartisiWeight(T, i, j, &k); + // PrintArray(T, i, k); + // PrintArray(T, k + 1, j); + QuickSortWeight(T, i, k); + QuickSortWeight(T, k + 1, j); + } +} // Print all graph component to the CLI void PrintGraph(Graph *graph) { @@ -202,7 +291,7 @@ void KruskalMST(Graph *graph, Edge result[], int *e) { // Pick the smallest edge of the graph Edge next_edge = graph->edge[i++]; - + MPI_Send(&x, 1, MPI_INT, 1, 0, MPI_COMM_WORLD); int x = find(subsets, next_edge.src); int y = find(subsets, next_edge.dest); if (x != y) @@ -218,8 +307,9 @@ void KruskalMST(Graph *graph, Edge result[], int *e) } void printResult(Edge *result, int e) -{ - // printf("%d\n", e); +{ // Get the number of processes + + int minimumCost = 0; // #pragma omp parallel for for (int i = 0; i < e; i++) @@ -228,15 +318,19 @@ void printResult(Edge *result, int e) minimumCost += result[i].weight; } printf("%d\n", minimumCost); - for (int i = 0; i < e; i++) + + + for (int i = start; i < numPartition; i++) { printf("%d-%d\n", result[i].src, result[i].dest); } } // Driver program to test above functions -int main(int argc, char** argv) +int main() { + + Graph *graph= ReadArguments(); MPI_Init(NULL, NULL); // Get the number of processes int world_size; @@ -247,13 +341,13 @@ int main(int argc, char** argv) MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); - Graph *graph = ReadArguments(); Edge result[graph->V]; int e = 0; int t = clock(); KruskalMST(graph, result, &e); t = clock() - t; - printResult(result, e); + // printResult(result, e); printf("Waktu Eksekusi: %f ms \n", ((double)t) / CLOCKS_PER_SEC * 1000); MPI_Finalize(); + return 0; } \ No newline at end of file diff --git a/src/MST_Serial.c b/src/MST_Serial.c index 78971dd..6f4a643 100644 --- a/src/MST_Serial.c +++ b/src/MST_Serial.c @@ -185,10 +185,6 @@ void KruskalMST(Graph *graph, Edge result[], int *e) bubbleSort(graph->edge, graph->E, 1); // Step 2: Allocate memory for creating V subsets Subset *subsets = CreateSubset(V); -<<<<<<< HEAD - -======= ->>>>>>> fd8239d3b13d39b3a279991ba4a69031b1378216 while (((*e) < V - 1) && (i < graph->E)) { // Pick the smallest edge of the graph diff --git a/src/a b/src/a new file mode 100755 index 0000000000000000000000000000000000000000..80bbe4ed5ce9bfdf04b0344f38f98032aabeaa95 GIT binary patch literal 8624 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 M>Rx9>MV<El0$o%&VE_OC literal 0 HcmV?d00001 -- GitLab