Таблици с числа
Проблем
Три седмици, три списъка
Кафенето записва колко кафета е продало всеки работен ден. За една седмица списъкът от миналия урок е точно каквото трябва. За три седмици правиш три списъка:
week1 = [42, 38, 51, 47, 60]week2 = [45, 40, 49, 52, 63]week3 = [39, 44, 50, 48, 58]
tuesday = week1[1] + week2[1] + week3[1]print(f"Вторник: {tuesday}")Вторник: 122Сега управителят пита колко е продадено във всички вторници за последната година. Това са 52 седмици, значи 52 списъка и сбор с 52 члена. А цикъл не може да мине през week1, week2, week3: това са различни имена, не индекси.
Данните са таблица: седмиците са редове, дните са колони. Трябва ти списък, който е таблица.
Идеята
Списък от списъци: ред и колона
Таблицата в Python е списък от списъци: всеки елемент на външния списък е цял ред, а редът е обикновен списък с числа. В Java и C# това се казва двумерен масив, а често и матрица.
sales = [ [42, 38, 51, 47, 60], [45, 40, 49, 52, 63], [39, 44, 50, 48, 58],]print(sales[1][0])print(f"{len(sales)} седмици, {len(sales[0])} дни")453 седмици, 5 дни- Всеки вътрешен
[ … ]е един ред. Пишем ги един под друг, за да се вижда таблицата. sales[1][0]е редът с индекс 1 (втората седмица) и колоната с индекс 0 (понеделник). Първо е редът, после колоната. Броенето и тук е от 0.len(sales)е броят на редовете.len(sales[0])е дължината на първия ред, тоест броят на колоните.
sales[1] е цял ред, обикновен списък с 5 числа. Затова пишеш две отделни скоби [1][0]: първо взимаш реда, после числото в него.
sales = [ [42, 38, 51, 47, 60], [45, 40, 49, 52, 63], [39, 44, 50, 48, 58],]print(sales[1])print(sales)[45, 40, 49, 52, 63][[42, 38, 51, 47, 60], [45, 40, 49, 52, 63], [39, 44, 50, 48, 58]]Празна таблица 52 × 5, пълна с нули, правиш ред по ред: 52 пъти добавяш нов ред от 5 нули.
table = []for week in range(52): table.append([0] * 5)table[0][1] = 7print(len(table), len(table[0]), table[0][1], table[1][1])52 5 7 0По-точно, за любопитните
- Понеже редовете са отделни списъци, могат да са с различна дължина. Такава таблица се казва назъбена (jagged):
shifts = [ [8, 8], [6, 6, 6], [12],]print(len(shifts[1]), len(shifts[2]))3 1При нея len(sales[0]) не е дължината на всеки ред. Затова във вложения цикъл е по-сигурно да пишеш len(sales[row]): дължината на реда, през който минаваш в момента.
- Не прави празната таблица с
[[0] * 5] * 3. Така и трите места сочат един и същ ред, както двете имена на един списък от миналия урок. Промяна в единия ред се вижда във всички:
table = [[0] * 5] * 3table[0][1] = 7print(table)[[0, 7, 0, 0, 0], [0, 7, 0, 0, 0], [0, 7, 0, 0, 0]]Виж го
Цялата таблица на екрана
За да минеш през всяка клетка, ти трябват два цикъла един в друг. Вложените цикли вече ги знаеш от звездичките: външният избира реда, вътрешният минава през колоните на този ред.
for week in range(len(sales)): for day in range(len(sales[0])): print(f"{sales[week][day]:5}", end="") print() 42 38 51 47 60 45 40 49 52 63 39 44 50 48 58Външният цикъл се завърта 3 пъти, по веднъж за всяка седмица. При всяко негово завъртане вътрешният се завърта 5 пъти. Общо 15 клетки, в реда, в който четеш таблица: отляво надясно, отгоре надолу.
:5 във f-низа печата числото в поле от 5 знака, подравнено вдясно. Така колоните застават една под друга. end="" държи числата на един ред, а празното print() след всеки ред минава на нов.
Пробвай
Коя клетка?
В дневника всеки ред е ученик, а всяка колона е предмет.
Какво ще отпечата програмата?
grades = [ [5, 3, 4, 6], [3, 4, 6, 5], [6, 2, 5, 4],]print(grades[1][2])Първият индекс е редът: 1 е вторият ученик, [3, 4, 6, 5]. Вторият индекс е колоната: 2 е третият предмет. Оценката е 6. Числото 2 излиза, ако объркаш реда с колоната, а 3, ако броиш от 1.
Идеята
Сума по редове и по колони
Колко кафета са продадени всяка седмица? Това е сумата на всеки ред. Сумата започва от 0 за всеки нов ред, затова total е вътре във външния цикъл:
for week in range(len(sales)): total = 0 for day in range(len(sales[0])): total += sales[week][day] print(f"Седмица {week + 1}: {total}")Седмица 1: 238Седмица 2: 249Седмица 3: 239sales = [ [42, 38, 51, 47, 60], [45, 40, 49, 52, 63], [39, 44, 50, 48, 58],]for week in range(len(sales)): total = 0 for day in range(len(sales[0])): total += sales[week][day] print(f"Седмица {week + 1}: {total}")Стъпка по стъпка, с думи
- Таблицата: 3 седмици (редове) по 5 дни (колони).
- week = 0: total започва отначало, от 0.
- Вътрешният цикъл минава през дните: 42 + 38 + 51 + 47 + 60 = 238.
- Отпечатва „Седмица 1: 238“.
- week = 1: total започва отначало, от 0.
- Вътрешният цикъл минава през дните: 45 + 40 + 49 + 52 + 63 = 249.
- Отпечатва „Седмица 2: 249“.
- week = 2: total започва отначало, от 0.
- Вътрешният цикъл минава през дните: 39 + 44 + 50 + 48 + 58 = 239.
- Отпечатва „Седмица 3: 239“.
Колко е продадено в понеделник, във вторник и т.н.? Това е сумата на всяка колона. Таблицата е същата, сменя се само редът на циклите: отвън е денят, отвътре са седмиците. Ще го допишеш в следващата карта.
Кой цикъл е отвън, решава какво сумираш. Индексите в sales[week][day] не се местят: първо е винаги редът.
Пробвай
Сума по дни
Външният цикъл минава през дните, вътрешният през седмиците. Какво трябва да пише на празното място?
days = ["Пн", "Вт", "Ср", "Чт", "Пт"]
for day in range(len(sales[0])):
total = 0
for week in range(len(sales)):
total += sales[][day]
print(f"{days[day]}: {total}")week. Редът е седмицата, затова на първо място е week. Денят стои на място, докато вътрешният цикъл минава през трите седмици. Във вторник излиза 38 + 40 + 44 = 122, същото като в първата карта, но без 52 имена.
Пробвай
Намери грешката
Залата има 3 реда по 5 места: 1 е заето, 0 е свободно. Свободните места са 7, а програмата казва 5. На кой ред е грешката?
Избери реда с грешката.
Вътрешният цикъл е за местата на реда, а броят им е len(hall[0]) (5). len(hall) е броят на редовете (3), затова последните две места на всеки ред не се проверяват. Python не вижда такава грешка: и двете са числа, а програмата върви.
Идеята
Резервация: хората броят от 1
Зрителят иска ред 3, място 2. За него първият ред е ред 1. За списъка той е ред 0. Затова от числата, които въвежда човек, вадиш 1, преди да ги ползваш като индекси:
row = int(input())seat = int(input())if hall[row - 1][seat - 1] == 1: print("Мястото е заето.")else: hall[row - 1][seat - 1] = 1 print(f"Запазено: ред {row}, място {seat}")32Запазено: ред 3, място 212Мястото е заето.hall е залата от предната карта. Клетката се променя като обикновена променлива: hall[row - 1][seat - 1] = 1.
По-точно, за любопитните
- Ако някой поиска ред 7, индексът 6 е извън таблицата и програмата спира с
IndexError, също като при обикновен списък. По-коварно е, ако поиска ред 0: индексът е -1, а в Python това е последният ред. Грешка няма, а се запазва грешното място:
hall = [ [1, 1, 0, 0, 1], [0, 1, 1, 1, 0], [0, 0, 0, 1, 1],]try: print(hall[7 - 1][2 - 1])except IndexError as error: print(f"IndexError: {error}")print(hall[0 - 1])IndexError: list index out of range[0, 0, 0, 1, 1]Затова провери числата, преди да ги ползваш: 1 <= row <= len(hall), и същото за мястото.
- Тук всяко число е на отделен ред. Как да прочетеш цял ред числа наведнъж (
0 1 1 0), ще видиш в следващия урок за текста.
Упражнение
Сметки по таблица
Реши задачите в редактора си. Таблиците напиши направо в кода.
1. Диагонали и редове. За таблицата 2 × 2 по-долу намери сумата по главния диагонал (отгоре вляво надолу вдясно), по другия диагонал и сумите на първия и на втория ред. На главния диагонал индексът на реда и на колоната са равни. На другия диагонал колоната е n - 1 - ред.
1 23 4Главен диагонал: 5Втори диагонал: 5Първи ред: 3Втори ред: 72. Обърни таблицата. Продажбите са записани по седмици (редове) и дни (колони). Счетоводството ги иска по дни и седмици. Направи нова таблица, в която редовете стават колони. Това се казва транспониране. Новата таблица е с размери колони × редове, а клетката [r][c] отива на [c][r].
42 38 51 45 40 49 42 45 38 40 51 49Упражнение
Лабиринти и съкровище
3. Колко пътя има? Лабиринтът е таблица: 0 е проход, 1 е стена. Тръгваш от горния ляв ъгъл и стигаш до долния десен, като се движиш само надясно или надолу. Колко различни пътя има?
0 0 0 00 1 0 00 0 0 11 0 0 0Пътища: 3Подсказка
Направи втора таблица ways със същия размер. В нея пиши по колко начина се стига до всяка клетка. До началото се стига по 1 начин. До стена по 0. До всяка друга клетка се стига или отгоре, или отляво, затова броят е сборът от клетката над нея и клетката вляво от нея (ако ги има). Попълни таблицата ред по ред, отгоре надолу. Отговорът е в последната клетка.
4. Най-краткият път. Трудна задача. Сега може да се движиш и в четирите посоки: нагоре, надолу, наляво и надясно. С колко стъпки най-малко се стига от горния ляв до долния десен ъгъл? Ако няма път, отпечатай „Няма път“.
0 0 0 0 01 1 1 1 00 0 0 0 00 1 1 1 10 0 0 0 0Най-кратък път: 16 стъпкиПодсказка
Пусни „вълна“ от началото. Направи таблица dist, пълна с -1 (още не сме стигнали дотам), и сложи 0 в началото. После повтаряй: намери всички клетки с dist равно на текущата стъпка и на свободните им съседи, до които още не сме стигнали, запиши стъпка + 1. Вълната расте с една стъпка на всяко завъртане. Спри, когато стигне целта или когато нищо не се е променило (тогава няма път). Четирите посоки са удобни като два списъка: dr = [-1, 1, 0, 0] и dc = [0, 0, -1, 1]. Съседът в посока k е на ред r + dr[k] и колона c + dc[k].
Още задачи
- Намери съкровището. Игра на карта, която е таблица: 0 е път, 1 е препятствие, 2 е съкровището. Играчът започва в горния ляв ъгъл. На всеки ход отпечатай картата (
Pза играча,#за препятствие,.за път) и прочети команда като число: 8 нагоре, 2 надолу, 4 наляво, 6 надясно (както на цифровата клавиатура). Ако ходът води извън картата или в препятствие, играчът остава на място. Когато стъпи на съкровището, отпечатай „Намери съкровището с … хода!“. - Ред с най-много продажби. За таблицата
salesнамери коя седмица е била най-силна и кой ден от седмицата е най-силен средно. - Свободни места подред. За залата
hallнамери ред, в който има поне 3 свободни места едно до друго, и отпечатай номера на реда и на първото място.
Тест