مقارنة فرز التبادل



الخوارزميات الكروية في الفراغ رائعة. ومع ذلك ، دعونا ننزل من السماء إلى الأرض الخاطئة ونرى كيف سيثبت كل هذا الجمال النظري نفسه في الممارسة.

سينتهي تحليل الفئة التالية من الأنواع باختبارات لأنواع الصف. اليوم سنجري (ليس بمعنى التخلص ، ولكن بمعنى الركض في الاختبارات) فرز التبادلات. سيتم تشغيل أنواع من الفئات الأخرى في وقت لاحق.

تشارك في سباق اليوم:

أضعف مجموعة


هذه الخوارزميات لا تدعي أي شيء بسبب تعقيد الوقت الكئيب. نحن اختبارها بحتة كمرجع.

فرز سخيف
الفرز البطيء
الفرز البكم

إنه أكثر إثارة للاهتمام ليس فقط مدى روعتهم ، ولكن مدى سوءهم في العمل ، وأيهم أسوأ.

المجموعة الرئيسية


تجمع هنا الفرز التبادلي أ لا O ( ن 2 ). الفرز القزم (وتحسينه) + جميع أنواع الاختلافات في فرز الفقاعة.

فرز قزم
فرز قزم (محسن)
فرز الفقاعة
فرز كوكتيل
فرز فردي

هذا هو الجزء الأكثر إثارة للاهتمام من القياسات اليوم. من بين ممثلي المجموعة الرئيسية يتوقع النضال الأكثر عنادا.

أقوى مجموعة


تمكن أحد أصناف الفقاعة - الفرز بواسطة مشط - من التغلب على الحاجز O ( n 2 ) والوصول إلى O ( n log n ) العريق . لذلك في منافستنا ، فإن الفرز السريع له منافس جدير. أيضًا ، جرب الفرز الكوري الجديد الذي تم اختراعه حديثًا ، وهو نوع من الفرز السريع.

فرز الفرشاة
فرز سريع
الفرز ك

الأقوى ، بالمناسبة ، ليست فقط قابلة للمقارنة مع بعضها البعض. في بعض مجموعات البيانات ، ستكون المجموعة الرئيسية في منافسة شديدة.

كود المصدر


أنواع Python Exchange
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 

أنواع تبادل 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); } 

الوقت


يتم قياس الوقت في كل مكان بالثواني بدقة إلى ميكروثانية.

إذا تم تغذية حزمة من المصفوفات (من أصل عشرة أو مائة أو حتى مليون قطعة) على الفور إلى الخوارزمية ، فسيتم الإشارة إلى الوقت الإجمالي.

الحديد هو جهاز الكمبيوتر المحمول القديم المخلص الذي عرف أوقاتًا أفضل. لذا ، ربما ، كل شيء سيعمل من أجلك بشكل أسرع.

أسوأ الأسوأ


تعامل فورًا مع الغرباء. يتم إعداد صفائف لعناصر 40/50/60 تحتوي على أرقام عشوائية من 0 إلى 9.
النوع = عشوائي ، فريد = خطأ ، الحد الأدنى = 0 ، الحد الأقصى = 9 ، العدد = 1
الحجم
405060
بيثونفببيثونفببيثونفب
0.0040.0010.0050.0030.0070.004
0.0070.0090.0180.0090.0180.023
0.028.266470.05320.87220.1290145.9786

الفرز الغبي هو ترتيب من حيث الحجم أسرع من الفرز السخيف ، والفرز السخيف هو ترتيب من حيث الحجم أسرع من البطء. لا يوجد شيء آخر لمشاهدته.

الفلاحون الأوسط


للتحقق من المتوسط ​​، تم إنشاء حزم من 100 صفيف من 100 عنصر لكل منها (أرقام فريدة من 100 إلى 999) ، بالإضافة إلى حزم من عشرة صفيفات من 1000 عنصر لكل منها (أرقام فريدة من 1000 إلى 9999).

يتم إعداد المصفوفات العشوائية ، والمصفوفات التي تم فرزها تقريبًا ، والمصفوفات ذات الترتيب العكسي.

الوزن المتوسط: عشوائي


اكتب = عشوائي ، فريد = صحيح
الحجم (الحد الأدنى إلى الحد الأقصى) × العدد
100 (100 إلى 999) × 1001000 (1000 إلى 9999) × 10
بيثونفببيثونفب
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

حول نفس النتائج للجميع. تعمل تطبيقات Python بشكل أسرع قليلاً من PHP.

ميدلز: تقريبا مصنفة


اكتب = تقريبا ، فريد = صحيح
الحجم (الحد الأدنى إلى الحد الأقصى) × العدد
100 (100 إلى 999) × 1001000 (1000 إلى 9999) × 10
بيثونفببيثونفب
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

جميعها لها نفس النتائج ، زائد أو ناقص. من المثير للاهتمام: يؤدي ترتيب البيانات الجزئي عشرات المرات إلى تحسين أداء جميع الخوارزميات.

الوسط: عكسي


اكتب = عكسي ، فريد = صحيح
الحجم (الحد الأدنى إلى الحد الأقصى) × العدد
100 (100 إلى 999) × 1001000 (1000 إلى 9999) × 10
بيثونفببيثونفب
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

هناك استنتاج مثير للاهتمام - لا يؤثر الترتيب العكسي على السرعة كثيرًا ، والذي انخفض مرتين فقط مقارنةً بالرقم العشوائي.

الوسط: ثنائي


النوع = عشوائي ، فريد = خطأ ، الحد الأدنى = 0 ، الحد الأقصى = 1
الحجم × العد
100 × 1001000 × 10
بيثونفببيثونفب
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

