Цикл for для двумерного массива

1. Обработка и вывод вложенных списков Часто в задачах приходится хранить прямоугольные таблицы с данными. Такие таблицы называются матрицами или двумерными массивами. В языке программирования Питон таблицу можно представить в виде списка строк, каждый элемент которого является в свою очередь списком, например, чисел. Например, приведём программу, в которой создаётся числовая таблица из двух строк и трех столбцов, с которой производятся различные действия.
a = [[1, 2, 3], [4, 5, 6]] (a[0]) (a[1]) b = a[0] (b) (a[0][2]) a[0][1] = 7 (a) (b) b[2] = 9 (a[0]) (b)
Здесь первая строка списка 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])): (a[i][j], end=’ ‘) ()
Однажды мы уже пытались объяснить, что переменная цикла for в Питоне может перебирать не только диапазон, создаваемый с помощью функции range(), но и вообще перебирать любые элементы любой последовательности. Последовательностями в Питоне являются списки, строки, а также некоторые другие объекты, с которыми мы пока не встречались. Продемонстрируем, как выводить двумерный массив, используя это удобное свойство цикла for:
a = [[1, 2, 3, 4], [5, 6], [7, 8, 9]] for row in a: for elem in row: (elem, end=’ ‘) ()
Естественно, для вывода одной строки можно воспользоваться методом join():
for row in a: (‘ ‘.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] (s)
Или то же самое с циклом не по индексу, а по значениям строк:
a = [[1, 2, 3, 4], [5, 6], [7, 8, 9]] s = 0 for row in a: for elem in row: s += elem (s)
2. Создание вложенных списков
Пусть даны два числа: количество строк n и количество столбцов m. Необходимо создать список размером n×m, заполненный нулями.
Очевидное решение оказывается неверным:
В этом легко убедиться, если присвоить элементу a[0][0] значение 5, а потом вывести значение другого элемента a[1][0] – оно тоже будет равно 5. Дело в том, что [0] * m возвращает ccылку на список из m нулей. Но последующее повторение этого элемента создает список из n элементов, которые являются ссылкой на один и тот же список (точно так же, как выполнение операции b = a для списков не создает новый список), поэтому все строки результирующего списка на самом деле являются одной и той же строкой.
В визуализаторе обратите внимание на номер id у списков. Если у двух списков id совпадает, то это на самом деле один и тот же список в памяти.
n = 3 m = 4 a = [[0] * m] * n a[0][0] = 5 (a[1][0])
Таким образом, двумерный список нельзя создавать при помощи операции повторения одной строки. Что же делать?
Первый способ: сначала создадим список из n элементов (для начала просто из n нулей). Затем сделаем каждый элемент списка ссылкой на другой одномерный список из m элементов:
n = 3 m = 4 a = [0] * n for i in range(n): a[i] = [0] * m
Другой (но похожий) способ: создать пустой список, потом n раз добавить в него новый элемент, являющийся списком-строкой:
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 для заполнения очередного элемента списка), а не копируются ссылки на один и тот же список.
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 столбцов. Необходимо элементам, находящимся на главной диагонали, проходящей из левого верхнего угла в правый нижний (то есть тем элементам 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]. Получаем следующий алгоритм:
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: (‘ ‘.join([str(elem) for elem in row]))
Данный алгоритм плох, поскольку выполняет одну или две инструкции 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
Можно также внешние циклы объединить в один и получить еще одно, более компактное решение:
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: (‘ ‘.join([str(elem) for elem in row]))
А вот такое решение использует операцию повторения списков для построения очередной строки списка. i-я строка списка состоит из i чисел 2, затем идет одно число 1, затем идет n-i-1 число 0:
n = 4 a = [0] * n for i in range(n): a[i] = [2] * i + [1] + [0] * (n – i – 1) for row in a: (‘ ‘.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: (‘ ‘.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)]
Задачи на rmatics.mccme.ru
Источник
Добрый день друзья.
Сегодня затронем новую тему. Двумерные массивы. Или их еще называют матрицы.
Прочитайте улучшенную версию этого урока “Двумерные массивы”.
В новой версии:
- Ещё более доступное объяснение
- Дополнительные материалы
- 10 задач на программирование с автоматической проверкой решения
Что такое двумерный массив?
Двумерный массив это прямоугольная таблица с определенным количеством строк и столбиков. Каждая строка и каждый столбик имеют свой порядковый номер. На рисунке изображен двумерный массив, который имеет 6 строк и 7 столбцов.
Рис.1. Двумерный массив. Общий вид. |
Обратите внимание, что столбцы и строки нумеруются, начиная с нуля, как и в одномерных массивах.
Сразу же покажу, как объявить двумерный массив. В сущности, ничего нового. Синтаксис прост и очень похож на объявление обычного массива и любой другой переменной. Сначала указываем тип данных, затем имя массива, а после в квадратных скобках его размерность, то есть количество строк и количество столбиков. На рисунке ниже мы объявили двумерный массив размерностью 6 на 7.
Рис.2 Двумерный массив, объявление. |
Важно помнить, что первая строка и первый столбик имеют индекс нуль, поэтому последняя строка будет иметь номер не шесть, а пять, и аналогично последний столбец будет иметь номер шесть, а не семь.
Кстати, нельзя путать местами строки и столбики. Сначала записывается количество строк, потом количество столбиков.
Следующий естественный вопрос:
Как работать с двумерным массивом?
Как и другие переменные, двумерные массивы мы можем инициализировать при их объявлении. Для этого нам нужно указать все элементы массива. Делается это следующим образом.
Листинг 19.1
int arr [2][4] = {{1,2,4,29},{3,4,6,1}};
Записанный выше код создает массив с двумя строчками и четырьмя столбцами, и присваивает каждому элементу определенные значения. Получится вот такой массив.
Рис.3. Двумерный массив инициализированный при объявлении |
Кроме того, мы можем задать только некоторые элементы массива, тогда остальные будут заполнены нулями. Например:
Листинг 19.2
int arr [2][4] = {{1,2,4},{3,4}};
При таком объявлении мы получим массив, который изображен на следующем рисунке.
Рис.4. Двумерный массив, инициализированный не полностью. |
Понятно, что если у нас будет большой массив, то мы замучаемся так все записывать. Да и редко такое бывает нужно.
Как работать с отдельным элементом массива.
Понятно, что любой элемент задается номер строки и столбца, на пересечении которых он расположен. Посмотрите на следующий рисунок. На нем мы выбрали элемент, который находится во второй строке и третьем столбике (в естественной нумерации).
Рис.5. Обращение к элементу двумерного массива. |
Обращение к конкретному элементу происходит так же, как и в одномерном массиве. Сначала пишут имя массива, а затем, в квадратных скобках номер строки и номер столбика. Опять напоминаю, сначала идет номер строки, а потом номер столбика. Не путайте это. А так же, помните, что нумерация идет не с единицы, а с нуля. Это очень частая ошибка, всегда следите за этим.
Помните, когда мы хотели пройтись по всему одномерному массиву, мы использовали цикл for, который изменял наш индекс. Так как в двумерном массиве у нас два индекса ( один для строки и один для столбца), то для того, чтобы пройтись по всему массиву нам потребуется два вложенных цикла for. Разберите следующий пример.
Листинг19.3
#include <stdio.h>
int main (){
int arr [2][4] = {{1,2,4},{3,4}};
for (int i=0; i<2; i++){ // 1 цикл
for (int j=0; j<4; j++) // 2 цикл
f(“%dt”,arr[i][j]);
f(“n”);
}
return 0;
}
Данная программа, выводит последовательно все элементы массива. Результат её работы, представлен на следующем рисунке.
Рис.6. Двумерный массив. Поэлементный вывод на экран. |
Как работает вложенный цикл, вам уже должно быть понятно.
Сначала переменной i присваивается значение нуль, проверяется условие 0<2, и так как оно выполняется, программа начинает выполнять тело первого цикла. В теле первого цикла программа опять попадает в цикл, теперь уже второй. Переменной j присваивается значение 0 и проверяется условие 0<4. Оно истинно, поэтому выполняется тело второго цикла. Оно состоит из одной инструкции вывода на экран элемента arr[i][j]. Так как на данном шаге у нас i=0 j=0, то выводится значение элемент из нулевой строки и нулевого столбика. В нашем примере это элемент число 1. Тело второго цикла закончилось, происходит увеличение j на единицу j=1.
Проверка условия 1<4. Выполнение тела второго цикла: вывод на экран элемента arr[0][1] в нашем случае это 2. И так далее …
Используя вложенные циклы, мы можем и заполнять двумерные массивы.
Зачем нужны двумерные массивы?
Двумерные массивы используются для хранения данных одинакового типа и одинаковой структуры. Приведу пример. В одном из практических заданий к прошлым урокам, необходимо было составить программу, которая хранила оценки ученика по разным предметам. Для их хранения в памяти компьютера мы использовали несколько одномерных массивов. Это было не очень удобно. Для этих же целей, мы могли использовать двумерный массив. Например, нам нужно хранить оценки ученика по русскому, математике, физике и информатике, за целый месяц. Для этого мы создадим массив с 4 строками и 30 столбиками (или наоборот, 30 строк и 4 столбика, кому как больше нравится), и в каждую отдельную строчку будем записывать оценки по какому либо предмету. Это гораздо удобнее и практичнее.
Например, рассмотрим такую задачку. Пусть нам нужно было бы найти среднее арифметическое всех оценок по всем предметам. Если бы у нас было четыре одномерных массива, то нам пришлось бы писать четыре цикла, в каждом из них считать среднее арифметическое оценок по одному предмету, потом находить среднее арифметическое этих четырех чисел. Если оценки у нас хранятся в двумерном массиве, нам достаточно будет просто пройтись по всем элементам массива, используя два вложенных цикла for, и найти среднее арифметическое.
Но это еще не всё. Представьте, что вы хотите добавить один предмет и снова посчитать среднюю арифметическую оценку для всех предметов. Сколько мороки будет, если вы храните оценки в одномерных массивах. Вам придется кучу изменений вносить, дописывать еще один цикл (а вдруг добавилось 7 предметов?). Потом еще среднее искать между этими цифрами.
Другое дело если вы храните оценки в двумерном массиве. Всего лишь изменить условие в одном из циклов. Поменять одно число, представляете как удобно?
Напишите в комментариях пожалуйста, понятен ли вам этот пример, или лучше его подробно расписать?
Если этот материал кажется вам полезным, расскажите о нем друзьям используя кнопки основных социальных сетей, расположенные ниже.
Практическое задание.
Напишите программу, работающую следующим образом. Создайте массив 10 на 10. Заполните его нулями. Считайте два произвольных целых числа с клавиатуры, меньших либо равных 10. Первое число количество строк, второе – количество столбцов. Напишите функцию, которая заполняет массив по спирали и выводит его на экран. Т.е. если бы мы ввели 6 и 7, то получили бы следующий массив.
При этом табличка приблизительно должна быть выровнена по центру окна вывода.
Готовое решение пользователя с ником “Дмитрий”. За проявленное упорство и трудолюбие, и как первый выполнивший правильно практическое задание, Дмитрий награждается печенькой:
Источник
Двумерный массив в Паскале трактуется как одномерный массив, тип элементов которого также является массивом (массив массивов). Положение элементов в двумерных массивах Паскаля описывается двумя индексами. Их можно представить в виде прямоугольной таблицы или матрицы.
Рассмотрим двумерный массив Паскаля размерностью 3*3, то есть в ней будет три строки, а в каждой строке по три элемента:
Каждый элемент имеет свой номер, как у одномерных массивов, но сейчас номер уже состоит из двух чисел – номера строки, в которой находится элемент, и номера столбца. Таким образом, номер элемента определяется пересечением строки и столбца. Например, a21 – это элемент, стоящий во второй строке и в первом столбце.
Описание двумерного массива
Пример описания двумерного массива Паскаля
var a: array [1..n, 1..m] of <тип элементов>;
В примере объявлен двумерный массив Паскаля a, состоящий из n строк, в каждой из которых m столбцов. При этом к каждой i -й строке можно обращаться m[i], а каждому j -му элементу внутри i -й строки – m[i,j].
Обращение к элементам двумерного массива имеет вид: a[i,j]. Обращение происходит к элементу, расположенному в i -й строке и j -м столбце.
Ввод двумерного массива
Для последовательного ввода элементов одномерного массива применялись циклы, в которых изменяли значение индекса с 1-го до последнего. Положение элемента в двумерном массиве Паскаля определяется двумя индексами: номером строки и номером столбца. Это значит, что необходимо последовательно изменять номер строки с 1-й до последней и в каждой строке перебирать элементы столбцов с 1-го до последнего. Для этого потребуются два цикла for или while, причем один из них будет вложен в другой.
Ввод двумерного массива с клавиатуры:
Блок-схема: | Программный код: const n=5; m=10; var a: array [1..n, 1..m] of integer; i, j: integer; { индексы массива } begin for i :=1 to n do {цикл для перебора всех строк} for j :=1 to m do {перебор всех элементов строки по столбцам} readln ( a [ i , j ]); {ввод с клавиатуры элемента, стоящего в i -й строке и j -м столбце} end. |
Вывод двумерного массива Паскаля
Вывод элементов двумерного массива Паскаля также осуществляется последовательно, необходимо напечатать элементы каждой строки и каждого столбца.
Вывод двумерного массива в строку:
Блок-схема: | Программный код: Var A: array [1..10, 1..10] of integer; i, j: integer ; {переменные i,j вводятся как индекс массива} Begin For i :=1 to 10 do For j :=1 to 10 do Write ( a [i, j],’ ‘); {вывод массива осуществляется в строку, после каждого элемента печатается пробел} readln; end. |
Вывод можно осуществить и в столбик с указанием соответствующего индекса. Но в таком случае нужно учитывать, что при большой размерности массива все элементы могут не поместиться на экране и будет происходить скроллинг, т.е. при заполнении всех строк экрана будет печататься очередной элемент, а верхний смещаться за пределы экрана.
Вывод двумерного массива в столбик:
Var
A: array [1..10, 1..10] of integer;
i, j : integer ; {переменная i,j вводятся как индекс массива}
Begin
For i:=1 to 10 do
For j :=1 to 10 do
Writeln (‘a[‘, i,j, ‘]=’, a[i,j]); { вывод элементов массива в столбик }
readln;
end.
Вывод на экран элементов будет в следующем виде:
a [1,1]=2
a [2,3]=4
a [3,4]=1 и т.д.
Вывод двумерного массива таблицей:
for i :=1 to n do {цикл для перебора всех строк}
begin
for j:=1 to m do {перебор всех элементов строки по столбцам}
write(a[i,j]:k); {печать элементов, стоящих в i -й строке матрицы в одной экранной строке, при этом для вывода каждого элемента отводится 4 позиции}
writeln; {прежде, чем сменить номер строки в матрице, нужно перевести курсор на начало новой экранной строки}
end;
k – обычно выбирают на 1-2-3 символа больше чем само число, например: число 123 => k можно выбрать от 4 и более, если меньше, то числа будут сливаться или вообще не показываться(если n=0)
Источник