سموث سورت

في علم الحاسوب ، تُعدّ خوارزمية الترتيب السلس (smoothsort) خوارزمية ترتيب تعتمد على المقارنة . وهي نوع مُعدّل من خوارزمية الترتيب الهرمي ( heapsort ) ، وقد ابتكرها ونشرها إدسكار ديكسترا عام 1981. [ 1 ] ومثل خوارزمية الترتيب الهرمي، تُعتبر خوارزمية الترتيب السلس خوارزمية ترتيب موضعي بحد أقصى O ( n log n ) عملية (انظر ترميز Big O ). [ 2 ] ومثل خوارزمية الترتيب الهرمي، لا تُعتبر خوارزمية الترتيب السلس خوارزمية ترتيب مستقرة . وتكمن ميزة خوارزمية الترتيب السلس في أنها تقترب من زمن O ( n ) إذا كانت المدخلات مُرتبة بالفعل إلى حد ما ، بينما يبلغ متوسط ​​زمن خوارزمية الترتيب الهرمي O ( n log n ) بغض النظر عن حالة الترتيب الأولية.  

ملخص

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

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

تستخدم خوارزمية Smoothsort طريقةً مختلفةً لترتيب البيانات، وهي اجتيازٌ تصاعديٌّ عميقٌ بترتيبٍ لاحق . يتبع الابن الأيسر الشجرة الفرعية التي جذرها شقيقه، ويتبع الابن الأيمن والده. لكل عنصر ارتفاعٌ محددٌ فوق الأوراق، ولكل عنصرٍ غير ورقيٍّ أبناؤه في مواقعَ سابقةٍ في المصفوفة. مع ذلك، يعتمد عمقه أسفل الجذر على حجم المصفوفة. صُممت الخوارزمية بحيث يكون الجذر في نهاية الكومة، وعند استخراج أي عنصرٍ منها يكون قد استقرّ في موقعه النهائي ولا يحتاج إلى نقله. كذلك، تُعدّ المصفوفة المرتبة كومةً صالحةً، والعديد من الفترات المرتبة هي أشجارٌ فرعيةٌ مرتبةٌ وفقًا لترتيب الكومة.

بصورة أدق، كل موضع i هو جذر لشجرة فرعية فريدة، تشغل عقدها فاصلاً متصلاً ينتهي عند i . قد تكون البادئة الأولية للمصفوفة (بما في ذلك المصفوفة بأكملها) فاصلاً كهذا يُقابل شجرة فرعية، ولكنها عمومًا تتحلل إلى اتحاد عدد من فواصل الأشجار الفرعية المتتالية هذه، والتي يُطلق عليها ديكسترا اسم "الامتدادات". أي شجرة فرعية بدون أصل (أي جذرها عند موضع يقع أصله خارج البادئة قيد الدراسة) تُعطي امتدادًا في تحلل ذلك الفاصل، وبالتالي يكون هذا التحلل فريدًا. عند إضافة عقدة جديدة إلى البادئة، يحدث أحد أمرين: إما أن يكون الموضع ورقة ويُضيف امتدادًا بطول 1 إلى التحلل، أو أنه يندمج مع آخر امتدادين، ليصبح أصل جذورهما، وبالتالي يستبدل الامتدادين بامتداد جديد يحتوي على اتحادهما بالإضافة إلى الموضع الجديد (الجذر).

