logo

Asymptotisk analyse og sammenligning af sorteringsalgoritmer

Det er et veletableret faktum, at flettesortering kører hurtigere end indsættelsessortering. Bruger asymptotisk analyse . vi kan bevise, at merge sort kører i O(nlogn) tid og indsættelsessortering tager O(n^2). Det er indlysende, fordi merge sort bruger en opdel-og-hersk tilgang ved rekursivt at løse problemerne, hvor sortering ved indsættelse følger en inkrementel tilgang. Hvis vi gransker tidskompleksitetsanalysen yderligere, vil vi få at vide, at indsættelsessortering ikke er så slemt nok. Overraskende nok fusionerer indsættelsessorteringsslag sortering på mindre inputstørrelse. Dette skyldes, at der er få konstanter, som vi ignorerer, mens vi udleder tidskompleksiteten. På større inputstørrelser i størrelsesordenen 10^4 påvirker dette ikke vores funktions opførsel. Men når inputstørrelser falder til under sige mindre end 40, så dominerer konstanterne i ligningen inputstørrelsen 'n'. Så langt så godt. Men jeg var ikke tilfreds med en sådan matematisk analyse. Som en datalogi undergraduat skal vi tro på at skrive kode. Jeg har skrevet et C-program for at få en fornemmelse af, hvordan algoritmerne konkurrerer mod hinanden om forskellige inputstørrelser. Og også hvorfor der udføres en så streng matematisk analyse for at etablere driftstidskompleksiteter for disse sorteringsalgoritmer.

mit fulde form

Implementering:

