Perbandingan Penukaran Exchange



Algoritma bola dalam ruang hampa sangat bagus. Namun, mari kita turun dari surga ke bumi yang penuh dosa dan melihat bagaimana semua keindahan teoretis ini akan membuktikan dirinya dalam praktik.

Analisis jenis kelas berikutnya akan berakhir dengan tes untuk jenis kelas. Hari ini kita akan lari (bukan dalam arti membuang, tetapi dalam arti berlari dalam ujian) menyortir pertukaran. Berbagai kelas lain akan dijalankan nanti.

Dalam perlombaan hari ini terlibat:

Kelompok terlemah


Algoritma ini tidak mengklaim apa pun karena kompleksitas waktu yang tertekan. Kami mengujinya murni untuk referensi.

Semacam konyol
Penyortiran lambat
Pemilahan bodoh

Ini bahkan lebih menarik bukan hanya seberapa baik mereka, tetapi seberapa buruk mereka dalam bisnis, dan mana di antara mereka yang terburuk.

Grup utama


Di sini dikumpulkan pertukaran sortir a la O ( n 2 ). Penyortiran katai (dan pengoptimalannya) + segala macam variasi penyortiran gelembung.

Sortir Kurcaci
Penyortiran katai (dioptimalkan)
Sortir Bubble
Sortasi koktail
Sortir genap

Ini adalah bagian paling menarik dari pengukuran hari ini. Di antara perwakilan dari kelompok utama inilah perjuangan yang paling keras kepala diharapkan.

Kelompok terkuat


Salah satu varietas gelembung - pengurutan dengan sisir - berhasil mengatasi penghalang O ( n 2 ) dan mencapai waktu yang dihormati O ( n log n ). Jadi dalam kompetisi kami, sortasi cepat memiliki saingan yang layak. Juga, cobalah k-sort yang baru ditemukan , yang merupakan variasi dari sort cepat.

Urutkan sikat rambut
Sortir cepat
K sorting

Omong-omong, yang terkuat tidak hanya sebanding satu sama lain. Pada beberapa set data, kelompok utama akan berada dalam persaingan yang serius.

Kode sumber


Jenis Pertukaran Python
def stooge_rec(data, i = 0, j = None): if j is None: j = len(data) - 1 if data[j] < data[i]: data[i], data[j] = data[j], data[i] if j - i > 1: t = (j - i + 1) // 3 stooge_rec(data, i, j - t) stooge_rec(data, i + t, j) stooge_rec(data, i, j - t) return data def stooge(data): return stooge_rec(data, 0, len(data) - 1) def slow_rec(data, i, j): if i >= j: return data m = (i + j) // 2 slow_rec(data, i, m) slow_rec(data, m + 1, j) if data[m] > data[j]: data[m], data[j] = data[j], data[m] slow_rec(data, i, j - 1) return data def slow(data): return slow_rec(data, 0, len(data) - 1) def stupid(data): i, size = 1, len(data) while i < size: if data[i - 1] > data[i]: data[i - 1], data[i] = data[i], data[i - 1] i = 1 else: i += 1 return data def gnome(data): i, size = 1, len(data) while i < size: if data[i - 1] <= data[i]: i += 1 else: data[i - 1], data[i] = data[i], data[i - 1] if i > 1: i -= 1 return data def gnome_opt(data): i, j, size = 1, 2, len(data) while i < size: if data[i - 1] <= data[i]: i, j = j, j + 1 else: data[i - 1], data[i] = data[i], data[i - 1] i -= 1 if i == 0: i, j = j, j + 1 return data def bubble(data): changed = True while changed: changed = False for i in range(len(data) - 1): if data[i] > data[i+1]: data[i], data[i+1] = data[i+1], data[i] changed = True return data def shaker(data): up = range(len(data) - 1) while True: for indices in (up, reversed(up)): swapped = False for i in indices: if data[i] > data[i+1]: data[i], data[i+1] = data[i+1], data[i] swapped = True if not swapped: return data def odd_even(data): n = len(data) isSorted = 0 while isSorted == 0: isSorted = 1 for i in range(1, n - 1, 2): if data[i] > data[i + 1]: data[i], data[i + 1] = data[i + 1], data[i] isSorted = 0 for i in range(0, n - 1, 2): if data[i] > data[i + 1]: data[i], data[i + 1] = data[i + 1], data[i] isSorted = 0 return data def comb(data): gap = len(data) swaps = True while gap > 1 or swaps: gap = max(1, int(gap / 1.25)) # minimum gap is 1 swaps = False for i in range(len(data) - gap): j = i + gap if data[i] > data[j]: data[i], data[j] = data[j], data[i] swaps = True return data def quick(data): less = [] pivotList = [] more = [] if len(data) <= 1: return data else: pivot = data[0] for i in data: if i < pivot: less.append(i) elif i > pivot: more.append(i) else: pivotList.append(i) less = quick(less) more = quick(more) return less + pivotList + more def k(data): return k_rec(data, 0, len(data) - 1) def k_rec(data, left, right): key = data[left] i = left j = right + 1 k = left + 1 p = left + 1 temp = False while j - i >= 2: if key <= data[p]: if p != j and j != right + 1: data[j] = data[p] elif j == right + 1: temp = data[p] j -= 1 p = j else: data[i] = data[p] i += 1 k += 1 p = k data[i] = key if temp: data[i + 1] = temp if left < i - 1: k_rec(data, left, i - 1) if right > i + 1: k_rec(data, i + 1, right) return data 

