أربعة عشر شهرًا من الأسبوع نفسه

أربعة عشر شهرًا في A2SV — خمس جلسات في الأسبوع، وContest في آخر يوم، وأصعب ما في الأسبوع يأتي في الموضع نفسه منه. وما خرجت به لم يكن مجموعة Algorithms، بل سؤالًا: لا هل يعمل الكود؟ بل كم يكلّف تشغيله؟ وهو نفسه السؤال الذي تحكم به على كود لم تكتبه.

Algorithms · Competitive programming · Learning

كان أصعب ما في الأسبوع يأتي في الموضع نفسه منه.

يُشرَح Algorithm جديد، ويكون مفهومًا وهو يُشرَح. ثم يُطرَح أول Problem يستخدمه، فأجلس أمامه ولا أعرف من أين أبدأ.

قضيت أربعة عشر شهرًا في تلك الفجوة، في A2SV — Africa to Silicon Valley، وهي منظمة أمريكية غير ربحية تُدرّس هندسة البرمجيات لطلاب الجامعات في أفريقيا. وسنتها الأولى الإلزامية هي هياكل البيانات والخوارزميات والبرمجة الديناميكية والبرمجة التنافسية. درستها عن بُعد مع الدفعة السادسة، من يناير 2025 إلى فبراير 2026.

رشّحه لي صديق في الجامعة. قال إنه من أقوى البرامج في أفريقيا، وإنه يهدف إلى تخريج مهندسين يملكون طريقة التفكير لا أشخاص يجيدون كتابة الكود. فقلت لنفسي: ولمَ لا. وهو نفسه لم يلتحق به قطّ. لم يكن لديه الوقت.

والسبب الثاني ما كنت لأكتبه في طلب الالتحاق. كان البرنامج يُدار بالإنجليزية، وكانت إنجليزيتي وقتها متوسّطة، ولم أكن أحبّ أن أتكلّم بها أمام الناس. ولم يكن شيء من ذلك عائقًا بالصورة التي يبدو عليها. بل كان ذلك هو الموقف الذي احتجت أن أُوضَع فيه. والعبارة التي وصفت بها ذلك حينها: «أرمي نفسي في البحر».

كانت المقابلة بالإنجليزية

كان في المقابلة سؤال بسيط نسبيًا في الـProblem Solving. حللته، ثم تحدّثت عن الـTime Complexity والـSpace Complexity لِما كتبته.

والـComplexity هو الجزء الذي أشير إليه اليوم، لأنه تبيّن أنه صورة مصغّرة من البرنامج كله: لا: هل يعمل الشيء؟ بل: كم يكلّف تشغيله؟ كنت أظنّ أن البرمجة التنافسية تعني Contests، أي مجموعة Problems تحلّها. ثم صارت تعني ذلك السؤال الأخير، مرة بعد مرة.

كنت أقرأ الحلّ من أجل شيء واحد

كان ذلك يحدث مع الـAlgorithms الصعبة، سواء كانت جديدة عليّ أو سمعت بها من قبل وظلّت صعبة، ومعرفتي بأن هذا التعثّر قادم لم تجعله أصغر يومًا.

حين كنت أتعثّر، كنت أحاول من جديد، مرة بعد مرة، وأحيانًا أقرأ حلّ شخص آخر — لكنّي لم أكتفِ بقراءته قطّ. كنت أقرأه من أجل شيء واحد: الموضع الذي تعثّرت عنده بالضبط. ثم أفهم كيف فكّر صاحبه، وأكتب الـImplementation بنفسي. وحين أفهم كيف تجاوز الحلُّ ذلك الموضع، يتّضح الباقي في الغالب.

كففت عن محاولة تخيّل ما في الوسط

الموضوع الذي ظلّت تلك الفجوة مفتوحة فيه أطول مدة هو البرمجة الديناميكية، وأصعب ما فيه لم يكن الكود يومًا. كان التخيّل. الـProblem مبنيّة على شيء يتكرّر. وكنت أحاول أن أمسك التكرار كله في رأسي دفعة واحدة — كل State، وكيف تتّصل كل واحدة منها بما قبلها وما بعدها. كان أكثر من أن يُمسَك، وكانت الأجزاء التي يُفترض أن تتّصل تنفكّ وأنا أنظر إلى ما يليها.

