logo

Asimptotiskā analīze un šķirošanas algoritmu salīdzināšana

Ir vispāratzīts fakts, ka sapludināšanas kārtošana notiek ātrāk nekā ievietošanas kārtošana. Izmantojot asimptotiskā analīze . mēs varam pierādīt, ka sapludināšanas kārtošana notiek O(nlogn) laikā un ievietošanas kārtošana notiek O(n^2). Tas ir acīmredzams, jo sapludināšanas kārtošana izmanto sadali un iekaro pieeju, rekursīvi risinot problēmas, kur ievietošanas kārtošanai tiek izmantota pakāpeniska pieeja. Ja mēs vēl vairāk pārbaudīsim laika sarežģītības analīzi, mēs uzzināsim, ka ievietošanas kārtošana nav tik slikta. Pārsteidzoši, ievietošanas kārtošanas sitieni apvieno kārtošanu, izmantojot mazāku ievades izmēru. Tas ir tāpēc, ka ir maz konstantu, kuras mēs ignorējam, secinot laika sarežģītību. Lielākiem ievades izmēriem 10^4 secībā tas neietekmē mūsu funkcijas darbību. Bet, ja ievades lielums ir mazāks par 40, tad vienādojuma konstantes dominē pār ievades lielumu “n”. Līdz šim viss ir labi. Bet mani neapmierināja šāda matemātiskā analīze. Kā datorzinātņu bakalaura grāda iegūšanai mums ir jātic koda rakstīšanai. Esmu uzrakstījis C programmu, lai izjustu, kā algoritmi sacenšas viens ar otru par dažādiem ievades izmēriem. Un arī kāpēc tiek veikta tik stingra matemātiskā analīze, lai noteiktu šo šķirošanas algoritmu darbības laika sarežģītību.

c kods abs

Īstenošana:

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

Esmu salīdzinājis šādu algoritmu darbības laikus:



ankita Deivs
  • Ievietošanas kārtošana : tradicionālais algoritms bez izmaiņām/optimizācijas. Tas ļoti labi darbojas mazākiem ievades izmēriem. Un jā, tas pārspēj sapludināšanas veidu
  • Pieņem likteni : Ievēro "skaldi un valdi" pieeju. Ievades lielumam 10^5 šis algoritms ir pareizā izvēle. Tas padara ievietošanas kārtošanu nepraktisku tik lieliem ievades izmēriem.
  • Ievietošanas kārtošanas un sapludināšanas kārtošanas kombinētā versija: Esmu nedaudz mainījis sapludināšanas kārtošanas loģiku, lai sasniegtu ievērojami labāku darbības laiku mazākiem ievades izmēriem. Kā mēs zinām, sapludināšanas kārtošana sadala ievadi divās daļās, līdz tā ir pietiekami triviāla, lai kārtotu elementus. Bet šeit, kad ievades lielums nokrītas zem sliekšņa, piemēram, “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.
  • Ātrā kārtošana: Es neesmu ieviesis šo procedūru. Šī ir bibliotēkas funkcija qsort(), kas ir pieejama . Esmu apsvēris šo algoritmu, lai uzzinātu ieviešanas nozīmi. Lai pēc iespējas samazinātu soļu skaitu un maksimāli izmantotu pamatā esošās valodas primitīvus, ir vajadzīgas lielas programmēšanas zināšanas, lai pēc iespējas labāk ieviestu algoritmu. Tas ir galvenais iemesls, kāpēc ir ieteicams izmantot bibliotēkas funkcijas. Tie ir rakstīti, lai apstrādātu jebko un visu. Tie tiek optimizēti, cik vien iespējams. Un, pirms es aizmirstu no analīzes, qsort() darbojas pārsteidzoši ātri praktiski jebkurā ievades lielumā!

