병합 정렬이 삽입 정렬보다 빠르게 실행된다는 것은 잘 알려진 사실입니다. 사용 점근적 분석 . 병합 정렬은 O(nlogn) 시간에 실행되고 삽입 정렬에는 O(n^2) 시간이 걸린다는 것을 증명할 수 있습니다. 병합 정렬은 삽입 정렬이 증분식 접근 방식을 따르는 문제를 재귀적으로 해결하여 분할 정복 접근 방식을 사용하기 때문에 분명합니다. 시간 복잡도 분석을 더욱 면밀히 조사하면 삽입 정렬이 그다지 나쁘지 않다는 것을 알게 될 것입니다. 놀랍게도 삽입 정렬은 더 작은 입력 크기에서 병합 정렬을 능가합니다. 이는 시간 복잡도를 추론할 때 무시하는 상수가 거의 없기 때문입니다. 10^4 정도의 더 큰 입력 크기에서는 함수의 동작에 영향을 주지 않습니다. 그러나 입력 크기가 40 미만으로 떨어지면 방정식의 상수가 입력 크기 'n'을 지배합니다. 지금까지는 너무 좋았습니다. 하지만 나는 그런 수학적 분석에 만족하지 못했습니다. 컴퓨터 과학 학부생으로서 우리는 코드 작성을 믿어야 합니다. 나는 다양한 입력 크기에 대해 알고리즘이 서로 어떻게 경쟁하는지 알아보기 위해 C 프로그램을 작성했습니다. 또한 이러한 정렬 알고리즘의 실행 시간 복잡성을 확립하기 위해 엄격한 수학적 분석이 수행되는 이유도 있습니다.
파이썬 뱀 대 아나콘다
구현:
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();
다음 알고리즘의 실행 시간을 비교했습니다.
추상 클래스에 생성자가 있을 수 있나요?
- 삽입 정렬 : 수정/최적화가 없는 기존 알고리즘입니다. 더 작은 입력 크기에 대해 매우 잘 수행됩니다. 그리고 그렇습니다. 병합 정렬보다 낫습니다.
- 운명이 간다 : 분할 정복 접근법을 따릅니다. 10^5 차수의 입력 크기의 경우 이 알고리즘이 올바른 선택입니다. 이렇게 큰 입력 크기에 대해서는 삽입 정렬을 비현실적으로 만듭니다.
- 삽입 정렬과 병합 정렬을 결합한 버전: 더 작은 입력 크기에 대해 훨씬 더 나은 실행 시간을 달성하기 위해 병합 정렬 논리를 약간 조정했습니다. 우리가 알고 있듯이 병합 정렬은 요소를 정렬할 수 있을 만큼 사소해질 때까지 입력을 두 부분으로 나눕니다. 하지만 여기서는 입력 크기가 '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.
- 빠른 정렬: 이 절차를 구현하지 않았습니다. 이것은 에서 사용할 수 있는 라이브러리 함수 qsort()입니다. 구현의 중요성을 알기 위해 이 알고리즘을 고려했습니다. 가능한 최선의 방법으로 알고리즘을 구현하기 위해 단계 수를 최소화하고 기본 언어 기본 요소를 최대한 활용하려면 상당한 프로그래밍 전문 지식이 필요합니다. 이것이 라이브러리 함수를 사용하는 것이 권장되는 주된 이유입니다. 그들은 무엇이든 처리하기 위해 작성되었습니다. 가능한 한 최대로 최적화합니다. 그리고 내 분석을 잊어버리기 전에 qsort()는 거의 모든 입력 크기에서 엄청나게 빠르게 실행됩니다!
분석:
- 입력: 사용자는 테스트 케이스 수에 해당하는 알고리즘을 테스트하려는 횟수를 제공해야 합니다. 각 테스트 사례에 대해 사용자는 입력 크기 'n'을 나타내는 두 개의 공백으로 구분된 정수와 분석을 실행하고 평균을 구하려는 횟수를 나타내는 'num_of_times'를 입력해야 합니다. (설명: 'num_of_times'가 10이면 위에 지정된 각 알고리즘은 10번 실행되고 평균이 계산됩니다. 이는 입력 배열이 사용자가 지정한 입력 크기에 따라 무작위로 생성되기 때문에 수행됩니다. 입력 배열은 모두 정렬될 수 있습니다. 우리는 최악의 경우, 즉 내림차순에 해당할 수 있습니다. 이러한 입력 배열의 실행 시간을 피하기 위해 알고리즘은 'num_of_times'를 실행하고 평균을 취합니다.) clock() 루틴 및 CLOCKS_PER_SEC 매크로는 소요 시간을 측정하는 데 사용됩니다. 컴파일: 위의 코드는 Linux 환경(Ubuntu 16.04 LTS)에서 작성했습니다. 위의 코드 조각을 복사하세요. 지정된 대로 입력에서 gcc 키를 사용하여 컴파일하고 정렬 알고리즘의 성능에 감탄하세요!
- 결과: 작은 입력 크기에서 볼 수 있듯이 삽입 정렬은 병합 정렬보다 2 * 10^-6초 더 빠릅니다. 그러나 이러한 시간의 차이는 그다지 중요하지 않습니다. 반면에 하이브리드 알고리즘과 qsort() 라이브러리 기능은 모두 삽입 정렬만큼 좋은 성능을 발휘합니다.
이제 입력 크기가 n = 30에서 n = 1000으로 약 100배 증가했습니다. 이제 그 차이가 눈에 띕니다. 병합 정렬은 삽입 정렬보다 10배 빠르게 실행됩니다. 하이브리드 알고리즘의 성능과 qsort() 루틴 사이에는 다시 연관성이 있습니다. 이는 qsort()가 우리의 하이브리드 알고리즘과 다소 유사한 방식으로 구현된다는 것을 의미합니다. 즉, 서로 다른 알고리즘 사이를 전환하여 최상의 결과를 얻습니다.
마지막으로 입력 크기는 실제 시나리오에서 사용되는 가장 이상적인 크기인 10^5(1 Lakh!)로 증가됩니다. 병합 정렬이 삽입 정렬을 10배 빠르게 실행하는 이전 입력 n = 1000과 비교하면 차이가 훨씬 더 커집니다. 병합 정렬이 삽입 정렬보다 100배 더 좋습니다! 실제로 우리가 작성한 하이브리드 알고리즘은 0.01초 더 빠르게 실행하여 기존 병합 정렬을 수행합니다. 그리고 마지막으로 qsort() 라이브러리 함수는 3밀리초 더 빠르게 실행하여 실행 시간을 꼼꼼하게 측정하는 동시에 구현도 중요한 역할을 한다는 것을 마침내 증명합니다! :디
참고: n >= 10^6인 경우 위 프로그램을 실행하지 마세요. 컴퓨팅 성능이 많이 소모되기 때문입니다. 감사합니다. 즐거운 코딩 되세요! :)
퀴즈 만들기