وما أخرجني من ذلك أنني تخلّيت عنه.

بدل أن أتخيّل كل ما يحدث في الوسط، صرت أحدّد الطرفين: أين تبدأ الـState، وأين تقف، وما الـBase Cases — أي الحالات التي تُعرَف إجابتها من دون حساب شيء. وهذه الثلاثة صغيرة، تتّسع لها ورقة واحدة، وتعرف حين تخطئ فيها.

ثم تتكفّل الـRecurrence بما بينها. هي جملة عن خطوة واحدة: هذه الـState بدلالة الـStates التي تعتمد عليها. فإذا صحّت الخطوة وصحّ الطرفان، كان الوسط الذي عجزت عن تخيّله محسوبًا سلفًا. ولست مضطرًّا إلى رؤيته أبدًا.

يبدو هذا اختصارًا وهو نقيضه. الوسط ليس لي أن أتخيّله. هو الجزء الذي سلّمته إلى الـRecurrence.

خمس جلسات في الأسبوع

كان كل أسبوع يفتح موضوعًا جديدًا — DFS في أسبوع، وBFS في آخر — يُشرَح نظريًا أولًا، ثم يُطبَّق على Problems سهلة، ثم على Problems تكفّ عن كونها سهلة. وكان آخر يوم في الأسبوع، الجمعة أو السبت، Contest على Codeforces ندخله جميعًا. أربع جلسات على الموضوع، ثم مجموعة محدّدة من الـProblems ووقت يجري. ولا موضع تختبئ فيه فكرة مفهومة نصف فهم.

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

وكانت الجلسات ليلًا، وهو ما جعل الجمع بينه وبين دراسة جامعية وتدريب وشركة ممكنًا. ولم يكن في الموازنة أسلوب معيّن. كان A2SV يعطيني ما يكفي لألّا أكون مستعدًّا للاستغناء عنه، وكان ثمن الاحتفاظ به أن أجهد نفسي أكثر لأرتّب كل شيء آخر حوله. دخلت بعضها منهكًا، وكنت أتعامل مع الجلسة على أنها أهمّ شيء عليّ أن أركّز فيه.

ما تقوله البطاقات

ستّ مرات في 2025 نشر A2SV بطاقة Best Performer of the Week تحمل اسمي. وكل بطاقة تحمل أربعة أرقام. الحضور من خمسة، وهو ما يخبرك أن في الأسبوع خمس جلسات: 5/5 في أربع بطاقات، و4/5 في الاثنتين الأخريين. والـConsistency Strike من سبعة، أي إنه يومي — 7/7 في خمس بطاقات، و6/7 في واحدة. وعدد الـProblems المحلولة على الـHUB تراوح بين 15 و26. وProblems الـContest من ستة: 6/6 مرة، و5/6 ثلاث مرات، و4/6 مرتين.

والبطاقة التي تسمّي أفضل أداء في أسبوع هي بحكم تكوينها انتقاء لأفضل ما في ذلك الأسبوع. ومع ذلك تحتوي بطاقاتي على جلستين فائتتين، وStrike يومي انكسر، وأسبوعين حللت فيهما أربعًا من ستّ Problems في الـContest.

الثاني، في مسابقة بين مجموعات A2SV نفسها

الترتيبان التاليان كلاهما من مسابقات A2SV نفسها، تُقام على Codeforces بوصفها Contests خاصة بين مجموعاتها. وكانت مجموعتي G6E.

في A2SV Remote G6 Contest #14 جئت الثاني في الترتيب المجمّد، بستّ Problems وPenalty قدره 339. والأول حلّ ستًّا أيضًا بـ243، والثالث ستًّا بـ377. فالثلاثة الأوائل حلّوا كل شيء، وما فرّق بيننا هو وقت الـPenalty. وفي A2SV Remote Group Contest #2، ومعي زميل في الفريق، جئنا الثانيين بسبع Problems وPenalty قدره 404.

