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)]