Urusan Pertukaran PHP
  // function StoogeSort(&$arr, $i, $j) { if($arr[$j] < $arr[$i]) list($arr[$j], $arr[$i]) = array($arr[$i], $arr[$j]); if(($j - $i) > 1) { $t = ($j - $i + 1) / 3; StoogeSort($arr, $i, $j - $t); StoogeSort($arr, $i + $t, $j); StoogeSort($arr, $i, $j - $t); } return $arr; } // function SlowSort(&$arr, $i, $j) { if($i >= $j) return $arr; $m = ($i + $j) / 2; SlowSort($arr, $i, $m); SlowSort($arr, $m + 1, $j); if($arr[$m] > $arr[$j]) list($arr[$m], $arr[$j]) = array($arr[$j], $arr[$m]); SlowSort($arr, $i, $j - 1); return $arr; } // function StupidSort($arr) { $i = 1; $size = count($arr); while($i < $size) { if($arr[$i - 1] <= $arr[$i]){ ++$i; } else { list($arr[$i - 1], $arr[$i]) = array($arr[$i], $arr[$i - 1]); $i = 1; } } return $arr; } // function GnomeSort($arr) { $i = 1; $size = count($arr); while($i < $size) { if($arr[$i - 1] <= $arr[$i]){ ++$i; } else { list($arr[$i - 1], $arr[$i]) = array($arr[$i], $arr[$i - 1]); if($i > 1) --$i; } } return $arr; } // () function GnomeSort_opt($arr) { $i = 1; $j = 2; $size = count($arr); while($i < $size) { if($arr[$i - 1] <= $arr[$i]){ $i = $j; ++$j; } else { list($arr[$i - 1], $arr[$i]) = array($arr[$i], $arr[$i - 1]); if(--$i == 0){ $i = $j; $j++; } } } return $arr; } // function BubbleSort($arr) { $c = count($arr) - 1; do { $swapped = false; for ($i = 0; $i < $c; ++$i) { if ($arr[$i] > $arr[$i + 1]) { list($arr[$i + 1], $arr[$i]) = array($arr[$i], $arr[$i + 1]); $swapped = true; } } } while($swapped); return $arr; } // function ShakerSort($arr){ do{ $swapped = false; for($i = 0; $i < count($arr); $i++){ if(isset($arr[$i + 1])) { if($arr[$i] > $arr[$i+1]) { list($arr[$i], $arr[$i + 1]) = array($arr[$i + 1], $arr[$i]); $swapped = true; } } } if ($swapped == false) break; $swapped = false; for($i = count($arr) - 1; $i >= 0; $i--){ if(isset($arr[$i - 1])){ if($arr[$i] < $arr[$i - 1]) { list($arr[$i], $arr[$i - 1]) = array($arr[$i - 1], $arr[$i]); $swapped = true; } } } } while($swapped); return $arr; } //- function OddEvenSort($arr){ $n = count($arr); $sorted = false; while(!$sorted) { $sorted = true; for($i = 1; $i < $n - 2; $i += 2) { if($arr[$i] > $arr[$i + 1]) { list($arr[$i], $arr[$i + 1]) = array($arr[$i + 1], $arr[$i]); $sorted = false; } } for($i = 0; $i < $n - 1; $i += 2) { if($arr[$i] > $arr[$i + 1]) { list($arr[$i], $arr[$i + 1]) = array($arr[$i + 1], $arr[$i]); $sorted = false; } } } } // function CombSort($arr){ $gap = count($arr); $swap = true; while($gap > 1 || $swap){ if($gap > 1) $gap /= 1.25; $swap = false; $i = 0; while($i + $gap < count($arr)){ if($arr[$i] > $arr[$i + $gap]){ list($arr[$i], $arr[$i + $gap]) = array($arr[$i + $gap], $arr[$i]); $swap = true; } ++$i; } } return $arr; } // function QuickSort($arr) { $loe = $gt = array(); if(count($arr) < 2) { return $arr; } $pivot_key = key($arr); $pivot = array_shift($arr); foreach($arr as $val){ if($val <= $pivot){ $loe[] = $val; }elseif ($val > $pivot){ $gt[] = $val; } } return array_merge(QuickSort($loe), array($pivot_key => $pivot), QuickSort($gt)); } //k- function K_Sort($arr) { K_Sort_rec($arr, 0, count($arr) - 1); return $arr; } function K_Sort_rec(&$arr, $left, $right) { $key = $arr[$left]; $i = $left; $j = $right + 1; $k = $p = $left + 1; $temp = false; while($j - $i >= 2) { if($key <= $arr[$p]) { if(($p != $j) && ($j != ($right + 1))) { $arr[$j] = $arr[$p]; } elseif($j == ($right + 1)) { $temp = $arr[$p]; } --$j; $p = $j; } else { $arr[$i] = $arr[$p]; ++$i; ++$k; $p = $k; } } $arr[$i] = $key; if($temp) $arr[$i + 1] = $temp; if($left < ($i - 1)) K_Sort_rec($arr, $left, $i - 1); if($right > ($i + 1)) K_Sort_rec($arr, $i + 1, $right); } 