و«الثاني» هنا يعني الثاني في المسابقة على مستوى مجموعات A2SV — لا مركزًا على Codeforces، وإن كان يُقرأ هكذا إذا انتُزع من مسابقته. والرقم معلومة عن التجربة التي خرج منها، حتى حين يكون رقمي أنا.

اللحظة الأفضل هي التي أخسر فيها

أفضل ما في الأمر لحظة وصول الحلّ، وهي أقوى ما تكون بعد أن تكون Problem قد هزمتني مرات، ثم أرى أخيرًا الموضع الذي كان يوقفني.

وهناك لحظة أفضل منها، وهي التي أخسر فيها. أن أقرأ حلًّا أبسط وأذكى من حلّي، وأفكّر: كيف فكّر فيه هذا الشخص على هذا النحو؟

ويتعامل بعض الناس مع الأمر بحدّة أكبر من ذلك — غضبًا من Problem، أو توتّرًا شديدًا قبل Contest. والمنافسة وحدها تُنتج الدافع إلى كتابة أفضل كود تستطيعه، لا أول كود يعمل. وبقدر ما رأيت، لم يكن الغضب يضيف إلى ذلك شيئًا.

ما الذي تغيّر في الكود الذي أكتبه

قبل هذا كنت أستطيع أن أكتب كودًا وأمامي سؤال واحد: هل يعمل؟ ولهذا السؤال إجابة، وإجابته في النهاية نعم، وهو سؤال صغير. أمّا الآن فأول نسخة تعمل هي موضع البداية لا موضع التوقّف. كم يأخذ هذا الحلّ من وقت؟ وكم يستهلك من Memory؟ وهل هو صحيح فعلًا، أم صحيح فقط على الحالات التي صادف أن جرّبتها؟ وهل هناك طريق أبسط وأسرع إلى النتيجة نفسها؟

وجاء مع ذلك أمران أصغر. صرت أقدر على تخيّل تنفيذ الكود في رأسي بصورة أوضح، فأصل إلى الموضع المحتمل للخطأ أسرع. وصرت أنظر إلى كود فأتوقّع على نحو أفضل إن كان سيبطؤ عند Input كبير.

تحتاجه لتحكم على العمل، لا لتؤدّيه

لا شيء من هذا ضروري لمهندس AI، لا على نحو مباشر ولا على نحو يوميّ. فذلك العمل يجري في أغلبه مع موديلات موجودة بالفعل، ومع مكتبات وأدوات جاهزة. أمّا بناء الموديلات من الصفر، أو الـOptimization عند ذلك المستوى، فهناك تكبر أهمية هذا كله.

وهو ليس بلا فائدة كذلك، وثمّة موضع واحد يكفّ فيه عن كونه اختياريًا: الكود الذي لم أكتبه. الـAI سيكتب لك منه الكثير، وهو يعمل. لكنه ليس دائمًا Optimized، وأحيانًا يلفّ لفة طويلة ليصل إلى شيء يمكن كتابته بطريقة أبسط. وأن تعرف أيّ الاثنين أمامك يحتاج بالضبط ما أُنفقت فيه تلك السنة — الـComplexity، والبنية، وحسّ بما سيفعله الكود حين يكبر الـInput.

ولهذا فالاعتراض القائل: «أنا أدع الـAI يكتب الكود، فلست محتاجًا إلى تعلّم هذه الأشياء» — اعتراض مقلوب. الحجّة ليست أنك تحتاج هذا لتؤدّي العمل، بل أنك تحتاجه لتعرف إن كان العمل قد أُدِّي جيدًا. وإلا فالاختبار الوحيد الباقي هو: هل عمل الشيء؟ وأن يعمل الشيءُ حدٌّ منخفض يتجاوزه الكود المولَّد باستمرار.

النصف الذي لا أستطيع عرضه بالأرقام

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

البرنامج لم يعلّمني أن أكتب كودًا يعمل. كنت أستطيع ذلك في يناير 2025. علّمني أن أسأل كم يكلّف الكود، وأن أظلّ أسأل عن كود لم أكتبه.

كل الكتابات