CPP
#include  #include  #include  #include  #define MAX_ELEMENT_IN_ARRAY 1000000001 int cmpfunc(const void *a const void *b) {  // Compare function used by qsort  return (*(int *)a - *(int *)b); } int *generate_random_array(int n) {  srand(time(NULL));  int *a = malloc(sizeof(int) * n);  int i;  for (i = 0; i < n; ++i)  a[i] = rand() % MAX_ELEMENT_IN_ARRAY;  return a; } int *copy_array(int a[] int n) {  int *arr = malloc(sizeof(int) * n);  int i;  for (i = 0; i < n; ++i)  arr[i] = a[i];  return arr; } // Code for Insertion Sort void insertion_sort_asc(int a[] int start int end) {  int i;  for (i = start + 1; i <= end; ++i)  {  int key = a[i];  int j = i - 1;  while (j >= start && a[j] > key)  {  a[j + 1] = a[j];  --j;  }  a[j + 1] = key;  } } // Code for Merge Sort void merge(int a[] int start int end int mid) {  int i = start j = mid + 1 k = 0;  int *aux = malloc(sizeof(int) * (end - start + 1));  while (i <= mid && j <= end)  {  if (a[i] <= a[j])  aux[k++] = a[i++];  else  aux[k++] = a[j++];  }  while (i <= mid)  aux[k++] = a[i++];  while (j <= end)  aux[k++] = a[j++];  j = 0;  for (i = start; i <= end; ++i)  a[i] = aux[j++];  free(aux); } void _merge_sort(int a[] int start int end) {  if (start < end)  {  int mid = start + (end - start) / 2;  _merge_sort(a start mid);  _merge_sort(a mid + 1 end);  merge(a start end mid);  } } void merge_sort(int a[] int n) {  return _merge_sort(a 0 n - 1); } void insertion_and_merge_sort_combine(int a[] int start int end int k) {  // Performs insertion sort if size of array is less than or equal to k  // Otherwise uses mergesort  if (start < end)  {  int size = end - start + 1;  if (size <= k)  {  return insertion_sort_asc(a start end);  }  int mid = start + (end - start) / 2;  insertion_and_merge_sort_combine(a start mid k);  insertion_and_merge_sort_combine(a mid + 1 end k);  merge(a start end mid);  } } void test_sorting_runtimes(int size int num_of_times) {  // Measuring the runtime of the sorting algorithms  int number_of_times = num_of_times;  int t = number_of_times;  int n = size;  double insertion_sort_time = 0 merge_sort_time = 0;  double merge_sort_and_insertion_sort_mix_time = 0 qsort_time = 0;  while (t--)  {  clock_t start end;  int *a = generate_random_array(n);  int *b = copy_array(a n);  start = clock();  insertion_sort_asc(b 0 n - 1);  end = clock();  insertion_sort_time += ((double)(end - start)) / CLOCKS_PER_SEC;  free(b);  int *c = copy_array(a n);  start = clock();  merge_sort(c n);  end = clock();  merge_sort_time += ((double)(end - start)) / CLOCKS_PER_SEC;  free(c);  int *d = copy_array(a n);  start = clock();  insertion_and_merge_sort_combine(d 0 n - 1 40);  end = clock();  merge_sort_and_insertion_sort_mix_time += ((double)(end - start)) / CLOCKS_PER_SEC;  free(d);  start = clock();  qsort(a n sizeof(int) cmpfunc);  end = clock();  qsort_time += ((double)(end - start)) / CLOCKS_PER_SEC;  free(a);  }  insertion_sort_time /= number_of_times;  merge_sort_time /= number_of_times;  merge_sort_and_insertion_sort_mix_time /= number_of_times;  qsort_time /= number_of_times;  printf('nTime taken to sort:n'  '%-35s %fn'  '%-35s %fn'  '%-35s %fn'  '%-35s %fnn'  '(i)Insertion sort: '  insertion_sort_time  '(ii)Merge sort: '  merge_sort_time  '(iii)Insertion-mergesort-hybrid: '  merge_sort_and_insertion_sort_mix_time  '(iv)Qsort library function: '  qsort_time); } int main(int argc char const *argv[]) {  int t;  scanf('%d' &t);  while (t--)  {  int size num_of_times;  scanf('%d %d' &size &num_of_times);  test_sorting_runtimes(size num_of_times);  }  return 0; } 
Java
import java.util.Scanner; import java.util.Arrays; import java.util.Random; public class SortingAlgorithms {  // Maximum element in array  static final int MAX_ELEMENT_IN_ARRAY = 1000000001;  public static void main(String[] args) {  Scanner scanner = new Scanner(System.in);  int t = scanner.nextInt();  for (int i = 0; i < t; i++) {  int size = scanner.nextInt();  int num_of_times = scanner.nextInt();  testSortingRuntimes(size num_of_times);  }  scanner.close();  }    static int[] generateRandomArray(int n) {  // Generate an array of n random integers.  int[] arr = new int[n];  Random random = new Random();  for (int i = 0; i < n; i++) {  arr[i] = random.nextInt(MAX_ELEMENT_IN_ARRAY);  }  return arr;  }  static void insertionSortAsc(int[] a int start int end) {  // Perform an in-place insertion sort on a from start to end.  for (int i = start + 1; i <= end; i++) {  int key = a[i];  int j = i - 1;  while (j >= start && a[j] > key) {  a[j + 1] = a[j];  j--;  }  a[j + 1] = key;  }  }  static void merge(int[] a int start int end int mid) {  // Merge two sorted sublists of a.  // The first sublist is a[start:mid+1] and the second sublist is a[mid+1:end+1].  int[] aux = new int[end - start + 1];  int i = start j = mid + 1 k = 0;  while (i <= mid && j <= end) {  if (a[i] <= a[j]) {  aux[k++] = a[i++];  } else {  aux[k++] = a[j++];  }  }  while (i <= mid) {  aux[k++] = a[i++];  }  while (j <= end) {  aux[k++] = a[j++];  }  System.arraycopy(aux 0 a start aux.length);  }  static void mergeSort(int[] a) {  // Perform an in-place merge sort on a.  mergeSortHelper(a 0 a.length - 1);  }  static void mergeSortHelper(int[] a int start int end) {  // Recursive merge sort function.  if (start < end) {  int mid = start + (end - start) / 2;  mergeSortHelper(a start mid);  mergeSortHelper(a mid + 1 end);  merge(a start end mid);  }  }  static void insertionAndMergeSortCombine(int[] a int start int end int k) {  /*  Perform an in-place sort on a from start to end.  If the size of the list is less than or equal to k use insertion sort.  Otherwise use merge sort.  */  if (start < end) {  int size = end - start + 1;  if (size <= k) {  insertionSortAsc(a start end);  } else {  int mid = start + (end - start) / 2;  insertionAndMergeSortCombine(a start mid k);  insertionAndMergeSortCombine(a mid + 1 end k);  merge(a start end mid);  }  }  }  static void testSortingRuntimes(int size int num_of_times) {  // Test the runtime of the sorting algorithms.  double insertionSortTime = 0;  double mergeSortTime = 0;  double mergeSortAndInsertionSortMixTime = 0;  double qsortTime = 0;  for (int i = 0; i < num_of_times; i++) {  int[] a = generateRandomArray(size);  int[] b = Arrays.copyOf(a a.length);  long start = System.currentTimeMillis();  insertionSortAsc(b 0 b.length - 1);  long end = System.currentTimeMillis();  insertionSortTime += end - start;  int[] c = Arrays.copyOf(a a.length);  start = System.currentTimeMillis();  mergeSort(c);  end = System.currentTimeMillis();  mergeSortTime += end - start;  int[] d = Arrays.copyOf(a a.length);  start = System.currentTimeMillis();  insertionAndMergeSortCombine(d 0 d.length - 1 40);  end = System.currentTimeMillis();  mergeSortAndInsertionSortMixTime += end - start;  int[] e = Arrays.copyOf(a a.length);  start = System.currentTimeMillis();  Arrays.sort(e);  end = System.currentTimeMillis();  qsortTime += end - start;  }  insertionSortTime /= num_of_times;  mergeSortTime /= num_of_times;  mergeSortAndInsertionSortMixTime /= num_of_times;  qsortTime /= num_of_times;  System.out.println('nTime taken to sort:n'  + '(i) Insertion sort: ' + insertionSortTime + 'n'  + '(ii) Merge sort: ' + mergeSortTime + 'n'  + '(iii) Insertion-mergesort-hybrid: ' + mergeSortAndInsertionSortMixTime + 'n'  + '(iv) Qsort library function: ' + qsortTime + 'n');  } } 
Python3
import time import random import copy from typing import List # Maximum element in array MAX_ELEMENT_IN_ARRAY = 1000000001 def generate_random_array(n: int) -> List[int]: #Generate a list of n random integers. return [random.randint(0 MAX_ELEMENT_IN_ARRAY) for _ in range(n)] def insertion_sort_asc(a: List[int] start: int end: int) -> None: #Perform an in-place insertion sort on a from start to end. for i in range(start + 1 end + 1): key = a[i] j = i - 1 while j >= start and a[j] > key: a[j + 1] = a[j] j -= 1 a[j + 1] = key def merge(a: List[int] start: int end: int mid: int) -> None: #Merge two sorted sublists of a. #The first sublist is a[start:mid+1] and the second sublist is a[mid+1:end+1]. aux = [] i = start j = mid + 1 while i <= mid and j <= end: if a[i] <= a[j]: aux.append(a[i]) i += 1 else: aux.append(a[j]) j += 1 while i <= mid: aux.append(a[i]) i += 1 while j <= end: aux.append(a[j]) j += 1 a[start:end+1] = aux def _merge_sort(a: List[int] start: int end: int) -> None: #Recursive merge sort function. if start < end: mid = start + (end - start) // 2 _merge_sort(a start mid) _merge_sort(a mid + 1 end) merge(a start end mid) def merge_sort(a: List[int]) -> None: #Perform an in-place merge sort on a. _merge_sort(a 0 len(a) - 1) def insertion_and_merge_sort_combine(a: List[int] start: int end: int k: int) -> None:  '''  Perform an in-place sort on a from start to end.  If the size of the list is less than or equal to k use insertion sort.  Otherwise use merge sort.  ''' if start < end: size = end - start + 1 if size <= k: insertion_sort_asc(a start end) else: mid = start + (end - start) // 2 insertion_and_merge_sort_combine(a start mid k) insertion_and_merge_sort_combine(a mid + 1 end k) merge(a start end mid) def test_sorting_runtimes(size: int num_of_times: int) -> None: #Test the runtime of the sorting algorithms. insertion_sort_time = 0 merge_sort_time = 0 merge_sort_and_insertion_sort_mix_time = 0 qsort_time = 0 for _ in range(num_of_times): a = generate_random_array(size) b = copy.deepcopy(a) start = time.time() insertion_sort_asc(b 0 len(b) - 1) end = time.time() insertion_sort_time += end - start c = copy.deepcopy(a) start = time.time() merge_sort(c) end = time.time() merge_sort_time += end - start d = copy.deepcopy(a) start = time.time() insertion_and_merge_sort_combine(d 0 len(d) - 1 40) end = time.time() merge_sort_and_insertion_sort_mix_time += end - start start = time.time() a.sort() end = time.time() qsort_time += end - start insertion_sort_time /= num_of_times merge_sort_time /= num_of_times merge_sort_and_insertion_sort_mix_time /= num_of_times qsort_time /= num_of_times print(f'nTime taken to sort:n' f'(i)Insertion sort: {insertion_sort_time}n' f'(ii)Merge sort: {merge_sort_time}n' f'(iii)Insertion-mergesort-hybrid: {merge_sort_and_insertion_sort_mix_time}n' f'(iv)Qsort library function: {qsort_time}n') def main() -> None: t = int(input()) for _ in range(t): size num_of_times = map(int input().split()) test_sorting_runtimes(size num_of_times) if __name__ == '__main__': main() 
JavaScript
// Importing required modules const { performance } = require('perf_hooks'); // Maximum element in array const MAX_ELEMENT_IN_ARRAY = 1000000001; // Function to generate a list of n random integers function generateRandomArray(n) {  return Array.from({length: n} () => Math.floor(Math.random() * MAX_ELEMENT_IN_ARRAY)); } // Function to perform an in-place insertion sort on a from start to end function insertionSortAsc(a start end) {  for (let i = start + 1; i <= end; i++) {  let key = a[i];  let j = i - 1;  while (j >= start && a[j] > key) {  a[j + 1] = a[j];  j -= 1;  }  a[j + 1] = key;  } } // Function to merge two sorted sublists of a function merge(a start end mid) {  let aux = [];  let i = start;  let j = mid + 1;  while (i <= mid && j <= end) {  if (a[i] <= a[j]) {  aux.push(a[i]);  i += 1;  } else {  aux.push(a[j]);  j += 1;  }  }  while (i <= mid) {  aux.push(a[i]);  i += 1;  }  while (j <= end) {  aux.push(a[j]);  j += 1;  }  for (let i = start; i <= end; i++) {  a[i] = aux[i - start];  } } // Recursive merge sort function function _mergeSort(a start end) {  if (start < end) {  let mid = start + Math.floor((end - start) / 2);  _mergeSort(a start mid);  _mergeSort(a mid + 1 end);  merge(a start end mid);  } } // Function to perform an in-place merge sort on a function mergeSort(a) {  _mergeSort(a 0 a.length - 1); } // Function to perform an in-place sort on a from start to end function insertionAndMergeSortCombine(a start end k) {  if (start < end) {  let size = end - start + 1;  if (size <= k) {  insertionSortAsc(a start end);  } else {  let mid = start + Math.floor((end - start) / 2);  insertionAndMergeSortCombine(a start mid k);  insertionAndMergeSortCombine(a mid + 1 end k);  merge(a start end mid);  }  } } // Function to test the runtime of the sorting algorithms function testSortingRuntimes(size numOfTimes) {  let insertionSortTime = 0;  let mergeSortTime = 0;  let mergeSortAndInsertionSortMixTime = 0;  let qsortTime = 0;  for (let _ = 0; _ < numOfTimes; _++) {  let a = generateRandomArray(size);  let b = [...a];  let start = performance.now();  insertionSortAsc(b 0 b.length - 1);  let end = performance.now();  insertionSortTime += end - start;  let c = [...a];  start = performance.now();  mergeSort(c);  end = performance.now();  mergeSortTime += end - start;  let d = [...a];  start = performance.now();  insertionAndMergeSortCombine(d 0 d.length - 1 40);  end = performance.now();  mergeSortAndInsertionSortMixTime += end - start;  start = performance.now();  a.sort((a b) => a - b);  end = performance.now();  qsortTime += end - start;  }  insertionSortTime /= numOfTimes;  mergeSortTime /= numOfTimes;  mergeSortAndInsertionSortMixTime /= numOfTimes;  qsortTime /= numOfTimes;  console.log(`nTime taken to sort:n(i)Insertion sort: ${insertionSortTime}n(ii)Merge sort: ${mergeSortTime}n(iii)Insertion-mergesort-hybrid: ${mergeSortAndInsertionSortMixTime}n(iv)Qsort library function: ${qsortTime}n`); } // Main function function main() {  let t = parseInt(prompt('Enter the number of test cases: '));  for (let _ = 0; _ < t; _++) {  let size = parseInt(prompt('Enter the size of the array: '));  let numOfTimes = parseInt(prompt('Enter the number of times to run the test: '));  testSortingRuntimes(size numOfTimes);  } } // Call the main function main(); 

Jeg har sammenlignet køretiderne for følgende algoritmer:



10 af 40
  • Indsættelsessortering : Den traditionelle algoritme uden ændringer/optimering. Det fungerer meget godt til mindre inputstørrelser. Og ja, det slår merge sort
  • Går skæbnen : Følger opdel-og-hersk tilgangen. For inputstørrelser af størrelsesordenen 10^5 er denne algoritme det rigtige valg. Det gør indsættelsessortering upraktisk for så store inputstørrelser.
  • Kombineret version af indsættelsessortering og fletsortering: Jeg har justeret logikken i flettesorteringen en lille smule for at opnå en betydeligt bedre køretid for mindre inputstørrelser. Som vi ved, opdeler sortering sit input i to halvdele, indtil det er trivielt nok til at sortere elementerne. Men her, når inputstørrelsen falder under en tærskel som 'n'< 40 then this hybrid algorithm makes a call to traditional insertion sort procedure. From the fact that insertion sort runs faster on smaller inputs and merge sort runs faster on larger inputs this algorithm makes best use both the worlds.
  • Hurtig sortering: Jeg har ikke implementeret denne procedure. Dette er biblioteksfunktionen qsort() som er tilgængelig i . Jeg har overvejet denne algoritme for at kende betydningen af ​​implementering. Det kræver en stor programmeringsekspertise at minimere antallet af trin og højst gøre brug af de underliggende sprogprimitiver til at implementere en algoritme på den bedst mulige måde. Dette er hovedårsagen til, at det anbefales at bruge biblioteksfunktioner. De er skrevet til at klare alt og alt. De optimerer så vidt muligt. Og før jeg glemmer fra min analyse, kører qsort() lynende hurtigt på stort set alle inputstørrelser!

Analysen:

  • Input: Brugeren skal angive det antal gange, han/hun ønsker at teste algoritmen svarende til antallet af testcases. For hvert testtilfælde skal brugeren indtaste to mellemrumsadskilte heltal, der angiver inputstørrelsen 'n' og 'antal_of_times', der angiver det antal gange, han/hun ønsker at køre analysen og tage gennemsnit. (Tydeliggørelse: Hvis 'antal_of_times' er 10, så kører hver af de ovenfor angivne algoritmer 10 gange, og gennemsnittet tages. Dette gøres, fordi input-arrayet genereres tilfældigt svarende til input-størrelsen, som du angiver. Input-arrayet kunne være alt sorteret. Vores det kunne svare til det værste tilfælde, dvs. algoritmen køres 'antal_of_times' og gennemsnittet tages.) clock() rutine og CLOCKS_PER_SEC makro fra bruges til at måle den tid, det tager. Kompilering: Jeg har skrevet ovenstående kode i Linux-miljø (Ubuntu 16.04 LTS). Kopiér kodestykket ovenfor. Kompiler det ved at bruge gcc-tasten i inputs som specificeret og beundre styrken ved sorteringsalgoritmer!
  • Resultater:  Som du kan se for små inputstørrelser, sorteres sorteringsslag sammen, sorteres efter 2 * 10^-6 sek. Men denne forskel i tid er ikke så væsentlig. På den anden side fungerer hybridalgoritmen og qsort()-biblioteksfunktionen begge lige så godt som indsættelsessort. Asymptotisk analyse af Algos_0' src='//techcodeview.com/img/analysis-of-algorithms/63/asymptotic-analysis-and-comparison-of-sorting-algorithms.webp' title=Inputstørrelsen øges nu med cirka 100 gange til n = 1000 fra n = 30. Forskellen er nu mærkbar. Merge sortering kører 10 gange hurtigere end indsættelsessortering. Der er igen et bånd mellem ydeevnen af ​​hybridalgoritmen og qsort() rutinen. Dette tyder på, at qsort() er implementeret på en måde, der ligner mere eller mindre vores hybridalgoritme, dvs. at skifte mellem forskellige algoritmer for at få det bedste ud af dem. Asymptotisk analyse af Algos_1' loading='lazy' src='//techcodeview.com/img/analysis-of-algorithms/63/asymptotic-analysis-and-comparison-of-sorting-algorithms-1.webp' title=Til sidst øges inputstørrelsen til 10^5 (1 Lakh!), hvilket sandsynligvis er den ideelle størrelse, der bruges i praktiske scenarier. Sammenlignet med det tidligere input n = 1000, hvor flette sorteringsslag indsættelsessortering ved at køre 10 gange hurtigere her, er forskellen endnu mere signifikant. Flet sorteringsslag indsættelse sorteres efter 100 gange! Den hybride algoritme, som vi har skrevet, udfører faktisk den traditionelle flettesortering ved at køre 0,01 sek. hurtigere. Og endelig qsort() biblioteksfunktionen beviser os endelig, at implementering også spiller en afgørende rolle, mens den måler køretiderne omhyggeligt ved at køre 3 millisekunder hurtigere! :D
Asymptotisk analyse af Algos_2' loading='lazy' src='//techcodeview.com/img/analysis-of-algorithms/63/asymptotic-analysis-and-comparison-of-sorting-algorithms-2.webp' title=

Bemærk: Kør ikke ovenstående program med n >= 10^6, da det vil tage meget computerkraft. Tak og god kodning! :)

Opret quiz