Ordenar por fusión usando subprocesos múltiples

Merge Sort es una técnica de clasificación popular que divide una array o lista en dos mitades y luego comienza a fusionarlas cuando se alcanza la profundidad suficiente. La complejidad de tiempo del ordenamiento por fusión es O(nlogn).
Los subprocesos son procesos livianos y los subprocesos comparten con otros subprocesos su sección de código, sección de datos y recursos del sistema operativo, como archivos abiertos y señales. Pero, como un proceso, un hilo tiene su propio contador de programa (PC), un conjunto de registros y un espacio de pila.
Los subprocesos múltiples son una forma de mejorar el paralelismo al ejecutar los subprocesos simultáneamente en diferentes núcleos de su procesador. En este programa, usaremos 4 subprocesos, pero puede cambiarlo según la cantidad de núcleos que tenga su procesador.
Ejemplos: 
 

Input :  83, 86, 77, 15, 93, 35, 86, 92, 49, 21, 
         62, 27, 90, 59, 63, 26, 40, 26, 72, 36
Output : 15, 21, 26, 26, 27, 35, 36, 40, 49, 59, 
         62, 63, 72, 77, 83, 86, 86, 90, 92, 93

Input :  6, 5, 4, 3, 2, 1
Output : 1, 2, 3, 4, 5, 6

Nota* Es mejor ejecutar el programa en un sistema basado en Linux. 
Para compilar en el sistema Linux: 
 

g++ -pthread program_name.cpp

C++

// CPP Program to implement merge sort using
// multi-threading
#include <iostream>
#include <pthread.h>
#include <time.h>
 
// number of elements in array
#define MAX 20
 
// number of threads
#define THREAD_MAX 4
 
using namespace std;
 
// array of size MAX
int a[MAX];
int part = 0;
 
// merge function for merging two parts
void merge(int low, int mid, int high)
{
    int* left = new int[mid - low + 1];
    int* right = new int[high - mid];
 
    // n1 is size of left part and n2 is size
    // of right part
    int n1 = mid - low + 1, n2 = high - mid, i, j;
 
    // storing values in left part
    for (i = 0; i < n1; i++)
        left[i] = a[i + low];
 
    // storing values in right part
    for (i = 0; i < n2; i++)
        right[i] = a[i + mid + 1];
 
    int k = low;
    i = j = 0;
 
    // merge left and right in ascending order
    while (i < n1 && j < n2) {
        if (left[i] <= right[j])
            a[k++] = left[i++];
        else
            a[k++] = right[j++];
    }
 
    // insert remaining values from left
    while (i < n1) {
        a[k++] = left[i++];
    }
 
    // insert remaining values from right
    while (j < n2) {
        a[k++] = right[j++];
    }
}
 
// merge sort function
void merge_sort(int low, int high)
{
    // calculating mid point of array
    int mid = low + (high - low) / 2;
    if (low < high) {
 
        // calling first half
        merge_sort(low, mid);
 
        // calling second half
        merge_sort(mid + 1, high);
 
        // merging the two halves
        merge(low, mid, high);
    }
}
 
// thread function for multi-threading
void* merge_sort(void* arg)
{
    // which part out of 4 parts
    int thread_part = part++;
 
    // calculating low and high
    int low = thread_part * (MAX / 4);
    int high = (thread_part + 1) * (MAX / 4) - 1;
 
    // evaluating mid point
    int mid = low + (high - low) / 2;
    if (low < high) {
        merge_sort(low, mid);
        merge_sort(mid + 1, high);
        merge(low, mid, high);
    }
}
 
// Driver Code
int main()
{
    // generating random values in array
    for (int i = 0; i < MAX; i++)
        a[i] = rand() % 100;
 
    // t1 and t2 for calculating time for
    // merge sort
    clock_t t1, t2;
 
    t1 = clock();
    pthread_t threads[THREAD_MAX];
 
    // creating 4 threads
    for (int i = 0; i < THREAD_MAX; i++)
        pthread_create(&threads[i], NULL, merge_sort,
                                        (void*)NULL);
 
    // joining all 4 threads
    for (int i = 0; i < 4; i++)
        pthread_join(threads[i], NULL);
 
    // merging the final 4 parts
    merge(0, (MAX / 2 - 1) / 2, MAX / 2 - 1);
    merge(MAX / 2, MAX/2 + (MAX-1-MAX/2)/2, MAX - 1);
    merge(0, (MAX - 1)/2, MAX - 1);
 
    t2 = clock();
 
    // displaying sorted array
    cout << "Sorted array: ";
    for (int i = 0; i < MAX; i++)
        cout << a[i] << " ";
 
    // time taken by merge sort in seconds
    cout << "Time taken: " << (t2 - t1) /
              (double)CLOCKS_PER_SEC << endl;
 
    return 0;
}

Java

// Java Program to implement merge sort using
// multi-threading
import java.lang.System;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
 
class MergeSort{
     
      // Assuming system has 4 logical processors
    private static final int MAX_THREADS = 4;
       
    // Custom Thread class with constructors
    private static class SortThreads extends Thread{
        SortThreads(Integer[] array, int begin, int end){
            super(()->{
                MergeSort.mergeSort(array, begin, end);
            });
            this.start();
        }
    }
     
