下記コードで行列の内積を求めることができるのですが、
res_mat = [[sum([mat_1[i][j]*mat_2[j][k] for j in range(m)]) for k in range(l)] for i in range(n)]
が何をやっているかはわかる(内積の求め方は知っているので内積を求めていることが分かるということ)のですが、どうやって動作しているかがよくわかりません。自分では多分階層構造が良くわかっていないのだと思います。
それから、
sum([mat_1[i][j]*mat_2[j][k] for j in range(m)])
は
sum([mat_1[i][j]*mat_2[j][k]) for j in range(m)]
の間違いではないかと思っていたのですが、普通に動作するのでなぜこれで合っているのでしょうか?
n,m,l = [eval(x) for x in input().split()] mat_1 = [[eval(x) for x in input().split()] for _ in range(n)] mat_2 = [[eval(x) for x in input().split()] for _ in range(m)] res_mat = [[sum([mat_1[i][j]*mat_2[j][k] for j in range(m)]) for k in range(l)] for i in range(n)] print("---------------------------") [print(*res_mat[i]) for i in range(n)] [print(res_mat[i]) for i in range(n)]
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/04/04 12:48 編集
回答3件
0
ベストアンサー
こんにちは
ご質問を拝読して、
python
1res_mat = [[sum([mat_1[i][j]*mat_2[j][k] for j in range(m)]) for k in range(l)] for i in range(n)]
によって、2つの行列mat_1
とmat_2
との積行列を求めることができていることを、段階を踏んで示すことが、回答になり得るのではと思ったので、以下はその趣旨による回答になります。
ちなみに、いきなり上記の式が出てきても、これが行列の積として正しいのかは、ぱっと見ただけでは分からない、というのはごく自然だと思います。このような場合、どうしたらよいかというと、行列の積を求めるプログラムを、初めはリスト内包といった可読性が損なわれる(場合もある)記法を使わずに、forループを使ったり適宜、まとまった処理を関数に切り出すなどして愚直に書いていって、愚直でもロジックとしては納得がいくコードが完了したら、それを詰めていって冒頭のようなコードに近づけていけばよいかと思います。
そこで、この回答では、以下のようなコードを含む main.py から始めます。
python
1n, m, l = 3, 2, 4 2 3mat_1 = [ 4 [1, 2], 5 [2, 4], 6 [3, 6] 7] 8 9mat_2 = [ 10 [10, 20, 30, 40], 11 [20, 30, 40, 50] 12] 13 14res_mat = None 15 16print(res_mat)
上記は、3行2列の mat_1
と 2行4列の mat_2
の積 res_mat
を求めることを意図しています。なお、この回答で作成した main.py は、
に上げてあるので、お手元で試す際には、cloneするなりforkするなりご利用ください。上記の最初の main.py は、
で作成しています。以下、段階を踏んで、main.py を実装していきます。
(1) res_mat
は、 n行l列の行列になるので、とりあえずn×l のゼロ行列にしておきます。
python
1res_mat = [[0 for col_index in range(l)] for row_index in range(n)]
(2) 積の行列の成分を算出する関数 dot_product_element
を作ります。
関数の名前の意図は、2つの行列、mat_a
と mat_b
の積の行列の(ri, ci)
成分ということです。とりあえずゼロを返すようにしておきます。
python
1def dot_product_element(mat_a, mat_b, ri, ci): 2 return 0 3 4 5res_mat = [[dot_product_element(mat_1, mat_2, row_index, col_index) for col_index in range(l)] for row_index in range(n)]
(3) dot_product_element をforループで作ります。
dot_product_elementを実装しますが、初めから内包表記を使ったりせずに、愚直なコードで書きます。eskliaさんもご理解のとおり、行列AとBの積の (i, j)成分は、Aのi行の行ベクトルと、Bのj列の列ベクトルとの内積なので、内積の定義どおりに、各対応成分の積の合計を出すように for ループで書きます。合計値の変数s
を初期値0で用意して、ループするごとに加算していきます。
python
1def dot_product_element(mat_a, mat_b, ri, ci): 2 s = 0 3 for i in range(len(mat_a[ri])): 4 s += mat_a[ri][i] * mat_b[i][ci] 5 6 return s
ここまで出来ると、とりあえず積の行例を求めることができ、main.pyを実行すると、結果として以下が表示されます。
[[50, 80, 110, 140], [100, 160, 220, 280], [150, 240, 330, 420]]
この後は、コードを詰めていき、冒頭のコードに近づけていきます。
(4) 成分ごとの積の合計を求めるのに、sum() を使うように修正します。
python
1def dot_product_element(mat_a, mat_b, ri, ci): 2 products = [] 3 for i in range(len(mat_a[ri])): 4 products.append(mat_a[ri][i] * mat_b[i][ci]) 5 6 return sum(products)
(5) リスト内包表記を使用して、さらにdot_product_elementを詰めます。
上記(4) のコードを、リスト内包表記を使用して詰めると以下になります。
python
1def dot_product_element(mat_a, mat_b, ri, ci): 2 return sum([mat_a[ri][i] * mat_b[i][ci] for i in range(len(mat_a[ri]))])
(6) 関数 dot_product_elementで行っていることを、res_matを得る式に直接書いて、dot_product_elementを削除します。
python
1res_mat = [[sum([mat_1[row_index][i] * mat_2[i][col_index] for i in range(len(mat_1[row_index]))]) for col_index in range(l)] for row_index in range(n)]
(7) 式および変数名を修正します。
len(mat_1[row_index])
は mat_1
の列数なので、m
で置き換えることができます。
さらにいくつかの変数名を置き換えます。
-"i" to "j"
-"row_index" to "i"
-"col_index" to "k"
上記によって、ご質問にあるのと同じ
python
1res_mat = [[sum([mat_1[i][j] * mat_2[j][k] for j in range(m)]) for k in range(l)] for i in range(n)]
が得られました。
最後に得られた main.py を実行しても(3)の段階で得られた結果と同じ
[[50, 80, 110, 140], [100, 160, 220, 280], [150, 240, 330, 420]]
が得られますが、これがそもそも正解なのか?というのも確かめたいので、念のためnumpy の dot で検算します。
$ python Python 3.7.4 (default, Aug 13 2019, 15:17:50) [Clang 4.0.1 (tags/RELEASE_401/final)] :: Anaconda, Inc. on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import numpy as np >>> mat_1 = [ ... [1, 2], ... [2, 4], ... [3, 6] ... ] >>> >>> mat_2 = [ ... [10, 20, 30, 40], ... [20, 30, 40, 50] ... ] >>> np.dot(mat_1, mat_2) array([[ 50, 80, 110, 140], [100, 160, 220, 280], [150, 240, 330, 420]]) >>>
合ってました。
eskliaさんがリスト内包表記に慣れていないとすると、上記のステップのうち(4)から(5)にコードを詰めるところで、「どうしてこうなるのか?」という疑問が湧くかもしれませんが、for文でリストを作るのを、内包表記で書きかえるのは慣れの問題かなと思います。初めは for で書いて内包表記に書き直すということ意識的に心がけるとよいかもしれません。
以上、参考になれば幸いです。
投稿2020/04/05 09:24
総合スコア9058
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/04/07 10:34
2020/04/08 08:59
0
[[sum([mat_1[i][j]*mat_2[j][k] for j in range(m)]) for k in range(l)] for i in range(n)]
・sum([mat_1[i][j]*mat_2[j][k] for j in range(m)])
は内積を計算
・for k in range(l)
はmat_2の列方向の処理
・for i in range(n)
はmat_1の行方向の処理
だと思います。
sum([mat_1[i][j]*mat_2[j][k] for j in range(m)])
については質問の追記、「[(a00b00) + (a01b10)]」になるので「sum[(a00b00),(a01b10)]」で良いかと思います。
ネストした内包表記は難しいです。
変数の順番を入れ替えてみたり、[]の位置を変更してみたり色々してみると理解が深まるかもしれません。
投稿2020/04/04 15:56
総合スコア10744
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/04/05 06:03 編集
2020/04/05 06:46
0
理解の助けになるかもしれないことを列挙しておきます。
- 普通のforループで書き直しましょう。内包表記は目が慣れないときついです。
なお、基本的には以下のような方針で機械的に書き直せます。
python
1# 内包表記バージョン 2result = [x for x in range(10)] 3 4# 上をforループで書き直したもの 5result = [] 6for x in range(10): 7 result.append(x) 8 9# 多重の場合 10result = [[x * y for y in range(5)] for x in range(10)] 11 12# 上をforループで書き直したもの 13result = [] 14for x in range(10): 15 tmp = [] 16 for y in range(10): 17 tmp.append(x * y) 18 result.append(tmp)
- 内積は難しすぎるので、もう少し簡単な例をいろいろ試してみましょう。当分は内包表記ではなくループバージョンで。
の間違いではないかと思っていたのですが、普通に動作するのでなぜこれで合っているのでしょうか?
なぜ間違いではないかと思ったのでしょうか?
現状だと「はい」というだけの回答になります。より実り豊かなコミュニケーションを求めるのであれば、間違いではないかと思った理由を言語化して説明する責務が質問者さんにはあります。
投稿2020/04/04 14:27
編集2020/04/04 14:27総合スコア30935
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/04/05 05:57 編集
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。