logo

Asimptotinė rūšiavimo algoritmų analizė ir palyginimas

Gerai žinomas faktas, kad sujungimo rūšiavimas vyksta greičiau nei įterpimo rūšiavimas. Naudojant asimptotinė analizė . galime įrodyti, kad sujungimo rūšiavimas vyksta per O(nlogn) laiką, o įterpimo rūšiavimas trunka O(n^2). Tai akivaizdu, nes sujungimo rūšiavimas naudoja „skaldyk ir užkariauk“ metodą, rekursyviai sprendžiant problemas, kai įterpiant rūšiuojant laikomasi laipsniško požiūrio. Jei dar labiau išnagrinėsime laiko sudėtingumo analizę, sužinosime, kad įterpimo rūšiavimas nėra toks blogas. Stebėtina, kad įterpimo rūšiavimo ritmai sujungia rūšiavimą esant mažesniam įvesties dydžiui. Taip yra todėl, kad yra keletas konstantų, į kurias mes nepaisome, kai nustatome laiko sudėtingumą. Naudojant didesnius įvesties dydžius 10^4 eilės tai neturi įtakos mūsų funkcijos veikimui. Bet kai įvesties dydis nukrenta žemiau, tarkime, mažesnis nei 40, tada lygties konstantos dominuoja įvesties dydžiui „n“. Kol kas viskas gerai. Bet manęs tokia matematinė analizė netenkino. Būdami kompiuterių mokslų bakalauro laipsniais, turime tikėti kodo rašymu. Parašiau C programą, kad pajusčiau, kaip algoritmai konkuruoja tarpusavyje dėl įvairių įvesties dydžių. Taip pat kodėl tokia griežta matematinė analizė atliekama nustatant šių rūšiavimo algoritmų veikimo laiko sudėtingumą.

Instagram privalumai asmeniniam naudojimui

Įgyvendinimas:

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(); 

Aš palyginau šių algoritmų veikimo laiką:



neigimas diskreti matematika
  • Įterpimo rūšiavimas : tradicinis algoritmas be pakeitimų / optimizavimo. Tai labai gerai veikia naudojant mažesnius įvesties dydžius. Ir taip, jis įveikia sujungimo rūšiavimą
  • Ateis likimas : Vadovaujasi „skaldyk ir valdyk“ principu. 10^5 įvesties dydžiams šis algoritmas yra tinkamas pasirinkimas. Dėl to įterpimo rūšiavimas yra nepraktiškas esant tokiems dideliems įvesties dydžiams.
  • Kombinuota įterpimo rūšiavimo ir sujungimo rūšiavimo versija: Šiek tiek patobulinau sujungimo rūšiavimo logiką, kad būtų pasiektas žymiai geresnis mažesnių įvesties dydžių veikimo laikas. Kaip žinome, sujungimo rūšiavimas padalija įvestį į dvi dalis, kol ji tampa pakankamai nereikšminga, kad būtų galima rūšiuoti elementus. Bet čia, kai įvesties dydis nukrenta žemiau slenksčio, pvz., „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.
  • Greitas rūšiavimas: Šios procedūros neįgyvendinau. Tai bibliotekos funkcija qsort(), kurią galima rasti . Apsvarsčiau šį algoritmą, kad sužinočiau įgyvendinimo reikšmę. Norint maksimaliai sumažinti žingsnių skaičių ir maksimaliai panaudoti pagrindinės kalbos primityvus, kad algoritmas būtų įgyvendintas geriausiu įmanomu būdu, reikia daug programavimo žinių. This is the main reason why it is recommended to use library functions. Jie parašyti tvarkyti bet ką ir viską. Jie optimizuoja maksimaliai. Ir prieš tai pamiršdamas iš savo analizės, qsort() veikia nepaprastai greitai naudojant beveik bet kokį įvesties dydį!