Waktu


Waktu diukur di mana-mana dalam hitungan detik akurat untuk mikrodetik.

Jika satu pak array (dari sepuluh, seratus atau bahkan satu juta keping) segera dimasukkan ke algoritma, maka total waktu ditunjukkan.

Besi adalah laptop lama saya yang setia yang tahu waktu yang lebih baik. Jadi, sangat mungkin, semuanya akan bekerja untuk Anda lebih cepat.

Yang terburuk dari yang terburuk


Segera berurusan dengan orang luar. Array untuk 40/50/60 elemen yang berisi angka acak dari 0 hingga 9 disiapkan untuk mereka.
type = acak, unik = Salah, min = 0, maks = 9, jumlah = 1
ukuran
405060
PythonPhpPythonPhpPythonPhp
0,0040,0010,0050,0030,0070,004
0,0070,0090,0180,0090,0180,023
0,028.266470,05320.87220,1290145.9786

Penyortiran bodoh adalah urutan besarnya lebih cepat dari penyortiran konyol, dan penyortiran konyol adalah urutan besarnya lebih cepat dari lamban. Tidak ada yang perlu diperhatikan.

Petani menengah


Untuk memeriksa rata-rata, paket seratus array masing-masing 100 elemen (nomor unik 100 hingga 999), serta paket sepuluh array masing-masing elemen 1000 (nomor unik 1000 hingga 9999) dihasilkan.

