Вкладені списки

Обробка і вивід вкладених списків

Часто в задачах доводиться зберігати прямокутні таблиці з даними. Такі таблиці називаються матрицями або двовимірними масивами. У мові програмування Пайтон таблицю можна представити у вигляді списку рядків, кожен елемент якого є в свою чергу списком, наприклад, чисел.
Наприклад, створити числову таблицю з двох рядків і трьох стовпців можна так:

A = [[1, 2, 3], [4, 5, 6]]
Тут перший рядок списку 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.

Для обробки і виведення списку як правило використовується два вкладених циклу. Перший цикл по номеру рядка, другий цикл по елементах всередині рядка. Наприклад, вивести двовимірний числовий список на екран через підрядник, розділяючи числа пробілами всередині одного рядка, можна так:

# Приклад 1
A = [[1, 2, 3], [4, 5, 6]]
for i in range(len(A)):
    for j in range(len(A[i])):
        print(A[i][j], end = ' ')
    print ()

Скриншот результату роботи програми:





Те ж саме, але цикли не по індексу, а за значеннями списку:

# Приклад 2
A = [[1, 2, 3], [4, 5, 6]]
for row in A:
    for elem in row:
        print (elem, end = ' ')
    print()

Природно для виведення одного рядка можна скористатися методом join:

for row in A:
    print(' '.join(list(map(str, row))))

Використовуємо два вкладених циклу для підрахунку суми всіх чисел в списку:

# Приклад 3
A = [[1, 2, 3], [4, 5, 6]]
S = 0
for i in range(len(A)):
    for j in range(len(A[i])):
        S += A[i][j]
print("s=",S)

Або те ж саме з циклом не по індексу, а за значеннями рядків:

# Приклад 4
A = [[1, 2, 3], [4, 5, 6]]
S = 0
for row in A:
    for elem in row:
        S += elem
print("s=",S)

Створення вкладених списків
Нехай дано два числа: кількість рядків n і кількість стовпців m. Необхідно створити список розміром n × m, заповнений нулями.

Очевидне рішення виявляється невірним:

A = [[0] * m] * n

У цьому легко переконатися, якщо присвоїти елементу A [0] [0] значення 1, а потім вивести значення іншого елемента A [1] [0] - воно теж дорівнюватиме 1! Справа в тому, що [0] * m повертає Посилання на список з m нулів. Але подальше повторення цього елемента створює список з n елементів, які є посиланням на один і той же список (точно так само, як виконання операції B = A для списків не створює новий список), тому всі рядки результуючого списку насправді є однією і тієї ж рядком.

Таким чином, двовимірний список можна створювати за допомогою операції повторення одного рядка. Що ж робити?

Перший спосіб: спочатку створимо список з n елементів (для початку просто з n нулів). Потім зробимо кожен елемент списку посиланням на інший одновимірний список з m елементів:

A = [0]*n
for i in range(n):
    A[i] = [0]*m

Інший (але схожий) спосіб: створити порожній список, потім n раз додати в нього новий елемент, який є списком-рядком:

A = []
for i in range(n):
    A.append([0]*m)

Але ще простіше скористатися генератором: створити список з n елементів, кожен з яких буде списком, що складаються з m нулів:

A = [[0]*m for i in range(n)]

У цьому випадку кожен елемент створюється незалежно від інших (заново конструюється список [0] * m для заповнення чергового елемента списку), а не копіюються посилання на один і той же список.


Введення двовимірного масиву
Нехай програма отримує на вхід двовимірний масив, у вигляді n рядків, кожен з яких містить m чисел, розділених пробілами. Як їх рахувати? Наприклад, так:

A = []
for i in range(n):
     A.append(list(map(int, input().split())))

Або, без використання складних вкладених викликів функцій:

A = []
for i in range (n):
     row = input().split ()
     for i in range(len(row)):
         row[i] = int(row[i])
     A.append(row)

Можна зробити те ж саме і за допомогою генератора:

A = [list(map(int, input().split())) for i in range(n)]


Приклад обробки двовимірного масиву
Нехай дано квадратний масив з n рядків і n стовпців. Необхідно елементів, що знаходяться на головній діагоналі, що проходить з лівого верхнього кута в правий нижній (тобто тих елементів A [i] [j], для яких i == j) присвоїти значення 1, елементів, що знаходяться вище головної діагоналі - значення 0, елементів, що знаходяться нижче головної діагоналі - значення 2. тобто отримати такий масив (приклад для n == 4):

     1 0 0 0
  2 1 0 0
  2 2 1 0 
  2 2 2 1

Розглянемо кілька способів вирішення цього завдання. Елементи, які лежать вище головної діагоналі - це елементи A [i] [j], для яких i <j, а для елементів нижче головної діагоналі i> j. Таким чином, ми можемо порівнювати значення i і j і за ними визначати значення A [i] [j]. Отримуємо наступний алгоритм:

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

Даний алгоритм поганий, оскільки виконує одну або дві інструкції if для обробки кожного елемента. Якщо ми ускладнимо алгоритм, то ми зможемо обійтися взагалі без умовних інструкцій.

Спочатку заповнимо головну діагональ, для чого нам знадобиться один цикл:

for i in range(n):
    A[i][i] = 1

Потім заповнимо значенням 0 всі елементи вище головної діагоналі, для чого нам знадобиться в кожній з рядків з номером 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

Аналогічно присвоюємо значення 2 елементам

A[i][j] для j = 0, ..., i-1:

for i in range(n):
    for j in range(0, i):
        A[i][j] = 2

Можна також зовнішні цикли об'єднати в один і отримати ще одне, більш компактне рішення:

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

А ось таке рішення використовує операцію повторення списків для побудови чергового рядка списку. i-й рядок списку складається з i чисел 2, потім йде одне число 1, потім йде n-i-1 число 0:

for i in range(n):
    A[i] = [2]*i + [1] + [0]*(n - i - 1)

А можна замінити цикл на генератор:

A = [[2] * i + [1] + [0] * (n - i - 1) for i in range(n)]


Вкладені генератори двовимірних масивів

Для створення двовимірних масивів можна використовувати вкладені генератори, розмістивши генератор списку, що є рядком, всередині генератора для рядків. Наприклад, зробити список з 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)]

Задача.  Матриці
# Заповнити матрицю випадковими додатніми і від'ємними числами. Вивести її 
# на екран так, щоб замість від'ємних чисел стояли прочерки, тобто вивід повинен
# виглядати десь так:

# 2 - 5  6
# - 0  -  -
# 5 4  -  -
# Розв'язок:
from random import randint
N = 3
M = 4
a = []
for i in range(N):
    b = []
    for j in range(M):
        b.append(randint(-9, 9))
    a.append(b)

# Вивід матриці на екран
for row in a:
    for elem in row:
        print("%3d" % elem, end = ' ')
    print()
print()

for i in range(N):
    for j in range(M):
        if a[i][j] >= 0:
            print("%3d" % a[i][j], end='')
        else:
            print("%3s" % "-", end='')
    print()

Скриншот результату роботи програми:












Запитання
1. Чому для роботи з вкладеними списками використовують вкладені цикли ?


Завдання
1. Виконайте приклади 1,2,3,4 і відправте скриншоти результатів
2. Дано прямокутну таблицю(матрицю)

matrix = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]

а) Вивести елементи матриці на екран, використовуючи вкладені цикли

б) Обчислити суму елементів матриці

в) Транспонувати матрицю, т. б. рядки матриці зробити стовпчиками, а стовпчики - рядками.

3. Напишіть програму вибору робочих днів місяця по тижнях








Комментариев нет:

Отправить комментарий