Two-dimensional lists (arrays) - Learn Python 3 - Snakify

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)
Advertising by Google, may be based on your interests

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 كما واحدة جديدة، ويحدث أي نسخ من المراجع.

Advertising by Google, may be based on your interests

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)]
Advertising by Google, may be based on your interests

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]))    
Advertising by Google, may be based on your interests

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)]
Advertising by Google, may be based on your interests