Analizė:

  • Įvestis: Vartotojas turi nurodyti, kiek kartų jis/ji nori išbandyti algoritmą, atitinkantį testavimo atvejų skaičių. Kiekvienam bandomajam atvejui vartotojas turi įvesti du tarpais atskirtus sveikuosius skaičius, nurodančius įvesties dydį „n“ ir „num_of_times“, nurodantį, kiek kartų jis/ji nori atlikti analizę ir apskaičiuoti vidurkį. (Paaiškinimas: jei „num_of_times“ yra 10, tada kiekvienas iš aukščiau nurodytų algoritmų paleidžiamas 10 kartų ir imamas vidurkis. Taip daroma, nes įvesties masyvas sugeneruojamas atsitiktinai, atsižvelgiant į jūsų nurodytą įvesties dydį. Įvesties masyvas gali būti visas surūšiuotas. Tai gali atitikti blogiausią atvejį . t. y. tokie masyvo veikimo laikai yra mažėjančia tvarka. paleiskite „num_of_times“ ir imamas vidurkis.) clock() rutina ir CLOCKS_PER_SEC makrokomanda nuo naudojama laikui matuoti. Kompiliacija: aukščiau pateiktą kodą parašiau Linux aplinkoje (Ubuntu 16.04 LTS). Nukopijuokite aukščiau esantį kodo fragmentą. Sukompiliuokite jį naudodami gcc raktą įvestyje, kaip nurodyta, ir grožėkitės rūšiavimo algoritmų galia!
  • Rezultatai:  Kaip matote esant mažiems įvesties dydžiams įterpimo rūšiavimo ritmai sujungiami rūšiuojami pagal 2 * 10^-6 sek. Tačiau šis laiko skirtumas nėra toks reikšmingas. Kita vertus, hibridinis algoritmas ir qsort() bibliotekos funkcija veikia taip pat gerai, kaip ir įterpimo rūšiavimas. Asimptotinė Algos_0 analizė' src='//techcodeview.com/img/analysis-of-algorithms/63/asymptotic-analysis-and-comparison-of-sorting-algorithms.webp' title=Įvesties dydis dabar padidintas maždaug 100 kartų iki n = 1000 nuo n = 30. Dabar skirtumas yra apčiuopiamas. Rūšiavimas sujungiamas 10 kartų greičiau nei įterpimo rūšiavimas. Vėlgi yra ryšys tarp hibridinio algoritmo ir qsort() rutinos. Tai rodo, kad qsort () yra įdiegtas tokiu būdu, kuris yra daugiau ar mažiau panašus į mūsų hibridinį algoritmą, t. y. perjungiamas tarp skirtingų algoritmų, kad iš jų būtų kuo geriau. Asimptotinė Algos_1 analizė' loading='lazy' src='//techcodeview.com/img/analysis-of-algorithms/63/asymptotic-analysis-and-comparison-of-sorting-algorithms-1.webp' title=Galiausiai įvesties dydis padidinamas iki 10^5 (1 tūkst.!), kuris greičiausiai yra idealus dydis, naudojamas praktiniuose scenarijuose. Palyginti su ankstesne įvestimi n = 1000, kur sujungimo rūšiavimo ritmo įterpimo rūšiavimas vyksta 10 kartų greičiau, skirtumas yra dar reikšmingesnis. Sujungti rūšiavimą plaka įterpimo rūšiuoti pagal 100 kartų! Hibridinis algoritmas, kurį parašėme, iš tikrųjų atlieka tradicinį sujungimo rūšiavimą, paleidžiant 0,01 sek. greičiau. Ir galiausiai, bibliotekos funkcija qsort() pagaliau įrodo, kad diegimas taip pat atlieka lemiamą vaidmenį, kai kruopščiai matuojamas veikimo laikas, kai jis veikia 3 milisekundėmis greičiau! :D
Asimptotinė Algos_2 analizė' loading='lazy' src='//techcodeview.com/img/analysis-of-algorithms/63/asymptotic-analysis-and-comparison-of-sorting-algorithms-2.webp' title=

Pastaba: nevykdykite aukščiau nurodytos programos, kai n >= 10^6, nes tai užims daug skaičiavimo galios. Ačiū ir sėkmingo kodavimo! :)

Sukurti viktoriną