ماذا يعني range في الخوارزميات وهياكل البيانات؟ شرح عملي بالأمثلة

شروحات تقنية

كلمة range في البرمجة تعني ببساطة "نطاق": مجموعة متتابعة من القيم أو العناصر تقع بين بدايةٍ ونهايةٍ محدّدتين. لكن المصطلح نفسه يحمل ثلاثة معانٍ مختلفة يخلط بينها كثيرون، وهذا الخلط هو سبب معظم الحيرة: هناك النطاق كـ"مجال قيم"، وهناك دالّة range() في لغة بايثون، وهناك "المدى" الإحصائي الذي يساوي أكبر قيمة ناقص أصغر قيمة. أوضِّح لك كلًّا منها بأمثلة عملية حتى تعرف أيّها يقصده سؤالك.

المعنى الأساسي: النطاق كمجال من القيم

في سياق الخوارزميات، الـ range هو المجال الذي يمكن أن يأخذه متغيّر أو مؤشّر بين حدّين: حدٍّ أدنى (start) وحدٍّ أعلى (end). نكتبه عادةً على شكل فترة، مثل [3, 9] أي كل الأعداد من 3 إلى 9.

النقطة التي يخطئ فيها المبتدئون كثيرًا هي هل النهاية داخلة أم لا؟ في معظم لغات البرمجة يُستخدم النطاق "نصف المفتوح" [start, end): البداية داخلة والنهاية خارجة. فالنطاق [0, 5) يشمل 0, 1, 2, 3, 4 فقط، وليس 5. هذا الاصطلاح ليس مصادفة؛ فهو يجعل طول النطاق يساوي end - start مباشرةً، ويجعل نطاقين متتاليين مثل [0, 5) و[5, 10) يتلاصقان دون تداخل أو فجوة.

دالّة range() في بايثون

هذا هو المعنى الأكثر بحثًا فعليًّا. range() دالّة مبنية في بايثون تُولّد سلسلة أعداد، وتُستخدم غالبًا مع حلقة for:

for i in range(5):
    print(i)   # يطبع 0 1 2 3 4

وتقبل الدالّة حتى ثلاثة معطيات: range(start, stop, step):

  1. range(2, 8) ← الأعداد من 2 إلى 7.
  2. range(0, 10, 2) ← الأعداد الزوجية 0, 2, 4, 6, 8.
  3. range(10, 0, -1) ← عدٌّ تنازلي من 10 إلى 1.

انتبه إلى ملاحظتين تختصران عليك أخطاءً كثيرة: القيمة stop غير داخلة (تمامًا كالنطاق نصف المفتوح)، وrange في بايثون 3 ليست قائمة بل كائن "كسول" لا يُنشئ الأعداد في الذاكرة إلا عند الحاجة، ولهذا يمكنك كتابة range(1000000) دون أن تلتهم الذاكرة. لو أردت قائمة فعلية استخدم list(range(...)).

المدى الإحصائي: أكبر قيمة ناقص أصغر قيمة

هناك معنى ثالث مختلف تمامًا يظهر في تحليل البيانات: المدى (statistical range) هو الفرق بين أكبر عنصر وأصغر عنصر في مجموعة. فإذا كانت لديك الدرجات [40, 72, 55, 90] فالمدى = 90 − 40 = 50. هذا رقم واحد يصف مدى تشتّت البيانات، ولا علاقة له بحلقات التكرار ولا بدالّة بايثون. إذا رأيت "range" في سياق إحصاء أو رياضيات، فهذا هو المقصود غالبًا.

استعلامات النطاق في هياكل البيانات

في هياكل البيانات المتقدّمة يظهر مفهوم استعلام النطاق (range query): أن تسأل عن خاصيّةٍ لمجموعة عناصر متتالية، مثل "مجموع عناصر المصفوفة من الموضع 2 إلى الموضع 7" أو "أصغر قيمة في هذا المجال".

الطريقة الساذجة أن تمرّ على كل العناصر في كل استعلام، وكلفتها O(n) لكل سؤال. لكن الخوارزميات الجيدة تُسرّع ذلك بشكل هائل:

  • مجموع البادئة (Prefix Sum): تحضِّر مصفوفة تراكمية مرّة واحدة، فيصير مجموع أي نطاق عملية طرح واحدة بكلفة O(1). مثالية عندما لا تتغيّر البيانات.
  • شجرة القطع (Segment Tree) وشجرة فينويك (Fenwick / BIT): تتيحان استعلام النطاق وتحديث العناصر معًا بكلفة O(log n)، وهي الأنسب حين تتغيّر البيانات باستمرار.

الخلاصة العملية هنا: كلمة range في هذا السياق لا تعني "قيمة" بل تعني "عملية على مقطع متّصل"، والمهارة كلها في اختيار البنية التي تجعل تلك العملية سريعة.

جدول مقارنة: أي "range" تقصد؟

المعنىالسياقمثالالناتج
نطاق/فترةالخوارزميات عمومًا[0, 5)القيم 0–4
دالّة range()بايثونrange(0, 5)مولِّد للأعداد 0–4
المدىالإحصاءmax − min لـ[40,90]50
استعلام النطاقهياكل البياناتمجموع المواضع 2..7قيمة مجمّعة واحدة

نصيحة عملية من التجربة

أكثر خطأ يقع فيه المبرمج المبتدئ هو "خطأ الفرق بواحد" (off-by-one): أن يكتب range(1, n) وهو يظن أنه سيصل إلى n، بينما تتوقف عند n-1. القاعدة الذهبية: تذكّر دائمًا أن الحدّ الأعلى غير داخل في بايثون وفي معظم لغات البرمجة. وإذا أردت المرور على كامل مصفوفة طولها n، فالصيغة الصحيحة هي range(n)، لا range(1, n) ولا range(0, n+1).

الأسئلة الشائعة

هل range في بايثون تُنشئ قائمة؟ لا في بايثون 3؛ هي كائن كسول يُنتج الأعداد عند الطلب فقط، ما يوفّر الذاكرة. حوّلها بـlist(range(...)) إن احتجت قائمة فعلية.

لماذا لا يشمل النطاق قيمته النهائية؟ لأن اصطلاح [start, end) نصف المفتوح يجعل طول النطاق يساوي end − start تمامًا، ويجعل النطاقات المتتالية تتلاصق دون تداخل، وهذا يبسّط الخوارزميات كثيرًا.

ما الفرق بين range و interval؟ عمليًّا متقاربان؛ كلاهما يصف مجالًا بين حدّين. غير أن "interval" أكثر شيوعًا في الرياضيات مع تحديد صريح للانفتاح والانغلاق، بينما "range" أعمّ ويُستخدم في البرمجة والإحصاء معًا.

كيف أحسب مجموع نطاق بسرعة إذا كانت البيانات ثابتة؟ استخدم تقنية مجموع البادئة (Prefix Sum): جهّز مصفوفة تراكمية مرّة واحدة، ثم احسب مجموع أي نطاق بعملية طرح واحدة بكلفة O(1).