الفصل الثاني
Getting
Started
مقدمة
في
الفصل الأول اتعرضنا لشوية مفاهيم أساسية
عن الـ algorithms
وأهمية
العلم ده وتطبيقاته ,
في
الفصل الثاني بنبدأ نتعرض عمليا لأول
algorithms
لحل
مشكلة ال sorting
اللي
تم توضيحها في الفصل الأول ,
هندرس
بالتفصيل 2
algorithms لحل
المشكلة دي وهما insertion
sort و
merge
sort.
Insertion
Sort
كلمة
sort يعني
ترتيب وكلمة insertion
جاية
من الفعل insert
بمعنى
يدخل أو يدرج(كأنك
في ال algorithm
هتمسك
حاجة وتدرجها في مكان معين وهتشوف ده
بيحصل إزاي)
, ال
insertion
sort بيشتغل
بطريقة بسيطة ومفهومة جدا ,
هنفرض
اننا بنرتب تصاعدي من الصغير للكبير ,
ببساطة
معاك شوية أرقام عددهم n
وعايز
ترتبهم ,
هنرتبهم
واحدة واحدة ,
بس
عايزين لما نرتب شوية نفصلهم عن الباقيين
, يعني
دايما معانا مجموعة أرقام مرتبة ومجموعة
أرقام غير مرتبة ,
تبدأ
ناخد رقم رقم ,
طبعا
لما تكون مجموعة العناصر مكونة من رقم
واحد فهي مترتبة جاهزة ,فلما
ناخد أول رقم مش هنحتاج نرتب ,
كدة
مجموعة الأرقام المرتبة مكونة من الرقم
ده بس والمجموعة الغير مرتبة مكونة من
باقي الأرقام ,
نبدأ
ناخد رقم من المجموعة الغير مرتبة ونحطه
في مكانه المظبوط في المجموعة المرتبة
(وده
ال insertion
انك
بتدرج رقم جديد من المجموعة الغير مرتبة
في المجموعة المرتبة)
في
كل مرة المجموعة الغير مرتبة بتقل عنصر
والمجموعة المرتبة بتزيد عنصر لغاية في
النهاية تنتهي المجموعة الغير مرتبة
وبكدة يكون ال algorithm
رتب
كل الأرقام بنجاح ,
لغاية
دلوقتي بيتهيقلي كل ال algorithm
واضح
إلا خطوة انك تحط الرقم في مكانه المظبوط
(Insertion)
هي
اللي ممكن تكون بتفكر هتتعمل ازاي برمجيا
, ببساطة
انت هتحط الرقم ده وسط مجموعة أرقام مرتبة
أصلا (اللي
هي ال insertion
من
المجموعة الغير مرتبة للمجموعة المرتبة)
وبالتالي
انت بتعدي على عناصر المجموعة المرتبة
دي لغاية متلاقي أول عنصر أصغر منه تروح
حاطه قبليه ,
وبكدة
هو كل اللي قبليه أصغر منه وكل اللي بعديه
أكبر منه(يعني
في مكانه المظبوط تمام)
تعالى
نشوف مثال علشان الأمور توضح أكتر
في
الصورة اللي قدامك خطوات ترتيب مجموعة
الأرقام (5,
2, 4, 6, 1, 3) , طبعا
قلنا أن أول عنصر هو كدة كدة مترتب ,
وبالتالي
أول خطوة(a)
بتبدأ
بإدراج (insertion)
تاني
عنصر في مكانه المظبوط في المجموعة
المترتبة بأنها تجيبه قبل العنصر 5
لأن
2 أصغر
من 5
تاني
خطوة(b)
بتعمل
insertion
لتالت
عنصر اللي هو 4
بأنها
تجيبه بعد ال 2
وقبل
ال 5
تالت
خطوة(c)
بتعمل
insertion
لرابع
عنصر اللي هو 6
بأنها
تسيبه مكانه لأنه أكبر من كل العناصر اللي
في المجموعة المرتبة
رابع
خطوة(d)
بتعمل
insertion
لخامس
عنصر اللي هو 1
بأنها
تجيبه أول عنصر لأنه أصغر من كل العناصر
اللي في المجموعة المرتبة
خامس
وآخر خطوة بتعمل insertion
لسادس
وآخر عنصر اللي هو 3
بأنها
تجيبه بعد ال 2
وقبل
ال 4
هو
في ال algorithm
بيفرض
إن مجموعة العناصر موجودين في array
data structure
وكلنا
عارفين ان ال array
مفهاش
امكانية انك تحط عنصر بين عنصرين فعلشان
تحط عنصر بين عنصرين لازم تعمل shift(إزاحة)
لكل
العناصر اللي انت عايز تحط العنصر الجديد
قبليها وبالتالي توفر مكان للعنصر الجديد
, علشان
كدة في أسهم كتير بتشير لعملية الإزاحة
دي في الصورة.
تعالى
بقى نشوف ال pseudocode
بتاع
أول algorithm
معانا
, قبل
منشوف ال pseudocode
أنا
بنصح أي حد إنه يجرب يكتب ال pseudocode
بنفسه
قبل ميشوفه بعد مفهم ال algorithm
وبعد
ميتأكد ان ال pseudocode
بتاعه
صح يحاول ي implement(ينفذ)
ال
algorithm
بإيده
برده بأي لغة برمجة قبل ميشوف ال
implementation
بتاعه
, الحوار
ده هيبني عندك ال ability(القدرة)
إنك
تحل مشكلة(problem
solving) بالبرمجة
, لإن
دايما بيكون الموضوع واضح إنه سهل بس لما
تيجي تنفذه بإيدك بتظهر مشاكل ,
لما
تحل المشاكل دي انت بتبني skills(مهارات)
كتير
.
ال
pseudocode
بتاعنا
ببساطة قبل منشوفه هو إنك هتعدي على
العناصر من العنصر التاني(لإن
الأول مترتب)
لغاية
آخر عنصر في كل مرة بتاخد العنصر وتحطه
في مكانه المظبوط وخلص الحوار ,
ازاي
بقى الكلام ده يتكتب بالانجليزي ويكون
translatable
(قابل
للترجمة)
ﻷي
لغة برمجة هو ده اللي هتشوفه في الصورة
اللي جاية.
في
الـ line(السطر)
الأول
عملية ال loop
من
العنصر التاني لغاية آخر عنصر
في
line 2
بيخزن
العنصر اللي هيعمله insertion
في
variable(متغير)
وسماه
key
في
line 3
كتب
comment(تعليق)
انه
هيعمل insertion
للعنصر
ده في مكانه المظبوط في المجموعة المرتبة
في
line 4
عرف
variable
وسماه
i واداله
قيمة j
- 1 وهو
ال index(مكان)
العنصر
اللي قبل العنصر اللي هنعمله insertion
في
line 5
دخل
while
loop وحط
ال condition(الشرط)
بتاعها
ان ال i
> 0 يعني
يمر على عناصر المجموعة المرتبة كلها و
a[i] >
key يعني
العنصر الحالي من عناصر المجموعة المرتبة
يكون أكبر من العنصر اللي عايز يعمله
insertion
يعني
لسة موصلش لعنصر أصغر من العنصر اللي عايز
يعمله insertion
علشان
يحطه قبليه
في
line 6
بيعمل
shift
للعنصر
الحالي من عناصر المجموعة المرتبة علشان
يوفر مكان لل key
العنصر
اللي عايز يعمله insertion
في
line 7
بيقلل
المتغير i
بواحد
علشان ينقله يشاور على مكان العنصر اللي
جاي من عناصر المجموعة المرتبة
سطر
6 و
7
بيتنفذوا
تحت شرط الـ while
في
line 8 ود
خارج ال while
يعني
هيتنفذ بعد مال while
تنتهي
بيحط ال key
في
المكان اللي ال index
بتاعه
i + 1 وده
لإن ال while
loop ب
terminate
(تنتهي)
وال
i بيشاور
على أول عنصر أصغر من العنصر اللي عايز
يعمله insertion
وبالتالي
هو بيحطه قبليه وده بعد متكون ال while
عملت
shift لكل
العناصر اللي أكبر منه علشان توفرله مكان
ملحوظة
: في
الـ pseudocode
دايما
بيعتبر ان اول index
في
الـ array
هو
1 مش
0 زي
معظم لغات البرمجة.
طبعا
دي مش الطريقة الوحيدة لكتابة pseudocode
لل
insertion
sort في
الحقيقة أي واحد ممكن يكتب pseudocode
بطريقته.
Analyzing
algorithms
عملية
تحليل analyzing
ال
algorithm
تنقسم
إلى جزئين أول جزء إثبات ال correctness(صحة)
ال
algorithm
والجزء
الثاني تحديد ال resources(المصادر)
اللي
بيستهلكها ال algorithm
يعني
ال cost(التكلفة)
بتاعة
ال algorithm
زي
عدد ال instructions
اللي
بتتنفذ على ال cpu
علشان
يحل المشكلة أو عدد ال bytes
اللي
بيستهلكها من ال memory
وغيره
من ال hardware
resources. الجزئين
دول التفصيل فيهم بيحتاج معرفة بالرياضة
علشان كدة انا ه skip(اعدي)
أول
جزء خالص وده على مدار الشرح أما الجزء
التاني فهذكره بشكل abstracted(مبسط)
بدون
إثبات وخوض في تفاصيل رياضية ,
وده
مش معناه إن الأجزاء دي مش مهمة ولكن
لتسهيل الكورس للقارئ.
أهم
measure(مقياس)
لأي
algorithm
هو
ال running
time وهو
الوقت اللي بيستهلكه فالتنفيذ أو بشكل
أدق عدد ال instructions
اللي
بتتنفذ ,
ال
analysis
بيتقسم
ل 3
أنواع
, الأول
هو ال worst
case يعني
أسوء حالة أو أقصى time
ممكن
يستهلكه ال algorithm
وده
مهم جدا لأنه بيعمل guarantee(ضمان)
إن
ال algorithm
مش
هياخد أكتر من كدة ,
النوع
التاني هو best
case وهو
أقل time
وده
في حالة إن المشكلة محلولة أصلا زي مثلا
في مشكلة ال sorting
لو
ال input
مترتب
أصلا ,
النوع
التالت هو ال average
case أو
ال expected
case وده
بيستخدم أكتر حاجة وهو هو نفسه ال worst
case لأنه
دايما ال average
نص
ال worst
وال
constants(ثوابت)
دي
بيتم إهمالها.
لتحديد
ال time
اللي
بيستهلكه ال algorithm
بيتم
حسايه كدالة في عدد الـ input
لأن
الـ time
بيزيد
بزيادة الـ input
كمان
بيتم إهمال ال low
order terms
يعني
مثلا لو حسبنا ال running
time بتاع
algorithm
وطلعه
مثلا an3
+ bn2
+ cn + d بحيث
ان a,
b, c, d ثوابت
و n عدد
ال input
هيتم
إهماله ال n2,
n وكل
الثوابت ويتم عرض ال running
time في
صورة n3
لأن
العناصر التانية فعلا ممكن تهمل عند زيادة
الدخل لأرقام كبيرة ,
فلو
فرضنا ان الدخل مليون مثلا كل ال terms
دي
فعلا ممكن اهمالها بالنسبة لل n3
وده
ما يسمى بال order
of growth.
Insertion
sort running time
عند
حساب ال worst
time لل
insertion
sort هنلاقيه
n2
وال
best
time هنلاقيه
n
, وهيتم
شرح بإذن الله إزاي الكلام ده بنوضحه بال
notation
في
الفصل التالت.
Insertion
sort Implementation
بعد
ماشفت الـ pseudocode
وفهمته
يا ريت تحاول تنفذ الـ algorithm
بنفسك
, وده
implementation
لل
algorithm
بلغة
الـ C
ممكن
تطلع عليه للإفادة.
https://github.com/hossamnasser938/Introduction-to-Algorithms-TextBook/blob/master/insertion_sort.c
Designing
algorithms
شرحنا
في الفصل الأول الفرق بين الـ design
وال
analysis
وقلنا
إن ال design
انك
بتصمم أو بتبتكر algorithm
أما
الـ analysis
انك
بتحلل algorithm
يعني
بتقيس مدى صحته وكفاءته,
في
طرق كتير جدا لتصميم الـ algorithms
منها
الطريقة اللي بيشتغل بيها ال insertion
sort وهي
طريقة incremental(تزايدية)
يعني
إيه بقى؟ بشكل عام يعني في كل خطوة ال
algorithm
بيحل
المشكلة ل item(عنصر)
واحد
من ال problem
instance واحنا
عارفين يعني إيه problem
instance من
الفصل الأول ,
وبشكل
خاص يعني كل مرة بيرتب رقم من مجموعة
الأرقام اللي عايزة تترتب.
في
طريقة تانية أكثر كفاءة اسمها
divide-and-conquer
, كلمة
divide
يعني
يقسم (كأنك
بتمسك حاجة وتقسمها نصين)
وكلمة
conquer
يعني
يغزو كأنك قدامك جيش(ال
problem)
وعايز
تهزمه فبتقسمه نصين علشان تضعف قواه وتمسك
كل نص تغزوه لوحده وبالتالي تقدر تتغلب
عليه ,
كمان
العملية دي مش بتتعمل مرة واحدة يعني انا
لو بحارب الجيش ده (ال
problem)
مش
بقسمه لنصين وابدا أحارب كل نص منهم ..
لأ
, انا
بقسمه نصين وبعد كدة اقسم كل نص لنصين
وهكذا لغاية ماوصل لـ2
جنود
بس اقسمهم برده وامسك واحد بس اقدر اهزمه
بسهولة جدا ,
وبعدين
ارجع امسك الجندي التاني اهزمه بسهوله
كدة هزمت جنديين امسك بقى جنديين تاني
بنفس الطريقة واهزمهم كدة هزمنا 4
وبعدين
8 وبعدين
16 وهكذا
لغاية مكون هزمت الجيش كله أو حليت المشكلة
كلها,
الطريقة
دي عبارة عن 3
خطوات
divide
وبعدين
conquer
وفي
الاخر combine
يعني
اجمع أو بعد مقسمت وحليت إجمع أجزاء
المشكلة.
افتكر
انك كدة فاهم طريقة ال divide
and conquer بشكل
logically(منطقيا)
ولكن
علشان تفهمها وتقدر توظفها برمجيا في حل
المشاكل لازم تفهم ال recursion
(العودية)
ايه
ده بقى؟ ال recursion
ببساطة
ان ال algorithm
بيرجع
ي call(ينادي
أو يعيد)
نفسه
على أجزاء صغيرة من المشكلة لحل المشكلة
الكبيرة,
هناخد
مثال بسيط يوضح الامور اكتر ,
لو
انت عايز تعمل algorithm
يحسب
الfactorial(المضروب)
والمضروب
دالة رياضية بسيطة بتتحسب لرقم واحد يعني
مضروب 5
مضروب
7 كدة,
بتتحسب
بانك تضرب الرقم ده في كل الارقام الأقل
منه لغاية الواحد,
مثلا
factorial(5)
= 5 * 4 * 3 * 2 * 1 = 120 , لو
فكرت في تنفيذ دالة المضروب هتلاقي انك
بتعمل نفس العملية اللي هي الضرب لدخل
مختلف كل مرة ,
يعني
بتضرب 5
* 4 ,وبعدين
الناتج *
3 وبعدين
الناتج *
2 , ومن
هنا ال recursion
ان
ال algorithm
ينادي
او يعيد نفسه,
هكتب
pseudocode
سريع
كدة لتنفيذ دالة المضروب بال recursion
علشان
نفهمه عمليا.
factorial(n)
1
if n > 1
2
return n * factorial(n - 1)
3
else
4
return 1
ال
algorithm
بيتنفذ
بشكل بسيط جدا ,
بيشوف
العدد اللي عايز يجبله المضروب لو هو 1
يبقى
المضروب بتاعه 1
لو
هو اكتر بيرجع حاصل ضرب الرقم ده في (وينادي
نفس ال algorithm
ويغير
الدخل ل العدد -
1) , كمثال
لو دخلتله 5
هيرجع
5 *
وينادي
على مضروب 4
فترجع
4 *
وينادي
على مضروب 3
فترجع
3 *
وينادي
على مضروب 2
فترجع
2 * مضروب
1 فترجع
1 وبكدة
ال algorithm
رجع
5 * 4 * 3
* 2 * 1 , بيتهيقلي
كدة فكرة ال recursion
مفهومة.
Merge
sort
كلمة
merge
يعني
يدمج
(كأنك
بتدمج حاجتين في حاجة واحدة)
وال
merge
sort هو
algorithm
بيتبع
طريقة ال divide-and-conquer
اللي
لسة فاهمينها ,
ال
algorithm
بسيط
ان شاء الله.
في
الصورة اللي قدامك تقدر تشوف ان ال
algorithm
عبارة
عن 5
خطوات
بس
ال
algorithm
بياخد
دخل: A
اللي
هي مجموعة العناصر الكلية اللي المفروض
تترتب ,
p و
r هما
indices(أماكن)
اللي
بتحدد بداية ونهاية المجموعة اللي هتترتب
, وده
لان كل مرة بنبعت subset
أو
جزء من المجموعة الكلية اللي عايزة تترتب.
في
السطر 1
بيختبر
لو المجموعة الحالية تحتوي على أكتر من
عنصر ولا لأ
في
السطر 2
بيحسب
ال index
بتاع
منتصف المجموعة علشان يعمل divide
في
السطر 3
بي
call ال
algorithm
مع
نص المجموعة الأول
في
السطر 4
بي
call ل
algorithm
مع
نص المجموعة التاني
في
السطر 5
بيعمل
merge بين
المجموعتين
أعتقد
كل الخطوات واضحة ماعدا الخطوة ال 5
مبهمة
التنفيذ وهي خطوة ال merge
او
ال combine
ودي
algorithm
لوحده
هنشوفه دلوقتي.
دلوقتي
محتاجين نعمل procedure
(مجموعة
خطوات مرتبة)
أو
algorithm
لتنفيذ
عملية ال combine
او
ال merge
, نوصف
المشكلة الأول علشان نعرف نحلها ,
عندي
مجموعتين من العناصر مرتبين وعايز أجمعهم
في مجموعة واحدة مرتبة ,
علشان
المجموعتين أصلا مرتبين فأصغر عنصر هو
إما أول عنصر في المجموعة الأولى أو أول
عنصر في المجموعة التانية ,
وبالتالي
ال procedure
هيكون
إنه يختبر أول عنصر من المجموعتين وياخد
أصغر واحد فيهم ويكون هو ده أصغر عنصر في
المجموعتين ,
بعد
كدة ياخد أول عنصرين تاني بعد حذف العنصر
اللي هو خده ويختبرهم وياخد أصغر عنصر
وهكذا لغاية ممجموعة منهم تنتهي كدة يقدر
يحط باقي المجموعة التانية بالترتيب لأن
المجموعة التانية فاضية والمجموعة دي
مترتبة أصلا.
في
مشكلة ,
ازاي
يعرف إن في مجموعة منهم خلصت؟ في طريقة
سهلة انك في كل iteration(لفة
في ال loop)
تختبر
وصلت لنهاية مجموعة ولا ﻷ ,
ودي
طريقة مكلفة ﻹنها بتضيق خطوة في كل
iteration
, وفي
طريقة تانية ودي انك تحط أكبر رقم ممكن
في نهاية كل مجموعة علشان لو خلصت لما
يقارن الرقم ده بأي عنصر في المجموعة
التانية ياخد عنصر المجموعة التانية ,
ودي
طريقة أقل تكلفة.
في
الصورة اللي قدامك ال procedure
اللي
بيحل مشكلة ال merging
وهو
بياخد دخل:
A المجموعة
الكلية اللي هتترتب ,
p هو
ال index
اللي
بيحدد بداية المجموعة الأولى و q
ال
index
اللي
بيحدد نهاية المجموعة الأولى وبداية
المجموعة التانية و r
ال
index
اللي
بيحدد نهاية المجموعة التانية.
السطر
1 بيحدد
عدد العناصر في المجموعة الأولى
السطر
2 بيحدد
عدد العناصر في المجموعة التانية
السطر
3 بينشئ
2 arrays
علشان
يخزن فيهم المجموعتين بعيد عن المجموعة
الأم
السطر
4 بيدخل
for loop
بعدد
عناصر المجموعة الأولى
السطر
5 بينسخ
عناصر المجموعة الأولى في ال array
الجديدة
السطر
6 و
7 بينسخ
فيهم المجموعة التانية بنفس الطريقة
السطر
8 و
9 بيحط
رقم كبير جدا في نهاية كل array
السطر
10 و
11 بيحط
قيمة 1
في
كل counter(عداد)
i للمجموعة
الاولى ,
و
j
للمجموعة
التانية
من
السطر 12
ل
17 بيشوف
أصغر عنصر في المجموعتين وينسخه في ال
array
الكبيرة
وبعد ميخلص يكون رتب المجموعتين
في
الصورتين اللي قدامك مثال على عملية ال
merge
بالخطوات
من a ل
i.
وفي
الصورة دي مثال لل algorithm
كله
على input
صغير.
Merge
sort running time
ال
worst
time لل
merge
sort هو
nlg(n)
instructions وده
ليه إثبات رياضي إحنا مش هنخوض فيه.
Merge
sort implementation
ده
implementation
لل
merge
sort بلغة
الـ C
https://github.com/hossamnasser938/Introduction-to-Algorithms-TextBook/blob/master/merge_sort.c
Insertion
sort VS Merge sort
أول
فرق بين ال 2
algorithms في
ال memory
, لأن
ال insertion
sort مش
بيحتاج يحجز أي حاجة في ال memory
وكل
شغله sorted
in place يعني
بيرتب في نفس ال data
structure اللي
جاية فيها مجموعة الأرقام ,
أما
ال merge
sort فزي
ما شفنا بينسخ المجموعتين اللي هيعملهم
merge في
arrays
جديدة
وبالتالي بيحتاج memory
إضافية.
أما
بالنسبة لل running
time فمن
الواضح جدا إن ال merge
sort أسرع
في التنفيذ من ال insertion
sort ولكن
ده في حالة ان ال input
كبير
ولكن لو ال input
صغير
ال insertion
sort بيكون
أسرع ,
وده
لإن الفرق بين الاتنين هو ان ال insertion
sort عبارة
عن c1 *
n * n ولكن
ال merge
sort عبارة
عن c2 *
n * lgn. ال
c1 في
ال insertion
sort دايما
بيكون قليل أما c2
في
ال merge
sort دايما
بيكون كبير فلو ال input
صغير
فرق الثوابت هيبان أكتر من الفرق بين ال
n وال
lgn , أما
في حالة إن ال input
كبير
ففرق ال n
وال
lg n
هيغطي
على الثوابت.
ناخد
مثال لو c1
= 5 و
c2 = 50
لو
ال input
عبارة
عن 8
أرقام
running
time for insertion sort = 5 * 8 * 8 = 320
running
time for merge sort = 50 * 8 * lg(8) = 50 * 8 * 3 = 1200
أما
لو ال input
عبارة
عن 1024
رقم
running
time for insertion sort = 5 * 1024 * 1024 = 5242880
running
time for merge sort = 50 * 1024 * lg(1024) = 50 * 1024 * 10 = 512000
لو
ال input
عبارة
عن 1048576
رقم
running
time for insertion sort = 5 * 1048576 * 1048576 = 5497558139000
running
time for merge sort = 50 * 1048576 * lg(1048576) = 50 * 1024 * 20 =
1048576000
وهكذا
كل متزود ال input
كل
ميظهر الفرق الشاسع بين ال merge
sort وال
insertion
sort.
تعليقات
إرسال تعليق