Susunan acak, susunan yang hampir diurutkan, dan susunan urutan terbalik disiapkan.

Middleweight: Acak


type = acak, unik = Benar
size (min to max) × hitung
100 (100 hingga 999) × 1001000 (1000 hingga 9999) × 10
PythonPhpPythonPhp
0,141010,189011.591091.7251
0,206010,223012.330132.09712
0,153010,229011.67112.23613
0,216010,264022.555152.73116
0,167010,334021.72513.13218

Sekitar hasil yang sama untuk semua. Implementasi python bekerja sedikit lebih cepat daripada PHP.

Middles: Hampir diurutkan


type = hampir, unik = Benar
size (min to max) × hitung
100 (100 hingga 999) × 1001000 (1000 hingga 9999) × 10
PythonPhpPythonPhp
0,0090,0050,0090,005
0,0090,0140,010,014
0,010,010,0110,008
0,0110,0080,0130,008
0,0110,0170,0130,017

Semua memiliki hasil yang sama, plus atau minus. Dari yang menarik: pemesanan data parsial puluhan kali meningkatkan kinerja semua algoritma.

Tengah: Terbalik


type = terbalik, unik = Benar
size (min to max) × hitung
100 (100 hingga 999) × 1001000 (1000 hingga 9999) × 10
PythonPhpPythonPhp
0,268010,359023.100183.4292
0,396020,453034.497264.19524
0,227010,384022.481143.64421
0,302020,426033.344194.17524
0,304020,644043.365196.22036

Ada kesimpulan yang menarik - urutan terbalik tidak terlalu mempengaruhi kecepatan, yang jatuh hanya 2 kali dibandingkan dengan angka acak.

Tengah: Biner


type = acak, unik = Salah, min = 0, maks = 1
ukuran × hitungan
100 × 1001000 × 10
PythonPhpPythonPhp
0,0730,0940,811050,86905
0,104010,113011.163071.06606
0,088010,129010,864051.18207
0,115010,150011.301071.41608
0,114010,258011.319082.46314

Dari kurang lebih menarik: jika alih-alih angka dari 100 hingga 9999 Anda mengurutkan nol dan yang, maka indikator kecepatan tidak akan tumbuh banyak.

Juara


Intrik utama di sini adalah apakah menyortir dengan sisir dan menyortir k dapat memeras puasa dari alas?

Juara: Acak


Untuk mengetahuinya, mari kita bentuk paket array acak: 10 buah 10 ribu elemen dan 10 buah 100 ribu elemen. Saya ingin memberikan jutawan untuk algoritma input, tetapi laptop tidak menarik. Entah tidak ada RAM yang cukup, maka kedalaman rekursi terlalu besar.
type = acak, unik = Salah, hitung = 10
ukuran (minimal maks)
10000 (dari 10.000 hingga 99999)100000 (dari 100000 hingga 999999)
PythonPhpPythonPhp
0,802050,6310410.45068.24647
0,477031.640096.6513826.5435
0,900052.1861315.808929.4867

Penyortiran Python K lebih lambat, dan PHP lebih cepat daripada penyortiran cepat.
Menyortir sisir dibandingkan dengan yang cepat terlihat padat, meskipun lebih rendah dari itu di semua tes (dan yang ini dan yang berikutnya) untuk setiap kumpulan data.

Namun, ada sisir dan keuntungan penting yang tak terbantahkan. Itu tidak memiliki rekursi dan karena itu, tidak seperti rekan-rekannya, ia berhasil memproses array jutaan elemen.

