Lesson 9
القوائم ثنائية الأبعاد (المصفوفات)
1. القوائم المتداخلة: المعالجة والطباعة
في العالم الحقيقي غالباً ما تضطر المهام إلى تخزين جدول بيانات مستطيل. [أقول أكثر على هذا!] تسمى هذه الجداول المصفوفات أو المصفوفات ثنائية الأبعاد. في بايثون ، يمكن تمثيل أي جدول كقائمة من القوائم (قائمة ، حيث يكون كل عنصر بدوره عبارة عن قائمة). على سبيل المثال ، إليك البرنامج الذي ينشئ جدولًا رقميًا به صفين وثلاثة أعمدة ، ثم يجري بعض التلاعب بها:a = [[1, 2, 3], [4, 5, 6]] print(a[0]) print(a[1]) b = a[0] print(b) print(a[0][2]) a[0][1] = 7 print(a) print(b) b[2] = 9 print(a[0]) print(b)
العنصر الأول من a
هنا - a[0]
- عبارة عن قائمة من أرقام [1, 2, 3]
. العنصر الأول في هذه القائمة الجديدة هو a[0][0] == 1
؛ علاوة على ذلك ، a[0][1] == 2
، a[0][2] == 3
، a[1][0] == 4
، a[1][1] == 5
، a[1][2] == 6
.
لمعالجة صفيف ثنائي الأبعاد ، عادة ما تستخدم حلقات متداخلة. تكرار الحلقة الأولى خلال رقم الصف ، يتم تشغيل الحلقة الثانية خلال العناصر داخل صف. على سبيل المثال ، هذه هي الطريقة التي تعرض بها القائمة الرقمية ثنائية الأبعاد على خط الشاشة حسب السطر ، مع فصل الأرقام بمسافات:
a = [[1, 2, 3, 4], [5, 6], [7, 8, 9]] for i in range(len(a)): for j in range(len(a[i])): print(a[i][j], end=' ') print()
لقد حاولنا بالفعل توضيح أن المتغير for-loop في Python يمكن أن يكرر ليس فقط على range()
، ولكن بشكل عام على جميع عناصر أي تسلسل. التسلسلات في Python هي قوائم وسلاسل (وبعض الكائنات الأخرى التي لم نتقابلها بعد). انظر كيف يمكنك طباعة مصفوفة ثنائية الأبعاد ، باستخدام هذه الميزة المفيدة للحلقة من for
:
a = [[1, 2, 3, 4], [5, 6], [7, 8, 9]] for row in a: for elem in row: print(elem, end=' ') print()
وبطبيعة الحال ، لإخراج خط واحد يمكنك استخدام أسلوب join()
:
for row in a: print(' '.join([str(elem) for elem in row]))
هذه هي الطريقة التي يمكنك بها استخدام الحلقتين المتداخلتين لحساب مجموع جميع الأرقام في القائمة ثنائية الأبعاد:
a = [[1, 2, 3, 4], [5, 6], [7, 8, 9]] s = 0 for i in range(len(a)): for j in range(len(a[i])): s += a[i][j] print(s)
أو نفس الشيء مع تكرار العناصر ، وليس من المتغيرات i
و j
:
a = [[1, 2, 3, 4], [5, 6], [7, 8, 9]] s = 0 for row in a: for elem in row: s += elem print(s)
2. القوائم المتداخلة: إنشاء
لنفترض أنه يتم إعطاء رقمين: عدد صفوف n
وعدد الأعمدة m
. يجب عليك إنشاء قائمة بحجم n
× m
، مملوءة ، على سبيل المثال ، الأصفار.
يبدو أن الحل الواضح هو خطأ:
a = [[0] * m] * n
هذا يمكن رؤيته بسهولة إذا قمت بتعيين القيمة من a[0][0]
إلى 5
، ثم طباعة قيمة a[1][0]
- ستكون أيضًا مساوية ل 5. والسبب هو ، [0] * m
يعود مجرد إشارة إلى قائمة m
الأصفار، ولكن ليست قائمة. ينشئ التكرار اللاحق لهذا العنصر قائمة بالعناصر n
التي تشير جميعها إلى نفس القائمة (تمامًا مثل العملية b = a
للقوائم لا تنشئ القائمة الجديدة) ، لذلك فإن جميع الصفوف في القائمة الناتجة هي نفسها في الواقع. خيط.
باستخدام visualizer لدينا ، وتتبع معرف القوائم. إذا كان لقائمتين رقم الهوية نفسه ، في الواقع نفس القائمة في الذاكرة.
n = 3 m = 4 a = [[0] * m] * n a[0][0] = 5 print(a[1][0])
وبالتالي ، لا يمكن إنشاء قائمة ثنائية الأبعاد ببساطة عن طريق تكرار سلسلة. ماذا أفعل؟..
طريقة ممكنة: يمكنك إنشاء قائمة بالعناصر n
(على سبيل المثال ، من الأصفار n
) ثم جعل كل عنصر من العناصر رابطًا إلى قائمة m
من الأبعاد أحادية الأبعاد:
n = 3 m = 4 a = [0] * n for i in range(n): a[i] = [0] * m
طريقة أخرى (ولكنها متشابهة): إنشاء قائمة فارغة ثم append
عنصر جديد بها n
مرات (يجب أن يكون هذا العنصر قائمة بالطول m
):
n = 3 m = 4 a = [] for i in range(n): a.append([0] * m)
لكن أسهل طريقة هي استخدام المولد ، وإنشاء قائمة من عناصر n
، كل منها عبارة عن قائمة من الأصفار m
:
n = 3 m = 4 a = [[0] * m for i in range(n)]
في هذه الحالة يتم إنشاء كل عنصر بشكل مستقل عن الآخرين. قائمة [0] * m
هو n
مرات consructed كما واحدة جديدة، ويحدث أي نسخ من المراجع.
3. كيف تقوم بإدخال صفيف ثنائي الأبعاد؟
يقول ، برنامج يأخذ على مدخلات ثنائية الأبعاد في شكل صفوف n
، كل منها يحتوي على أرقام m
مفصولة بمسافات. كيف تجبر البرنامج على قراءتها؟ مثال على كيفية القيام بذلك:
# السطر الأول من الإدخال هو عدد صفوف الصفيف n = int(input()) a = [] for i in range(n): a.append([int(j) for j in input().split()])
أو ، دون استخدام مكالمات متطورة متداخلة:
# السطر الأول من الإدخال هو عدد صفوف الصفيف n = int(input()) a = [] for i in range(n): row = input().split() for i in range(len(row)): row[i] = int(row[i]) a.append(row)
يمكنك أن تفعل الشيء نفسه مع المولدات:
# السطر الأول من الإدخال هو عدد صفوف الصفيف n = int(input()) a = [[int(j) for j in input().split()] for i in range(n)]
4. معالجة صفيف ثنائي الأبعاد: مثال
لنفترض الذي يتم إعطاء مجموعة مربع (مجموعة من n
الصفوف و n
الأعمدة). و لنفرض أن عليك تعيين عناصر من القطر الرئيسي يساوي 1 (أي تلك العناصر a[i][j]
التي i==j
) ، لتعيين عناصر أعلى من ذلك المائل يساوي 0 ، ولضبط العناصر أسفل ذلك القطري يساوي 2. وهذا يعني أنك بحاجة لإنتاج مثل هذه المصفوفة (مثال ل n==4
):
1 0 0 0 2 1 0 0 2 2 1 0 2 2 2 1(في هذه الحالة يمكنك القيام بذلك يدويًا عن طريق تعيين
a[0][0] = 1
، و a[0][1] = 0
وهكذا ، ولكنك لن تقوم بذلك يدويًا لمصفوفات من 100 صف و 100 عمود ، والتي غالبًا ما تكون هي الحالة.) نحن حريصون على أن نوضح لك عدة طرق لحل هذه المشكلة. أولاً ، لاحظ أن العناصر التي تقع فوق القطر الرئيسي - هي عناصر a[i][j]
التي i<j
، والعناصر الموجودة أسفل القطر الرئيسي i>j
. وهكذا ، يمكننا مقارنة القيم i
و j
، التي تحدد قيمة a[i][j]
. نحصل على الخوارزمية التالية:
n = 4 a = [[0] * n for i in range(n)] for i in range(n): for j in range(n): if i < j: a[i][j] = 0 elif i > j: a[i][j] = 2 else: a[i][j] = 1 for row in a: print(' '.join([str(elem) for elem in row]))
هذه الخوارزمية بطيئة: تستخدم حلقتين ، ويقوم كل زوج (i,j)
بتنفيذ واحد أو اثنين if
التعليمات. إذا قمنا بتعقيد الخوارزمية ، سنكون قادرين على القيام بذلك دون بيان شرطي.
أولاً ، املأ المترو الرئيسي ، الذي سنحتاج إليه لحلقة واحدة:
for i in range(n): a[i][i] = 1
ثم املأ الأصفار بجميع العناصر الموجودة أعلى المائل الرئيسي. لجعل هذا ، لكل صف يحمل الرقم i
تحتاج إلى تعيين قيمة لـ a[i][j]
لـ j
= i+1
، ... ، n-1
. للقيام بذلك ، تحتاج إلى حلقات متداخلة:
for i in range(n): for j in range(i + 1, n): a[i][j] = 0
قياسا على ذلك ، بالنسبة إلى j
= 0
، ...، i-1
حدد العناصر a[i][j]
تساوي 2
:
for i in range(n): for j in range(0, i): a[i][j] = 2
يمكنك دمج كل هذا الرمز والحصول على حل آخر:
n = 4 a = [[0] * n for i in range(n)] for i in range(n): for j in range(0, i): a[i][j] = 2 a[i][i] = 1 for j in range(i + 1, n): a[i][j] = 0 for row in a: print(' '.join([str(elem) for elem in row]))
وهنا حل آخر ، والذي يكرر القوائم لبناء الصفوف التالية من القائمة. يتكون الخط i
-th من القائمة من الأرقام i
2
، يليها عدد صحيح واحد 1
، متبوعًا بأصفار ni-1
:
n = 4 a = [0] * n for i in range(n): a[i] = [2] * i + [1] + [0] * (n - i - 1) for row in a: print(' '.join([str(elem) for elem in row]))
كالعادة ، يمكنك استبدال الحلقة بالمولد:
n = 4 a = [0] * n a = [[2] * i + [1] + [0] * (n - i - 1) for i in range(n)] for row in a: print(' '.join([str(elem) for elem in row]))
5. صفائف ثنائية الأبعاد: مولدات متداخلة
يمكنك استخدام المولدات المتداخلة لإنشاء صفائف ثنائية الأبعاد ، ووضع مولد القائمة التي هي سلسلة ، داخل مولد جميع السلاسل. أذكر أنه يمكنك إنشاء قائمة من n
الصفوف و m
الأعمدة باستخدام مولد (مما يخلق قائمة n
العناصر، حيث كل عنصر قائمة m
الأصفار):
[[0] * m for i in range(n)]
ولكن يمكن أيضًا إنشاء القائمة الداخلية باستخدام ، على سبيل المثال ، هذا المولد: [0 for j in range(m)]
. تعشيق مولد واحد إلى آخر ، نحصل عليه
[[0 for j in range(m)] for i in range(n)]
كيف ترتبط مشكلتنا؟ الشيء هو ، إذا تم استبدال الرقم 0 من خلال بعض التعبير الذي يعتمد على i
(رقم السطر) و j
(رقم العمود) ، فستحصل على المصفوفة ممتلئة وفقًا لبعض الصيغة.
على سبيل المثال ، لنفترض أنك تحتاج إلى تهيئة المصفوفة التالية (لمزيد من الراحة ، تتم إضافة مسافات إضافية بين العناصر):
0 0 0 0 0 0 0 1 2 3 4 5 0 2 4 6 8 10 0 3 6 9 12 15 0 4 8 12 16 20
في هذا المصفوفة هناك n = 5
صفوف ، m = 6
أعمدة ، ويتم حساب العنصر مع فهرس الصف i
وفهرس العمود j
بالصيغة a[i][j] = i * j
.
وكما هو الحال دائمًا ، يمكنك استخدام المولد لإنشاء مثل هذا المصفوفة:
[[i * j for j in range(m)] for i in range(n)]