أشار ديكسترا [ 1 ] إلى أن القاعدة البديهية هي دمج الأجزاء إذا وفقط إذا كانت متساوية في الحجم، وفي هذه الحالة ستكون جميع الأشجار الفرعية أشجارًا ثنائية مثالية بحجم 2k - 1 . ومع ذلك، فقد اختار قاعدة مختلفة، تُتيح أحجامًا أكثر احتمالًا للأشجار. تتمتع هذه القاعدة بنفس الكفاءة التقاربية [ 2 ولكنها تُحسّن الكفاءة بمعامل ثابت صغير من خلال تقليل عدد الأجزاء المطلوبة لتغطية كل فترة.

تعتمد قاعدة ديكسترا على دمج آخر جزأين إذا وفقط إذا كانت أحجامهما أعداد ليوناردو المتتالية L ( i +1 ) و L ( i ) (بهذا الترتيب)، والتي يتم تعريفها بشكل متكرر، بطريقة مشابهة جدًا لأعداد فيبوناتشي ، على النحو التالي:

  • L (0) = L (1) = 1
  • L ( k +2) = L ( k +1) + L ( k ) + 1

نتيجةً لذلك، يكون حجم أي شجرة فرعية عددًا من أعداد ليوناردو. يمكن إيجاد تسلسل أحجام الامتدادات التي تُقسّم المواضع n الأولى ، لأي قيمة لـ n ، بطريقة جشعة: الحجم الأول هو أكبر عدد من أعداد ليوناردو لا يتجاوز n ، ويتم تقسيم الباقي (إن وُجد) بشكل متكرر. تتناقص أحجام الامتدادات تناقصًا تامًا، باستثناء حجمين نهائيين ربما يساويان 1، وتتجنب أعداد ليوناردو المتتالية باستثناء الحجمين الأخيرين ربما.

إضافةً إلى كون كل امتداد شجرة مرتبة ترتيبًا هرميًا، تُحفظ جذور الأشجار مرتبةً ترتيبًا تصاعديًا. وهذا يُضيف فعليًا فرعًا ثالثًا (يُطلق عليه ديكسترا اسم "الابن بالتبني") إلى كل جذر، رابطًا إياه بالجذر السابق. وبذلك، تُدمج جميع الأشجار معًا في كومة عالمية واحدة، مع وجود القيمة القصوى العالمية في النهاية.

على الرغم من أن موقع الابن المتبنى لكل عقدة ثابت، إلا أن الرابط موجود فقط لجذور الشجرة، مما يعني أن الروابط تُزال عند دمج الأشجار. وهذا يختلف عن الأبناء العاديين، الذين يرتبطون طالما أن الأصل موجود.

في المرحلة الأولى (مرحلة نمو الكومة) من عملية الفرز، يُعاد تنظيم جزء أولي متزايد الحجم من المصفوفة بحيث تكون الشجرة الفرعية لكل امتداد منها كومة قصوى: أي أن العنصر في أي موضع غير طرفي يكون على الأقل بنفس حجم العناصر في المواضع التي تمثل أبناءه. بالإضافة إلى ذلك، فإن جميع الجذور تكون على الأقل بنفس حجم أبنائها.

في المرحلة الثانية (مرحلة تقليص الكومة)، يتم فصل العقدة القصوى عن نهاية المصفوفة (دون الحاجة إلى تحريكها) ويتم إعادة تأسيس ثوابت الكومة بين أبنائها. (وتحديدًا، بين الأبناء المتبنين الذين تم إنشاؤهم حديثًا).

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

العمليات

على الرغم من أن المرحلتين من عملية الفرز متعاكستان فيما يتعلق بتطور بنية تسلسل الأكوام، إلا أنهما يتم تنفيذهما باستخدام عنصر أساسي واحد، وهو ما يعادل عملية "الفرز لأسفل" في كومة ثنائية قصوى.

التصفية لأسفل

تُعيد عملية الفرز الأساسية (التي يُطلق عليها ديكسترا اسم " التنقيط ") ثبات الكومة عندما يكون من المحتمل انتهاكه فقط عند العقدة الجذرية. إذا كانت العقدة الجذرية أصغر من أي من أبنائها، يتم تبديلها مع أكبر أبنائها، وتُكرر العملية مع العقدة الجذرية في شجرتها الفرعية الجديدة.

يكمن الفرق بين خوارزمية الترتيب السلس وكومة الحد الأقصى الثنائية في أن جذر كل امتداد يجب ترتيبه بالنسبة إلى جذر الامتداد السابق، وهو جذر "الخطوة الثانية". لذا، تبدأ عملية الفرز التنازلي بسلسلة من المقارنات الرباعية (العقدة الجذرية وثلاثة أبناء) حتى لا يكون الابن هو العنصر الأقصى، ثم سلسلة من المقارنات الثلاثية (الجذر بالإضافة إلى اثنين من الأبناء) حتى تجد العقدة الجذرية موقعها النهائي وتُعاد الثوابت.

كل شجرة هي شجرة ثنائية كاملة : لكل عقدة ابنان أو لا شيء. لا حاجة للتعامل مع الحالة الخاصة لابن واحد والتي تحدث في كومة ثنائية ضمنية قياسية . (لكن الحالة الخاصة لروابط الابن المتبنى تعوض هذا التوفير بشكل كبير).

نظرًا لوجود O (log n )  امتدادات، كل منها عبارة عن شجرة بعمق O (log n )  ، فإن الوقت اللازم لإجراء كل عملية تصفية تنازلية محدود بـ O (log n )  .

توسيع منطقة الكومة عن طريق دمج عنصر إلى اليمين

عند إضافة عنصر جديد إلى سلسلة الامتدادات (قائمة هياكل الكومة المنفصلة)، فإنه إما يُشكّل امتدادًا جديدًا بعنصر واحد، أو يدمج الامتدادين الأيمنين ليصبح العنصر الأب لجذريهما، مُشكّلًا امتدادًا جديدًا يحل محلهما في السلسلة. ويعتمد حدوث أيٍّ من هذين الاحتمالين على أحجام الامتدادات الموجودة حاليًا (وبالتالي على فهرس العنصر المُضاف فقط). وقد نصّت خوارزمية ديكسترا على أن الامتدادات تُدمج إذا وفقط إذا كانت أحجامها L ( k +1) و L ( k ) لبعض قيم k ، أي أعداد ليوناردو المتتالية؛ وسيكون حجم الامتداد الجديد L ( k +2) .

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

تحسين

تُوفّر خوارزمية ديكسترا الجهد من خلال ملاحظة أن شرط ثبات الكومة الكاملة مطلوب في نهاية مرحلة النمو، ولكنه ليس مطلوبًا في كل خطوة وسيطة. وعلى وجه الخصوص، فإن شرط أن يكون العنصر أكبر من ابنه لا يُصبح مهمًا إلا للعناصر التي تُمثّل جذور الشجرة النهائية.

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

تقليص منطقة الكومة عن طريق فصل العنصر الأيمن عنها

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

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

تحسين

من المعروف مسبقًا أن الجذور المكشوفة حديثًا مرتبة بشكل صحيح بالنسبة لأبنائها الطبيعيين؛ أما الترتيب بالنسبة لأبنائها غير الطبيعيين فهو محل التساؤل. لذا، عند تقليص حجم الكومة، يمكن تبسيط الخطوة الأولى من عملية الفرز إلى مقارنة واحدة مع الابن غير الطبيعي. في حال حدوث تبديل، يجب أن تُجري الخطوات اللاحقة مقارنة رباعية كاملة.

تحليل

تستغرق خوارزمية Smoothsort زمنًا قدره O ( n ) لمعالجة مصفوفة مُرتبة مسبقًا، و O ( n log n )  في أسوأ الحالات، وتحقق أداءً خطيًا تقريبًا على العديد من المدخلات شبه المرتبة. مع ذلك، فهي لا تتعامل مع جميع التسلسلات شبه المرتبة على النحو الأمثل. باستخدام عدد الانعكاسات كمقياس لعدم الترتيب (عدد أزواج المؤشرات i و j حيث i < j و A [ i ] > A [ j ] ؛ بالنسبة للمدخلات المرتبة عشوائيًا، يكون هذا العدد تقريبًا /4 ) ، توجد تسلسلات إدخال محتملة تحتوي على O ( n log n )  انعكاسًا، مما يجعلها تستغرق زمنًا قدره Ω( n log n )  ، بينما تستطيع خوارزميات الفرز التكيفية الأخرى حل هذه الحالات في زمن قدره O ( n log  log n )  . [ 2 ]

يحتاج خوارزمية الفرز السلس إلى تخزين أحجام جميع الأشجار في كومة ليوناردو في الذاكرة. ولأنها مُرتبة حسب الرتبة، وكل رتبة مميزة، يُستخدم عادةً متجه بتات يُشير إلى الرتب الموجودة. علاوة على ذلك، ولأن أكبر رتبة لا تتجاوز O (log n )  ، يمكن ترميز هذه البتات في O (1) كلمة من كلمات الآلة، بافتراض نموذج آلة ثنائي التفرع .

لاحظ أن O (1) كلمة آلة لا تعني كلمة آلة واحدة . يكفي متجه 32 بت للأحجام الأقل من L (32) = 7049155. بينما يكفي متجه 64 بت للأحجام الأقل من L (64) = 34335360355129 ≈ 2⁴⁵ . عمومًا، يتطلب الأمر 1/log² ( φ ) ≈ 1.44 بت من المتجه لكل بت من الحجم.

صنف الحور

هناك خوارزمية أبسط مستوحاة من خوارزمية smoothsort وهي خوارزمية poplar sort . [ 3 ] سميت هذه الخوارزمية نسبة إلى صفوف الأشجار ذات الحجم المتناقص التي غالباً ما تُرى في الأراضي الهولندية المستصلحة ، وهي تُجري مقارنات أقل من خوارزمية smoothsort للمدخلات غير المرتبة في الغالب، ولكنها لا تستطيع تحقيق وقت خطي للمدخلات المرتبة.

يتمثل التغيير الجوهري الذي أحدثته خوارزمية فرز الحور في أن جذور الأشجار المختلفة لا تُحفظ بترتيب مُرتب؛ فلا توجد روابط "متدرجة" تربطها معًا في كومة واحدة. بدلًا من ذلك، في كل مرة تُقلص فيها الكومة في المرحلة الثانية، يتم البحث في الجذور للعثور على المدخل الأقصى.

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

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

تم اقتراح نفس الهيكل كطابور أولوية للأغراض العامة تحت اسم كومة الترتيب اللاحق ، [ 4 ] تحقيق وقت إدخال مستهلك O (1) في هيكل أبسط من كومة ذات الحدين الضمنية .

التطبيقات

تستخدم مكتبة musl C خوارزمية smoothsort لتنفيذها لـ qsort(). [ 5 ] [ 6 ]

مراجع

  1. 1 2 ديكسترا، إدسكار دبليو. 16 أغسطس 1981 (EWD-796a) (PDF) . أرشيف إدسكار دبليو ديكسترا. مركز التاريخ الأمريكي، جامعة تكساس في أوستن . يمكن أيضًا طرح سؤال حول سبب عدم اختياري لأطوال الامتداد المتاحة: ... 63 31 15 7 3 1، والتي تبدو جذابة لأن كل امتداد يمكن اعتباره اجتيازًا لاحقًا لشجرة ثنائية متوازنة. بالإضافة إلى ذلك، ستكون علاقة التكرار أبسط. لكنني أعرف سبب اختياري لأعداد ليوناردو:( نص مكتوب )
  2. هيرتل ، ستيفان ( 13 مايو 1983). "سلوك خوارزمية Smoothsort على التسلسلات المرتبة مسبقًا" (ملف PDF) . رسائل معالجة المعلومات . 16 (4): 165-170 . doi : 10.1016/0020-0190(83)90116-3 . مؤرشف (ملف PDF) من الأصل بتاريخ 8 ديسمبر 2015 .
  3. ^ برون، كوينراد ؛ هيسيلينك، ويم هـ. (13 سبتمبر 1991). “إعادة النظر في الفرز السلس”. خطابات معالجة المعلومات . 39 (5): 269-276 . دوى : 10.1016 / 0020-0190 (91)90027-F .
  4. هارفي، نيكولاس جيه إيه؛ زاتلوكال، كيفن (26-28 مايو 2004). كومة ما بعد الترتيب . المؤتمر الدولي الثالث حول متعة الخوارزميات (FUN 2004). إلبا ، إيطاليا.
  5. فيلكر، ريتش (30 أبريل 2013). "كيف تُطبّق اللغات المختلفة عملية الفرز في مكتباتها القياسية؟" . ستاك أوفرفلو . تم الاطلاع عليه بتاريخ 28 أكتوبر 2020 .
  6. أوكس، لين؛ فيلكر، ريتش (11 أغسطس 2017). "src/stdlib/qsort.c" . musl - تطبيق للمكتبة القياسية لأنظمة لينكس . تم الاسترجاع في 26 يناير 2021 .