Kasus khusus


Meskipun juara ratusan kali lebih cepat daripada petani rata-rata, pada beberapa dataset tertentu, pengurutan menunjukkan lebih sederhana bahwa mereka tidak mudah dijahit.

Kasus khusus: Hampir diurutkan


Mari kita coba array yang hampir diurutkan, hanya mengambil ukuran yang lebih besar daripada yang diuji untuk petani rata-rata.

Paket 10 array masing-masing 10 ribu dan paket 10 array masing-masing 100 ribu elemen.
type = hampir, unik = Salah
size (min to max) × hitung
10.000 (dari 10.000 hingga 99999) × 10100000 (dari 100000 hingga 999999) × 10
PythonPhpPythonPhp
0,432020,0580,817050,58003
0,0840,0620,858050,54003
0,116010,124011.419081.36008
0,140010,119011.834111.41708
0,138010,231011.64912.56915
?122.088??
0,715041.589099,7835619.4731

Penyortiran cepat tidak ada di sini sama sekali - tidak ada cukup RAM. Pada saat yang sama, array acak dengan 10/100 ribu elemen diproses oleh quicksort dengan bang. k-sort mengalami masalah serupa, hanya menarik 10 ribu di PHP. Array besar hampir diurutkan adalah kasus merosot untuk penyortiran cepat dan modifikasinya. Karena pengaturan elemen ini, rekursi membagi array menjadi bagian-bagian untuk hampir setiap pasangan elemen yang berdekatan, yang mengarah pada jumlah panggilan rekursi maksimum yang dimungkinkan.

Omong-omong, situasinya mirip dengan array berurutan terbalik besar. Masalah yang sama muncul dengan yang hampir teratur - algoritma harus memanggil dirinya sendiri untuk setiap pasangan elemen yang berdekatan.

Menyortir dengan sisir, meskipun berfungsi satu setengah hingga dua kali lebih lambat daripada cepat atau k (secara umum), tetapi tidak memiliki memori yang disebutkan di atas meluap. Tanpa rekursi - tidak ada masalah.

Nah, kami perhatikan bahwa semua petani menengah bersama-sama menyusul para juara dalam susunan besar yang hampir disortir. Kemudian prinsip itu bekerja: "Pergi dengan tenang - Anda akan melanjutkan."

Kasus khusus: Juta mawar merah array kecil


Array kecil - elemen petani menengah. Jika Anda perlu memproses sejumlah besar set angka kecil, maka Anda bisa mendapatkan keuntungan waktu dengan mengambil gelembung "primitif" atau semacam gnome. Dan gunakan penyortiran cepat dan yang lain menyukainya untuk tugas yang lebih besar.
type = acak, unik = Benar
size (min to max) × hitung
5 (10 hingga 99) × 1.000.00010 (10 hingga 99) × 1.000.000
PythonPhpPythonPhp
11.7726717.88824.2903933.6659
12.5187218.16529.3326835.202
15.4418817.86130.0667236.7911
14.1868119.783131.6598139.3533
13.6397824.337428.4166252.864
14.2188121.145225.8054832.5419
14.5868328.501624.8594250.3139
18.5380630.723829.664758.2493


Dengan penyortiran pertukaran, semuanya jelas. Sekarang untuk bagian yang sangat menarik - urutkan berdasarkan sisipan.

Referensi


Aplikasi-Excel , AlgoLab , yang dengannya Anda dapat selangkah demi selangkah melihat visualisasi dari jenis-jenis ini (dan bukan hanya ini).

Semua array dalam bentuk JSON juga diletakkan di disk Google .

Wiki - Silly / Stooge , Lambat , Kerdil / Gnome , Gelembung / Gelembung , Pengocok / Pengocok , Ganjil / Genap , Sisir / Sisir , Cepat / Cepat

Artikel Seri


Source: https://habr.com/ru/post/id415691/


All Articles