      // Perform Threaded merge sort
    public static void threadedSort(Integer[] array){
          // For performance - get current time in millis before starting
        long time = System.currentTimeMillis();
        final int length = array.length;
        // Workload per thread (chunk_of_data) = total_elements/core_count
        // if the no of elements exactly go into no of available threads,
        // then divide work equally,
        // else if some remainder is present, then assume we have (actual_threads-1) available workers
        // and assign the remaining elements to be worked upon by the remaining 1 actual thread.
        boolean exact = length%MAX_THREADS == 0;
        int maxlim = exact? length/MAX_THREADS: length/(MAX_THREADS-1);
        // if workload is less and no more than 1 thread is required for work, then assign all to 1 thread
        maxlim = maxlim < MAX_THREADS? MAX_THREADS : maxlim;
        // To keep track of threads
        final ArrayList<SortThreads> threads = new ArrayList<>();
        // Since each thread is independent to work on its assigned chunk,
        // spawn threads and assign their working index ranges
        // ex: for 16 element list, t1 = 0-3, t2 = 4-7, t3 = 8-11, t4 = 12-15
        for(int i=0; i < length; i+=maxlim){
            int beg = i;
            int remain = (length)-i;
            int end = remain < maxlim? i+(remain-1): i+(maxlim-1); 
            final SortThreads t = new SortThreads(array, beg, end);
            // Add the thread references to join them later
            threads.add(t);
        }
        for(Thread t: threads){
            try{
                  // This implementation of merge requires, all chunks worked by threads to be sorted first.
                // so we wait until all threads complete
                t.join();
            } catch(InterruptedException ignored){}
        }
        // System.out.println("Merging k-parts array, where m number of parts are distinctly sorted by each Threads of available MAX_THREADS="+MAX_THREADS);
        /*
          The merge takes 2 parts at a time and merges them into 1,
          then again merges the resultant into next part and so on...until end
          For MAXLIMIT = 2 (2 elements per thread where total threads = 4, in a total of 4*2 = 8 elements)
          list1 = (beg, mid); list2 = (mid+1, end);
          1st merge = 0,0,1 (beg, mid, end)
          2nd merge = 0,1,3 (beg, mid, end)
          3rd merge = 0,3,5 (beg, mid, end)
          4th merge = 0,5,7 (beg, mid, end)
        */
        for(int i=0; i < length; i+=maxlim){
            int mid = i == 0? 0 : i-1;
            int remain = (length)-i;
            int end = remain < maxlim? i+(remain-1): i+(maxlim-1);
            // System.out.println("Begin: "+0 + " Mid: "+ mid+ " End: "+ end + " MAXLIM = " + maxlim);
            merge(array, 0, mid, end);
        }
        time = System.currentTimeMillis() - time;
        System.out.println("Time spent for custom multi-threaded recursive merge_sort(): "+ time+ "ms");
    }
 
    // Typical recursive merge sort
    public static void mergeSort(Integer[] array, int begin, int end){
        if (begin<end){
            int mid = (begin+end)/2;
            mergeSort(array, begin, mid);
            mergeSort(array, mid+1, end);
            merge(array, begin, mid, end);
        }
    }
     
    //Typical 2-way merge
    public static void merge(Integer[] array, int begin, int mid, int end){
        Integer[] temp = new Integer[(end-begin)+1];
         
        int i = begin, j = mid+1;
        int k = 0;
 
        // Add elements from first half or second half based on whichever is lower,
        // do until one of the list is exhausted and no more direct one-to-one comparison could be made
        while(i<=mid && j<=end){
            if (array[i] <= array[j]){
                temp[k] = array[i];
                i+=1;
            }else{
                temp[k] = array[j];
                j+=1;
            }
            k+=1;
        }
 
        // Add remaining elements to temp array from first half that are left over
        while(i<=mid){
            temp[k] = array[i];
            i+=1; k+=1;
        }
         
        // Add remaining elements to temp array from second half that are left over
        while(j<=end){
            temp[k] = array[j];
            j+=1; k+=1;
        }
 
        for(i=begin, k=0; i<=end; i++,k++){
            array[i] = temp[k];
        }
    }
}
 
class Driver{
    // Array Size 
    private static Random random = new Random();
    private static final int size = random.nextInt(100);
    private static final Integer list[] = new Integer[size];
    // Fill the initial array with random elements within range
    static {
      for(int i=0; i<size; i++){
        // add a +ve offset to the generated random number and subtract same offset
        // from total so that the number shifts towards negative side by the offset.
        // ex: if random_num = 10, then (10+100)-100 => -10
        list[i] = random.nextInt(size+(size-1))-(size-1);
      }
    }
    // Test the sorting methods performance
    public static void main(String[] args){
      System.out.print("Input = [");
      for (Integer each: list)
        System.out.print(each+", ");
      System.out.print("] \n" +"Input.length = " + list.length + '\n');
 
      // Test standard Arrays.sort() method
      Integer[] arr1 = Arrays.copyOf(list, list.length);
      long t = System.currentTimeMillis();
      Arrays.sort(arr1, (a,b)->a>b? 1: a==b? 0: -1);
      t = System.currentTimeMillis() - t;
      System.out.println("Time spent for system based Arrays.sort(): " + t + "ms");
 
      // Test custom single-threaded merge sort (recursive merge) implementation
      Integer[] arr2 = Arrays.copyOf(list, list.length);
      t = System.currentTimeMillis();
      MergeSort.mergeSort(arr2, 0, arr2.length-1);
      t = System.currentTimeMillis() - t;
      System.out.println("Time spent for custom single threaded recursive merge_sort(): " + t + "ms");
 
      // Test custom (multi-threaded) merge sort (recursive merge) implementation
      Integer[] arr = Arrays.copyOf(list, list.length);
      MergeSort.threadedSort(arr);
      System.out.print("Output = [");
      for (Integer each: arr)
        System.out.print(each+", ");
      System.out.print("]\n");
    }
}

Producción: 
 

Sorted array: 15 21 26 26 27 35 36 40 49 59 62 63 72 77 83 86 86 90 92 93
Time taken: 0.001023

Publicación traducida automáticamente

Artículo escrito por shubham_rana_77 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *