خوارزمية فرز الجرافة (Java, Python، ج/C++ Code أمثلة)
⚡ ملخص ذكي
تقوم خوارزمية فرز الدلو بتوزيع عناصر الإدخال في عدة دلاء، وفرز كل دلو بشكل مستقل، ثم تجمعها لإنتاج مصفوفة مرتبة نهائية.
ما هو فرز الجرافة؟
فرز الدلو، أو فرز الصناديق، هو أسلوب فرز توزيعي قائم على المقارنة، يقبل مصفوفة غير مرتبة كمدخلات وينتج مصفوفة مرتبة كمخرجات. تقوم هذه التقنية بتوزيع العناصر على عدة دلاء، ثم تفرز كل دلو على حدة باستخدام خوارزمية فرز أخرى، مثل فرز الإدراج. بعد ذلك، تُدمج جميع الدلاء معًا لتكوين المصفوفة المرتبة النهائية.
تُستخدم خوارزمية فرز الدلو عادةً عندما تكون العناصر:
- قيم النقطة العائمة
- موزعة بشكل منتظم على نطاق معروف
يعتمد التعقيد الزمني لخوارزمية فرز الدلو على عدد الدلو المستخدمة ومدى انتظام توزيع المدخلات. بينما خوارزميات الفرز الأخرى مثل نوع القشرة، دمج الفرز، كومة الفرز، و تصنيف سريع لتحقيق أفضل تعقيد زمني من O(n*logn)، يمكن لخوارزمية فرز الدلو أن تصل إلى تعقيد زمني خطي O(n) في ظل ظروف مواتية.
تعتمد خوارزمية فرز الدلو على أسلوب التوزيع والتجميع. حيث تُوزّع العناصر في دلاء متناظرة، ثم تُفرز داخل كل دلو، وتُجمع لتشكيل مصفوفة مُرتبة في الخطوة الأخيرة. سيتم شرح أسلوب التوزيع والتجميع هذا في القسم التالي.
نهج التشتيت والتجميع
قد يكون حل المشكلات المعقدة واسعة النطاق صعبًا في بعض الأحيان. تعالج طريقة التجميع والتوزيع هذه المشكلات بتقسيم مجموعة البيانات بأكملها إلى مجموعات. تُعالج كل مجموعة على حدة، ثم تُجمع النتائج معًا للوصول إلى الإجابة النهائية.
إليك كيفية تطبيق خوارزمية فرز الدلو لطريقة التشتيت والتجميع:
كيف يعمل فرز الجرافة
المبدأ الأساسي لعمل فرز الدلو هو كما يلي:
- يتم إنشاء مجموعة من الحاويات الفارغة. وبناءً على السياسة المختارة، يمكن أن يختلف عدد الحاويات.
- من مصفوفة الإدخال، يتم وضع كل عنصر في الحاوية المقابلة له.
- يتم فرز كل مجموعة على حدة باستخدام خوارزمية فرز ثانوية.
- يتم دمج المجموعات المصنفة لإنتاج مصفوفة إخراج واحدة.
كنية Code
Start Create N empty buckets For each array element: Calculate bucket index Put that element into the corresponding bucket For each bucket: Sort elements within each bucket Merge all the elements from each bucket Output the sorted array End
الطريقة الأولى: خوارزمية فرز الجرافة للنقطة العائمة Numbers
خوارزمية فرز الدلو للأعداد العشرية ضمن النطاق [0.0، 1.0]:
الخطوة 1) أنشئ عشرة (10) دلاء فارغة. يحتوي الدلو الأول على أرقام ضمن النطاق [0.0، 0.1). ويحتوي الدلو الثاني على أرقام ضمن النطاق [0.1، 0.2)، وهكذا.
الخطوة 2) لكل عنصر من عناصر المصفوفة:
- أ. احسب فهرس المجموعة باستخدام الصيغة التالية:
فهرس_العلبة = عدد_العلب * عنصر_المصفوفة - ب. أدخل العنصر في bucket[bucket_index]
الخطوة 3) قم بفرز كل مجموعة على حدة باستخدام فرز الإدراج.
الخطوة 4) قم بدمج جميع المجموعات في مصفوفة واحدة مرتبة.
دعونا نستعرض مثالاً على خوارزمية فرز الدلو. في هذا المثال، سنقوم بفرز المصفوفة التالية:
الخطوة 1) أولاً، نقوم بإنشاء 10 دلاء فارغة. يحتوي الدلو الأول على أرقام في النطاق [0.0، 0.1). ويحتوي الدلو الثاني على أرقام في النطاق [0.1، 0.2)، وهكذا.
الخطوة 2) لكل عنصر من عناصر المصفوفة، احسب فهرس الحاوية وضع العنصر في تلك الحاوية.
يتم حساب مؤشر الدلو باستخدام الصيغة التالية:
فهرس_العلبة = عدد_العلب * عنصر_المصفوفة
حساب مؤشر الجرافة:
أ) 0.78
فهرس_العلبة = عدد_العلب * عنصر_المصفوفة
= 10 * 0.78
= 7.8
وبالتالي، يتم تخزين العنصر 0.78 في bucket[floor(7.8)] أو bucket[7].
ب) 0.17
فهرس_العلبة = عدد_العلب * عنصر_المصفوفة
= 10 * 0.17
= 1.7
يتم تخزين عنصر المصفوفة 0.17 في bucket[floor(1.7)] أو bucket[1].
ج) 0.39
فهرس_العلبة = عدد_العلب * عنصر_المصفوفة
= 10 * 0.39
= 3.9
يتم تخزين 0.39 في bucket[floor(3.9)] أو bucket[3].
بعد المرور على جميع عناصر المصفوفة، تبدو المجموعات كما يلي:
الخطوة 3) ثم يتم فرز كل مجموعة باستخدام خوارزمية فرز الإدراج. بعد عملية الفرز، تكون النتيجة كالتالي:
الخطوة 4) في الخطوة الأخيرة، يتم دمج المجموعات في مصفوفة واحدة. هذه المصفوفة هي النتيجة المرتبة للمدخلات.
يتم دمج كل مجموعة مع مصفوفة الإخراج. على سبيل المثال، دمج عناصر المجموعة الثانية:
يظهر أدناه تسلسل عناصر المجموعة الأخيرة:
بعد عملية الدمج، تكون المصفوفة الناتجة هي المصفوفة المرتبة المطلوبة.
برنامج فرز الجرافة في C/C++
الإدخال:
//Bucket Sort Program in C/C++ //For values without integer parts #include <bits/stdc++.h> #define BUCKET_SIZE 10 using namespace std; void bucketSort(float input[], int array_size) { vector <float>bucket[BUCKET_SIZE]; for (int i = 0; i < array_size; i++) { int index = BUCKET_SIZE*input[i]; bucket[index].push_back(input[i]); } for (int i = 0; i < BUCKET_SIZE; i++) sort(bucket[i].begin(), bucket[i].end()); int out_index = 0; for (int i = 0; i < BUCKET_SIZE; i++) for (int j = 0; j < bucket[i].size(); j++) input[out_index++] = bucket[i][j]; } int main() { float input[]={0.78,0.17,0.39,0.26,0.72,0.94,0.21,0.12,0.23,0.69}; int array_size = sizeof(input)/sizeof(input[0]); bucketSort(input, array_size); cout <<"Sorted Output: "; for (int i = 0; i< array_size; i++) cout<<input[i]<<" "; return 0; }
الإخراج:
Sorted Output: 0.12 0.17 0.21 0.23 0.26 0.39 0.69 0.72 0.78 0.94
برنامج فرز الجرافة في Python
الإدخال:
# Bucket Sort Program in Python # For values without integer parts def bucketSort(input): output = [] bucket_size = 10 for bucket in range(bucket_size): output.append([]) for element in input: index = int(bucket_size * element) output[index].append(element) for bucket in range(bucket_size): output[bucket] = sorted(output[bucket]) out_index = 0 for bucket in range(bucket_size): for element in range(len(output[bucket])): input[out_index] = output[bucket][element] out_index += 1 return input input = [0.78, 0.17, 0.39, 0.26, 0.72, 0.94, 0.21, 0.12, 0.23, 0.69] print("Sorted Output:") print(bucketSort(input))
الإخراج:
Sorted Output: [0.12, 0.17, 0.21, 0.23, 0.26, 0.39, 0.69, 0.72, 0.78, 0.94]
دلو الترتيب في Java
الإدخال:
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class BucketSort { private static final int BUCKET_SIZE = 10; public static void bucketSort(float[] input, int arraySize) { List<Float>[] bucket = new ArrayList[BUCKET_SIZE]; for (int i = 0; i < arraySize; i++) { int index = (int)(BUCKET_SIZE * input[i]); if (bucket[index] == null) { bucket[index] = new ArrayList<>(); } bucket[index].add(input[i]); } for (int i = 0; i < BUCKET_SIZE; i++) { if (bucket[i] != null) { Collections.sort(bucket[i]); } } int outIndex = 0; for (int i = 0; i < BUCKET_SIZE; i++) { if (bucket[i] != null) { for (float value: bucket[i]) { input[outIndex++] = value; } } } } public static void main(String[] args) { float[] input = {0.78f,0.17f,0.39f,0.26f,0.72f,0.94f,0.21f,0.12f,0.23f,0.69f}; int arraySize = input.length; bucketSort(input, arraySize); System.out.println("Sorted Output:"); for (int i = 0; i < arraySize; i++) { System.out.print(input[i]+" "); } } }
الإخراج:
Sorted Output: 0.12 0.17 0.21 0.23 0.26 0.39 0.69 0.72 0.78 0.94
الطريقة الثانية: خوارزمية فرز الجرافة للعناصر الصحيحة
تختلف خوارزمية فرز الدلو للمدخلات التي تحتوي على أرقام خارج النطاق [0.0، 1.0] اختلافًا طفيفًا عن الخوارزمية السابقة. خوارزميةالخطوات المطلوبة في هذه الحالة هي كما يلي:
الخطوة 1) أوجد أكبر وأصغر عنصرين في المصفوفة.
الخطوة 2) حدد عدد الحاويات، n، وقم بتهيئتها على أنها فارغة.
الخطوة 3) احسب نطاق أو مدى كل مجموعة باستخدام الصيغة:
span = (maximum - minimum) / n
الخطوة 4) لكل عنصر من عناصر المصفوفة:
- 1. حساب مؤشر الدلو:
bucket_index = (element - minimum) / span - 2. أدخل العنصر في bucket[bucket_index]
الخطوة 5) قم بفرز كل مجموعة باستخدام فرز الإدراج.
الخطوة 6) قم بتسلسل كافة الدلاء في صفيف واحد.
دعونا نستعرض مثالاً على خوارزمية فرز الدلو. في هذا المثال، سنقوم بفرز المصفوفة التالية:
الخطوة 1) في الخطوة الأولى، نجد أكبر وأصغر عنصرين في المصفوفة المعطاة. في هذا المثال، أكبر عنصر هو 24 وأصغر عنصر هو 1.
الخطوة 2) بعد ذلك، نختار عدد الدلاء الفارغة، n. في هذا المثال، نستخدم 5 دلاء ونهيئها على أنها فارغة.
الخطوة 3) يتم حساب مدى كل دلو باستخدام الصيغة التالية:
span = (maximum - minimum) / n = (24 - 1) / 5 = 4
وبالتالي، تحتوي الخانة الأولى على الأرقام ضمن النطاق [0، 5). وتحتوي الخانة الثانية على الأرقام ضمن النطاق [5، 10)، وهكذا.
الخطوة 4) لكل عنصر من عناصر المصفوفة، احسب فهرس الحاوية وضع العنصر في تلك الحاوية. يُحسب فهرس الحاوية باستخدام الصيغة التالية:
bucket_index = (element - minimum) / span
حساب مؤشر الجرافة:
أ) 11
bucket_index = (العنصر – الحد الأدنى) / النطاق
= (11،1،4 - XNUMX،XNUMX) / XNUMX
= 2
وبالتالي، يتم تخزين العنصر 11 في الحاوية [2].
ب) 9
bucket_index = (العنصر – الحد الأدنى) / النطاق
= (9،1،4 - XNUMX،XNUMX) / XNUMX
= 2
ملاحظة: بما أن 9 هو عنصر حدودي لـ bucket[1]، فإنه يُلحق بـ bucket[1] بدلاً من وضعه في نفس الحاوية مثل العنصر السابق.
بعد إجراء العمليات على كل عنصر، تبدو المجموعات كما يلي:
الخطوة 5) الآن، يتم فرز كل مجموعة باستخدام خوارزمية فرز الإدراج. المجموعات بعد الفرز:
الخطوة 6) في الخطوة الأخيرة، يتم دمج المجموعات في مصفوفة واحدة. مجموعة هي النتيجة المصنفة للمدخلات.
برنامج فرز الجرافة في C/C++
الإدخال:
#include<bits/stdc++.h> using namespace std; void bucketSort(vector < double > & input, int No_Of_Buckets) { double max_value = * max_element(input.begin(), input.end()); double min_value = * min_element(input.begin(), input.end()); double span = (max_value - min_value) / No_Of_Buckets; vector<vector <double>> output; for (int i = 0; i < No_Of_Buckets; i++) output.push_back(vector <double>()); for (int i = 0; i < input.size(); i++) { double difference = (input[i] - min_value) / span - int((input[i] - min_value) / span); if (difference == 0 && input[i] != min_value) output[int((input[i] - min_value) / span) - 1].push_back(input[i]); else output[int((input[i] - min_value) / span)].push_back(input[i]); } for (int i = 0; i < output.size(); i++) { if (!output[i].empty()) sort(output[i].begin(), output[i].end()); } int index = 0; for (vector <double> & bucket: output) { if (!bucket.empty()) { for (double i: bucket) { input[index] = i; index++; } } } } int main() { vector <double> input ={11,9,21,8,17,19,13,1,24,12}; int No_Of_Buckets = 5; bucketSort(input, No_Of_Buckets); cout<<"Sorted Output:"; for (int i=0; i < input.size(); i++) cout <<input[i]<<" "; return 0; }
الإخراج:
Sorted Output:1 8 9 11 12 13 17 19 21 24
برنامج فرز الجرافة في Python
الإدخال:
def bucketSort(input, No_Of_Buckets): max_element = max(input) min_element = min(input) span = (max_element - min_element) / No_Of_Buckets output = [] for bucket in range(No_Of_Buckets): output.append([]) for element in range(len(input)): diff = (input[element] - min_element) / span - int( (input[element] - min_element) / span ) if diff == 0 and input[element] != min_element: output[int((input[element] - min_element) / span) - 1].append( input[element] ) else: output[int((input[element] - min_element) / span)].append(input[element]) for bucket in range(len(output)): if len(output[bucket]) != 0: output[bucket].sort() index = 0 for bucket in output: if bucket: for element in bucket: input[index] = element index = index + 1 input = [11, 9, 21, 8, 17, 19, 13, 1, 24, 12] No_Of_Buckets = 5 bucketSort(input, No_Of_Buckets) print("Sorted Output: ", input)
الإخراج:
Sorted Output: [1, 8, 9, 11, 12, 13, 17, 19, 21, 24]
دلو الترتيب في Java
الإدخال:
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class BucketSort { public static void bucketSort(List < Double > input, int No_Of_Buckets) { double max_value = Collections.max(input); double min_value = Collections.min(input); double span =(max_value - min_value) / No_Of_Buckets; List<List<Double>> output = new ArrayList<>(); for (int i = 0; i < No_Of_Buckets; i++) { output.add(new ArrayList<>()); } for (Double value: input) { double difference = (value - min_value) / span - ((value - min_value) / span); if (difference == 0 && value != min_value) { output.get((int)((value - min_value) / span) - 1).add(value); } else { output.get((int)((value - min_value) / span)).add(value); } } for (List <Double> bucket: output) { if (!bucket.isEmpty()) { Collections.sort(bucket); } } int index = 0; for (List <Double> bucket: output) { if (!bucket.isEmpty()) { for (Double value: bucket) { input.set(index,value); index++; } } } } public static void main(String[] args) { List <Double> input = new ArrayList<>(); input.add(11.0); input.add(9.0); input.add(21.0); input.add(8.0); input.add(17.0); input.add(19.0); input.add(13.0); input.add(1.0); input.add(24.0); input.add(12.0); int No_Of_Buckets = 5; bucketSort(input, No_Of_Buckets); System.out.println("Sorted Output:"); for (Double value: input) { System.out.print(value + " "); } } }
الإخراج:
Sorted Output: 1.0 8.0 9.0 11.0 12.0 13.0 17.0 19.0 21.0 24.0
مزايا وعيوب فرز الدلو
| الايجابيات | سلبيات |
|---|---|
| يُجري عمليات حسابية أسرع على البيانات الموزعة بشكل منتظم | يستهلك مساحة أكبر مقارنة بخوارزميات الفرز الموضعي |
| يمكن استخدامها كطريقة فرز خارجية لمجموعات البيانات الكبيرة | يكون الأداء سيئًا عندما لا يتم توزيع البيانات بشكل موحد |
| يمكن معالجة الدلاء بشكل مستقل ومتوازٍ | يتطلب معرفة مسبقة بنطاق البيانات وتوزيعها |
تحليل تعقيد فرز الدلو
تعقيد وقت فرز دلو
- أفضل تعقيد للحالة: إذا كانت جميع عناصر المصفوفة موزعة بشكل منتظم ومرتبة مسبقًا داخل كل خانة، فإن الأمر يتطلب وقتًا قدره O(n) لتوزيع العناصر في الخانات المناسبة. ثم يتم فرز كل خانة باستخدام ترتيب بالإدراج تكلفتها O(k). وبالتالي فإن التعقيد الإجمالي هو O(n+k).
- متوسط تعقيد الحالة: في الحالات المتوسطة، نفترض أن المدخلات موزعة توزيعًا منتظمًا. وبذلك، تحقق خوارزمية فرز الدلو تعقيدًا زمنيًا خطيًا قدره O(n+k). هنا، يتطلب الأمر O(n) من الوقت لتوزيع العناصر، وO(k) من الوقت لفرزها باستخدام فرز الإدراج.
- أسوأ حالة تعقيد: في أسوأ الأحوال، لا تتوزع العناصر بشكل متساوٍ وتتجمع في دلو واحد أو دلوين. في هذه الحالة، يتدهور أداء خوارزمية فرز الدلو ليصبح مشابهًا لـ خوارزمية فرز الفقاعاتوبالتالي، في أسوأ الحالات، يكون التعقيد الزمني لفرز الدلو هو O(n²).
تعقيد المساحة لفرز الدلو
يبلغ التعقيد المكاني لخوارزمية فرز الدلو O(n*k). حيث n هو عدد العناصر و k هو عدد الدلو المطلوب لحفظها أثناء الفرز.



