Analīze:

  • Ievade: Lietotājam ir jānorāda, cik reižu viņš/viņa vēlas pārbaudīt algoritmu, kas atbilst testa gadījumu skaitam. Katram testa gadījumam lietotājam ir jāievada divi ar atstarpi atdalīti veseli skaitļi, kas apzīmē ievades lielumu “n” un “num_of_times”, kas norāda, cik reižu viņš/viņa vēlas palaist analīzi un noteikt vidējo. (Paskaidrojums: ja “num_of_times” ir 10, tad katrs no iepriekš norādītajiem algoritmiem tiek izpildīts 10 reizes un tiek ņemts vidējais rādītājs. Tas tiek darīts, jo ievades masīvs tiek ģenerēts nejauši atbilstoši jūsu norādītajam ievades lielumam. Ievades masīvs var būt visu sakārtots. Mūsu tas varētu atbilst sliktākajam gadījumam .t.i., šādas masīvas tiek izvairīšanās secībā dilstošā secībā. palaist 'num_of_times' un tiek ņemts vidējais rādītājs.) clock() rutīna un makro CLOCKS_PER_SEC no tiek izmantots, lai mērītu patērēto laiku. Kompilācija: Iepriekš minēto kodu esmu uzrakstījis Linux vidē (Ubuntu 16.04 LTS). Kopējiet iepriekš minēto koda fragmentu. Kompilējiet to, izmantojot gcc atslēgu ievadē, kā norādīts, un apbrīnojiet šķirošanas algoritmu jaudu!
  • Rezultāti:  Kā redzat maziem ievades izmēriem ievietošanas kārtošanas sitieni sapludināt kārtot pēc 2 * 10^-6 sek. Bet šī laika atšķirība nav tik būtiska. No otras puses, hibrīdais algoritms un qsort () bibliotēkas funkcija darbojas tikpat labi kā ievietošanas kārtošana. Algos_0 asimptotiskā analīze' src='//techcodeview.com/img/analysis-of-algorithms/63/asymptotic-analysis-and-comparison-of-sorting-algorithms.webp' title=Ievades lielums tagad ir palielināts aptuveni 100 reizes līdz n = 1000 no n = 30. Tagad atšķirība ir jūtama. Sapludināšanas kārtošana darbojas 10 reizes ātrāk nekā ievietošanas kārtošana. Atkal ir saikne starp hibrīda algoritma veiktspēju un qsort() rutīnu. Tas liek domāt, ka qsort () ir ieviests tādā veidā, kas ir vairāk vai mazāk līdzīgs mūsu hibrīdajam algoritmam, t.i., pārslēdzoties starp dažādiem algoritmiem, lai no tiem gūtu maksimālu labumu. Algos_1 asimptotiskā analīze' loading='lazy' src='//techcodeview.com/img/analysis-of-algorithms/63/asymptotic-analysis-and-comparison-of-sorting-algorithms-1.webp' title=Visbeidzot ievades lielums tiek palielināts līdz 10^5 (1 lakh!), kas, visticamāk, ir ideālais izmērs, ko izmanto praktiskajos scenārijos. Salīdzinot ar iepriekšējo ievadi n = 1000, kur sapludināšanas kārtošana sitiena ievietošanas kārtošana, palaižot 10 reizes ātrāk, šeit atšķirība ir vēl būtiskāka. Sapludināt kārtot sitienus ievietošanas kārtot pēc 100 reizēm! Hibrīda algoritms, kuru esam uzrakstījuši, faktiski veic tradicionālo sapludināšanas kārtošanu, palaižot par 0,01 s ātrāk. Un visbeidzot, bibliotēkas funkcija qsort() beidzot pierāda, ka ieviešanai arī ir izšķiroša nozīme, rūpīgi mērot darbības laiku, palaižot par 3 milisekundēm ātrāk! :D
Algos_2 asimptotiskā analīze' loading='lazy' src='//techcodeview.com/img/analysis-of-algorithms/63/asymptotic-analysis-and-comparison-of-sorting-algorithms-2.webp' title=

Piezīme. Nepalaidiet iepriekš minēto programmu ar n >= 10^6, jo tā prasīs daudz skaitļošanas jaudas. Paldies un laimīgu kodēšanu! :)

Izveidojiet viktorīnu