من أكثر أو أقل إثارة للاهتمام: إذا قمت بدلاً من الأرقام من 100 إلى 9999 بفرز الأصفار والأصفار ، فلن تنمو مؤشرات السرعة كثيرًا.

الأبطال


المؤامرة الرئيسية هنا هي ما إذا كان الفرز بمشط وفرز ك يمكن أن يضغط على الصوم من القاعدة؟

الأبطال: عشوائي


لمعرفة ذلك ، لنقم بتشكيل حزم من مجموعة عشوائية: 10 قطع من 10 آلاف عنصر و 10 قطع من 100 ألف عنصر. أردت أن أعطي المليونيرات لخوارزميات الإدخال ، لكن الكمبيوتر المحمول لم يسحب. فإما أن الذاكرة العشوائية غير كافية ، فإن عمق العودية كبير جدًا.
النوع = عشوائي ، فريد = خطأ ، عدد = 10
الحجم (الحد الأدنى إلى الحد الأقصى)
10000 (من 10000 إلى 99999)100000 (من 100000 إلى 999999)
بيثونفببيثونفب
0.802050.6310410.45068.24647
0.477031.640096.6513826.5435
0.900052.1861315.808929.4867

تصنيف Python K أبطأ ، و PHP أسرع من الفرز السريع.
يبدو فرز المشط مقارنة بالسريع سريعًا ، على الرغم من أنه أقل شأناً منه في جميع الاختبارات (وعلى هذه وما يليها) لأي مجموعات بيانات.

ومع ذلك ، هناك أمشاط وميزة لا تقبل الجدل. ليس لديها عودة ، وبالتالي ، على عكس زملائها ، تعالج بنجاح صفائف ملايين العناصر.

حالات خاصة


على الرغم من أن الأبطال أسرع مئات المرات من متوسط ​​الفلاحين ، إلا أنه في بعض مجموعات البيانات المحددة ، تظهر الفرز ببساطة أكبر أنه لا يمكن خياطةها بسهولة.

حالات خاصة: تم فرزها تقريبًا


لنجرب المصفوفات التي تم فرزها تقريبًا ، فقط خذ حجمًا أكبر من تلك التي تم اختبارها للفلاحين العاديين.

عبوة مكونة من 10 صفائف لكل منها 10 آلاف وحزمة من 10 صفائف لكل منها 100 ألف عنصر.
نوع = تقريبا ، فريد = خطأ
الحجم (الحد الأدنى إلى الحد الأقصى) × العدد
10000 (من 10000 إلى 99999) × 10100000 (من 100000 إلى 999999) × 10
بيثونفببيثونفب
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

الفرز السريع غير موجود هنا على الإطلاق - لم يكن هناك ما يكفي من ذاكرة الوصول العشوائي. في الوقت نفسه ، تتم معالجة الصفائف العشوائية التي تحتوي على 10/100 ألف عنصر عن طريق فرز سريع مع ضجة. واجه K- الفرز مشاكل مماثلة ، سحب 10 آلاف فقط في PHP. تعد المصفوفات الكبيرة التي تم فرزها تقريبًا حالة متدهورة للفرز السريع وتعديلاته. بسبب هذا الترتيب للعناصر ، يقسم العودية الصفيف إلى أجزاء لكل زوج من العناصر المجاورة تقريبًا ، مما يؤدي إلى أقصى عدد ممكن من مكالمات العودية.

بالمناسبة ، الوضع مشابه للصفائف الكبيرة ذات الترتيب العكسي. تنشأ نفس المشكلة كما هو الحال مع تلك التي تم ترتيبها تقريبًا - يجب أن تطلق الخوارزمية نفسها لكل زوج من العناصر المجاورة.

الفرز حسب المشط ، على الرغم من أنه يعمل مرة ونصف أبطأ مرتين من السرعة أو k (بشكل عام) ، لكنه لا يفيض في الذاكرة المذكورة أعلاه. لا عودية - لا توجد مشكلة.

حسنًا ، نلاحظ أن جميع الفلاحين الأوسطين تجاوزوا الأبطال معًا في صفائف كبيرة تم فرزها تقريبًا. ثم عمل المبدأ: "تسير بهدوء - ستستمر".

حالات خاصة: مليون وردة قرمزية من المصفوفات الصغيرة


صفائف صغيرة - عنصر الفلاحين الأوسط. إذا كنت بحاجة إلى معالجة عدد كبير من مجموعات صغيرة من الأرقام ، فيمكنك الحصول على كسب الوقت من خلال أخذ فقاعة "بدائية" أو فرز جنوم. واستخدم الفرز السريع وغيرها مثل المهام الكبيرة.
اكتب = عشوائي ، فريد = صحيح
الحجم (الحد الأدنى إلى الحد الأقصى) × العدد
5 (10 إلى 99) × 1،000،00010 (10 إلى 99) × 1،000،000
بيثونفببيثونفب
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


مع الفرز الصرف ، كل شيء واضح. الآن للجزء المثير للاهتمام حقًا - الفرز حسب الإدخالات.

المراجع


Excel-application AlgoLab ، الذي يمكنك من خلاله خطوة بخطوة عرض التصور لهذه الأنواع (وليس فقط هذه).

يتم أيضًا وضع جميع الصفائف في شكل JSON على قرص Google .

Wiki - Silly / Stooge ، Slow ، Dwarf / Gnome ، Bubble / Bubble ، Shaker / Shaker ، Odd / Even ، Comb / Comb ، Fast / Quick

سلسلة مقالات


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


All Articles