Lesson 9
2次元リスト(配列)
1. ネストされたリスト:処理と印刷
実際には、タスクは長方形のデータテーブルを格納する必要があります。 [これ以上のことを言う!]このようなテーブルは、 行列または2次元配列と呼ばれます。 Pythonでは、どのテーブルもリストのリスト(各要素が順番にリストになるリスト)として表すことができます。たとえば、2行3列の数値表を作成し、それを使っていくつかの操作を行うプログラムを次に示します。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
。
2次元配列を処理するには、通常ネストされたループを使用します。最初のループは行番号を反復し、2番目のループは行の中の要素を通過します。たとえば、2次元数値リストを画面に1行ずつ表示し、その数字をスペースで区切る方法は次のとおりです。
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()
我々は既に、Pythonのforループ変数がrange()
に対してだけでなく、一般的にはどのシーケンスのすべての要素に対しても繰り返すことを説明しようとしました。 Pythonのシーケンスは、リストと文字列(およびまだ会っていない他のオブジェクト)です。あなたは、ループのこの便利な機能を使って、2次元配列を印刷することができますどのように見てfor
:
a = [[1, 2, 3, 4], [5, 6], [7, 8, 9]] for row in a: for elem in row: print(elem, end=' ') print()
当然、1行を出力するには、メソッドjoin()
使用します。
for row in a: print(' '.join([str(elem) for elem in row]))
これは、2つのネストされたループを使用して、2次元リスト内のすべての数の合計を計算する方法です。
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
数の2つの数が与えられたとします。あなたは、例えば、ゼロで埋められたサイズn
× m
リストを作成しなければならない。
明らかな解決策は間違っているようです:
a = [[0] * m] * n
これはa[0][0]
の値を5
、 a[1][0]
の値を印刷すると簡単に見られます。また、5と等しくなります。理由は[0] * m
はm
ゼロのリストへの参照を返しますが、リストは返しません。この要素の次の繰り返しは、同じリストをすべて参照するn
項目のリストを作成します(リストの操作b = a
は新しいリストを作成しません)ので、結果リスト内のすべての行は実際に同じです文字列。
ビジュアライザを使用して、リストのIDを追跡します。 2つのリストが同じID番号を持つ場合、実際には同じリストになります。
n = 3 m = 4 a = [[0] * m] * n a[0][0] = 5 print(a[1][0])
したがって、文字列を繰り返すだけでは2次元のリストを作成することはできません。何をすべきか?..
可能な方法: n
要素(例えばn
零点)のリストを作成し、各要素をm
要素の別の1次元リストへのリンクにすることができます。
n = 3 m = 4 a = [0] * n for i in range(n): a[i] = [0] * m
別の(しかし似たような)別の方法:空のリストを作成し、それに新しい要素をn
回append
(この要素は長さ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
回計算され、参照のコピーは発生しません。
3. 2次元配列をどのように入力しますか?
プログラムは、 n
行の形式で入力2次元配列を取ります。各行には、スペースで区切られた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. 2次元配列の処理:例
正方形の配列( n
行とn
列の配列)が与えられたとします。そして、対角線上の要素を0に設定するために、主対角線の要素を1(つまり、 i==j
要素a[i][j]
)に設定し、要素を設定する必要があるとしますその対角を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
である要素a[i][j]
と、主対角i>j
より下の要素に対する要素であることに注意してください。したがって、 a[i][j]
値を決定する値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]))
このアルゴリズムは、低速である:これは、2つのループを使用し、各ペアに対して(i,j)
一つまたは二つを実行if
の命令を。アルゴリズムを複雑にすると、条件文がなくてもアルゴリズムを実行できます。
最初に、メイン対角線を記入します。そのためには、1つのループが必要です。
for i in range(n): a[i][i] = 1
次に、メイン対角線の上にあるすべての要素をゼロで埋めます。これを行うには、番号i
行ごとに、 j
= i+1
、...、 n-1
a[i][j]
に値を割り当てる必要があります。これを行うには、ネストされたループが必要です。
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
番目の行は、 i
個の数字2
、1個の整数1
、それに続くni-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: 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. 2次元配列:ネストされたジェネレータ
ネストされたジェネレータを使用して、2次元配列を作成し、文字列であるリストのジェネレータをすべての文字列のジェネレータ内に配置することができます。ジェネレータ( n
要素のリストを作成します。各要素はm
ゼロのリストです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)]