Help us understand the problem. What is going on with this article?

いろいろな書き方で親しむ線形代数

はじめに

行列の演算は同じことでもいろいろな書き方ができます。いろいろな書き方ができるということは、いろいろな解釈ができるということで、パズルみたいで面白いです。

ベクトル

ベクトルは値を一列に並べたものです。縦ベクトルがよく用いられます。
長さMのベクトルは次のように、M個の値を並べたものです。

\begin{align*}
\boldsymbol{x} =
 \left(
  \begin{array}{c}
   x_1 \\
   \vdots \\
   x_M
  \end{array}
 \right)
\end{align*}

Tは転置を表す記号で、ベクトルの転置は

\begin{align*}
\boldsymbol{x}^\top = (x_1, \cdots, x_M)
\end{align*}

のように、縦方向に並んでいたものを横方向に、あるいは逆に横方向に並んでいたものを縦方向に並べ替えます。

行列のベクトル表現

M×N(M行N列)の行列は

\begin{align*}
\boldsymbol{X} &=
 \left(
  \begin{array}{ccc}
   x_{11} & \cdots & x_{1N} \\
   \vdots & \ddots & \vdots \\
   x_{M1} & \cdots & x_{MN}
  \end{array}
 \right)
\end{align*}

です。行は横方向、列は縦方向です。この行列を転置すると

\begin{align*}
\boldsymbol{X}^\top &=
 \left(
  \begin{array}{ccc}
   x_{11} & \cdots & x_{M1} \\
   \vdots & \ddots & \vdots \\
   x_{1N} & \cdots & x_{MN}
  \end{array}
 \right)
\end{align*}

です。

長さMのベクトルはM×1行列です。行列Xは、行列Xのi行目の要素を持つベクトルを用いて

\begin{align*}
\boldsymbol{X} =
 \left(
  \begin{array}{c}
   \boldsymbol{x}_1^\top \\
   \vdots \\
   \boldsymbol{x}_M^\top
  \end{array}
 \right), \qquad
\boldsymbol{x}_i =
 \left(
  \begin{array}{c}
   x_{i1} \\
   \vdots \\
   x_{iN}
  \end{array}
 \right) \\
\end{align*}

と行ベクトルで書けます。この行ベクトル表現を用いると、転置は

\begin{align*}
\boldsymbol{X}^\top = (\boldsymbol{x}_1, \cdots, \boldsymbol{x}_M)
\end{align*}

と書けます。縦に並んでいたベクトルが横並びになり、さらに要素のベクトル一つ一つも転置されています。

同様に、j列目の要素を縦に持つベクトルを用いて

\begin{align*}
\boldsymbol{X} = \left( \boldsymbol{x}_{\cdot 1}, \cdots, \boldsymbol{x}_{\cdot N} \right), \qquad
\boldsymbol{x}_{\cdot j} =
 \left(
  \begin{array}{c}
   x_{1j} \\
   \vdots \\
   x_{Mj}
  \end{array}
 \right) \\
\end{align*}

と列ベクトルで書くこともできます。同じ行列をいろいろな書き方ができるということは、状況に応じて都合の良い表現を利用できるということです。

行列の積

行列の積のベクトル表現1

M×K行列XとK×N行列Yの積は

\begin{align*}
\boldsymbol{X} \boldsymbol{Y}
 =
 \left(
  \begin{array}{ccc}
   \boldsymbol{x}_1^\top \boldsymbol{y}_{\cdot 1} & \cdots & \boldsymbol{x}_1^\top \boldsymbol{y}_{\cdot N} \\
   \vdots & \ddots & \vdots \\
   \boldsymbol{x}_M^\top \boldsymbol{y}_{\cdot 1} & \cdots & \boldsymbol{x}_M^\top \boldsymbol{y}_{\cdot N}
  \end{array}
 \right)
\end{align*}

です。言葉で言えば「i行j列要素は、Xのi行ベクトルと、Yのj列ベクトルの内積」です。Xを行ベクトル表現、Yを列ベクトル表現してみると

\boldsymbol{X} \boldsymbol{Y}
 =  \left(
  \begin{array}{c}
   \boldsymbol{x}_1^\top \\
   \vdots \\
   \boldsymbol{x}_M^\top
  \end{array}
 \right) \left( \boldsymbol{y}_{\cdot 1}, \cdots, \boldsymbol{y}_{\cdot N} \right)

です。先ほどの積と見比べると「積のi行j列要素は、Xのi行ベクトルと、Yのj列ベクトルの内積」になっています。内積なので、Xの行ベクトルの長さ(=Xの列数)と、Yの列ベクトルの長さ(=Yの行数)は等しくないといけません。また、M×K行列とK×N行列の積は、M×N行列になっています。Kの軸は内積の和の演算でまとめられて、行列の長さとしては残っていません。M×KとK×Nを並べて「真ん中が消える」と覚えると良いかもしれません。

ベクトルを一塊の要素と見ると、この行列は「ベクトルを要素とするベクトル」とも言えます。ベクトルを要素と見ると、Xは長さMの縦ベクトル(M×1行列)、Yは長さNの横ベクトル(1×N行列)です。ベクトルを要素と見ても、M×1行列と1×N行列の積でM×N行列(A)になります。
今度は要素に着目してみます。要素同士の内積を取る部分

\boldsymbol{x}_i^\top \boldsymbol{y}_{\cdot j}

は、長さKの横ベクトル(1×K行列)と長さKの縦ベクトル(K×1行列)の積です。1×K行列とK×1行列の積で1×1行列(スカラー)(B)となります。(A)と(B)を合わせると、1×1行列(スカラー)をM×N並べた行列、すなわちM×N行列が出来上がることになります。このように、行列演算は行列をブロックに分割しても、「ブロックを要素とする行列同士の演算としても、ブロック同士の演算としても、整合する」ようになっています。

2×2行列XとYの積の例です。

\begin{align*}
\boldsymbol{X} &=
 \left(
  \begin{array}{cc}
   1 & 2 \\
   3 & 4
  \end{array}
 \right) ,\quad
\boldsymbol{Y} =
 \left(
  \begin{array}{cc}
   5 & 7 \\
   6 & 8
  \end{array}
 \right) \\
\boldsymbol{X} \boldsymbol{Y} &=
 \left(
  \begin{array}{cc}
   1 \times 5 + 2 \times 6 & 1 \times 7 + 2 \times 8 \\
   3 \times 5 + 4 \times 6 & 3 \times 7 + 4 \times 8
  \end{array}
 \right) =
 \left(
  \begin{array}{cc}
   17 & 23 \\
   39 & 53
  \end{array}
 \right)
\end{align*}

この例でベクトル表現で見ると

\begin{align*}
\boldsymbol{X} &=
 \left(
  \begin{array}{c}
   (1, 2) \\
   (3, 4)
  \end{array}
 \right) ,\quad
\boldsymbol{Y} =
 \left(
  \begin{array}{cc}
   \left(
    \begin{array}{c}
     5 \\
     6
    \end{array}
   \right),
   \left(
    \begin{array}{c}
     7 \\
     8
    \end{array}
   \right)
  \end{array}
 \right) \\
\boldsymbol{X} \boldsymbol{Y} &=
 \left(
  \begin{array}{cc}
   (1, 2)
   \left(
    \begin{array}{c}
     5 \\
     6
    \end{array}
   \right) &
   (1, 2)
   \left(
    \begin{array}{c}
     7 \\
     8
    \end{array}
   \right) \\
   (3, 4)
   \left(
    \begin{array}{c}
     5 \\
     6
    \end{array}
   \right) &
   (3, 4)
   \left(
    \begin{array}{c}
     7 \\
     8
    \end{array}
   \right)
  \end{array}
 \right) =
 \left(
  \begin{array}{cc}
   17 & 23 \\
   39 & 53
  \end{array}
 \right)
\end{align*}

となります。

行列の積のベクトル表現2

今度はXを列ベクトル表現、Yを行ベクトル表現してみます。

\boldsymbol{X} \boldsymbol{Y}
 =  \left( \boldsymbol{x}_{\cdot 1}, \cdots, \boldsymbol{x}_{\cdot K} \right)
 \left(
  \begin{array}{c}
   \boldsymbol{y}_1^\top \\
   \vdots \\
   \boldsymbol{y}_K^\top
  \end{array}
 \right)

行列の積の「i行j列要素は、Xのi行ベクトルと、Yのj列ベクトルの内積」でした。いまベクトルを要素と見るとXは1行K列、YはK行1列なので、これらの積は「Xの第1行とYの第1列の内積」を要素に持つ1行1列の行列(C)になります。内積なので、k番目同士の積の、k=1,..,Kについての和を計算します。

\boldsymbol{X} \boldsymbol{Y}
 = \sum_{k=1}^K \boldsymbol{x}_{\cdot k} \boldsymbol{y}_k^\top

長さMの縦ベクトル(M×1行列)と長さNの横ベクトル(1×N行列)の積はM×N行列(D)です。(C)と(D)を合わせると、M×N行列を1×1個並べた行列、すなわちM×N行列になっています。

「行列の積のベクトル表現1」は行列の積の要素をベクトル同士の内積で表現していましたが、「行列の積のベクトル表現2」は行列を縦ベクトルと横ベクトルの積の和で表現(構成)しています。固有値分解/特異値分解や統計の共分散行列など様々なところでこちらの表現が出てきます。

行列の積のベクトル表現3

今度はYだけ、列ベクトル表現してみます。

\begin{align*}
\boldsymbol{X} \boldsymbol{Y}
 = \boldsymbol{X} (\boldsymbol{y}_{\cdot 1}, \cdots, \boldsymbol{y}_{\cdot N})
 = (\boldsymbol{X} \boldsymbol{y}_{\cdot 1}, \cdots, \boldsymbol{X} \boldsymbol{y}_{\cdot N})
\end{align*}

と書けます。積の第j列は、行列XとYのj列ベクトルの積になっています。

行列とベクトルの積

今度はM×K行列Xと長さKのベクトルwの積です。

\begin{align*}
\boldsymbol{X} \boldsymbol{w}
 = (\boldsymbol{x}_{\cdot 1} \cdots \boldsymbol{x}_{\cdot K})
 \left(
  \begin{array}{l}
   w_1 \\
   \vdots \\
   w_K
  \end{array}
 \right)
 = \sum_{k=1}^K w_k \boldsymbol{x}_{\cdot k}
\end{align*}

となり、Xの列ベクトルをwで重み付けて足し合わせたベクトルになっています。

行列の積のベクトル表現3では、積のj列ベクトルは、行列XとYのj列ベクトルの積$\boldsymbol{X} \boldsymbol{y}_{\cdot j}$でした。さらにこの積は、行列Xの列ベクトルを重み付けて足し合わせたベクトルになります。

\begin{align*}
\boldsymbol{X} \boldsymbol{y}_{\cdot j} = \sum_{k=1}^K y_{kj} \boldsymbol{x}_{\cdot k}
\end{align*}

右から書ける行列の列ベクトルごとに、この列ベクトルを重みベクトルとして、左の行列の列ベクトルの重み付き和を計算していることになります。

2×2行列Xと長さ2のベクトルyの積の例です。

\begin{align*}
\boldsymbol{X} &=
 \left(
  \begin{array}{cc}
   1 & 2 \\
   3 & 4
  \end{array}
 \right) ,\quad
\boldsymbol{y} =
 \left(
  \begin{array}{cc}
   5 \\
   6
  \end{array}
 \right) \\
\boldsymbol{X} \boldsymbol{y} &=
 \left(
  \begin{array}{cc}
   \left(
    \begin{array}{c}
     1 \\
     3
    \end{array}
   \right),
   \left(
    \begin{array}{c}
     2 \\
     4
    \end{array}
   \right)
  \end{array}
 \right)
 \left(
  \begin{array}{cc}
   5 \\
   6
  \end{array}
 \right) =
 5  \left(
  \begin{array}{cc}
   1 \\
   3
  \end{array}
 \right) +
 6  \left(
  \begin{array}{cc}
   2 \\
   4
  \end{array}
 \right) =
 \left(
  \begin{array}{cc}
   17 \\
   39
  \end{array}
 \right)
\end{align*}

yは先ほどの行列の積の例のYの第1列ベクトルであり、結果はXとYの積の第1列ベクトルと等しくなっています。

行列の積の計算量

M×K行列とK×N行列はM×N行列になるのでした。その各要素は長さKの内積計算でした。M×N要素について長さKの内積計算なので、ざっくりO(M×N×K)の演算量になります。
M×K行列XとK×N行列YとN次元ベクトルzの積XYzを計算することを考えてみます。このとき計算の順番として(XY)zとX(Yz)があり得ます。

  • (XY)zと計算する場合
    • XYの計算にO(M×N×K)、結果(XY)はM×N行列なので長さNのベクトルzの積にO(M×N×1)の演算量になります。全体としてざっくりO(M×N×K)です。
  • X(Yz)と計算する場合
    • Yzの計算にO(N×K×1)、結果(Yz)は長さKのベクトルなので、Xとの積にO(M×K×1)の演算量になります。全体としてざっくりO((M+N)×K)です。

計算量の比はざっくり M×N:M+N になります。M=20000、N=10000としたら、200M:30K で単純計算6666倍にもなります。実際にはここまでの差にはならないものの、270倍程度の差は出ました。行列演算では「行列が小さくなる方から計算する」というのが行列演算の鉄則になります。

固有値と固有ベクトル

M×Mの正方行列Aに対して、長さMのベクトルpと実数λが

\begin{align*}
\boldsymbol{A} \boldsymbol{p} = \lambda \boldsymbol{p}
\end{align*}

となるとき、λを固有値、pを固有ベクトルと呼びます。pをa倍しても明らかに上記の関係は成り立つので、pのノルムは1になるように正規化します。これらの組は最大M組存在し、相異なる固有値に対応する固有ベクトルは線形独立です。

行列の固有値・固有ベクトル表現

M組の固有値と固有ベクトルを並べると

\begin{align*}
\boldsymbol{A} (\boldsymbol{p}_1,\cdots,\boldsymbol{p}_M)
 &= (\boldsymbol{A} \boldsymbol{p}_1, \cdots, \boldsymbol{A} \boldsymbol{p}_M) \\
 &= (\lambda_1 \boldsymbol{p}_1, \cdots, \lambda_M \boldsymbol{p}_M) \\
 &= (\boldsymbol{p}_1, \cdots, \boldsymbol{p}_M) {\rm diag}(\lambda_1, \cdots, \lambda_M)
\end{align*}

となります。ここで diag(λ1,...,λM) は λ1,...,λM を対角成分に持つ行列(対角行列)です。二つ目の等号は固有値・固有ベクトルの定義より、三つ目の等号は「行列の積のベクトル表現3」が当てはまります。

\boldsymbol{P}=(\boldsymbol{p}_1,\cdots,\boldsymbol{p}_M)

と置くと

\begin{align*}
\boldsymbol{A} \boldsymbol{P} = \boldsymbol{P} {\rm diag}(\boldsymbol{\lambda})
\end{align*}

と書くことができ、両辺に右から P^-1 を掛けることで

\begin{align*}
\boldsymbol{A} = \boldsymbol{P} {\rm diag}(\boldsymbol{\lambda}) \boldsymbol{P}^{-1}
\end{align*}

が得られます。行列Aを固有値と固有ベクトルで表現できました。P^-1の行ベクトル表現を

\boldsymbol{P}^{-1} = \left(
  \begin{array}{cc}
   \boldsymbol{q_1}^\top \\
   \vdots \\
   \boldsymbol{q_M}^\top \\
  \end{array}
 \right)

とすると、

\begin{align*}
\boldsymbol{A}
 &= (\boldsymbol{p}_1,\cdots,\boldsymbol{p}_M) {\rm diag}(\lambda_1,\cdots,\lambda_M) \left(
  \begin{array}{cc}
   \boldsymbol{q_1}^\top \\
   \vdots \\
   \boldsymbol{q_M}^\top \\
  \end{array}
 \right) \\
 &= (\boldsymbol{p}_1,\cdots,\boldsymbol{p}_M) \left(
  \begin{array}{cc}
   \lambda_1 \boldsymbol{q_1}^\top \\
   \vdots \\
   \lambda_M \boldsymbol{q_M}^\top \\
  \end{array}
 \right) \\
 &= \sum_{i=1}^M \lambda_i \boldsymbol{p}_i \boldsymbol{q}_i
\end{align*}

と書け、「行列の積のベクトル表現2」の形になっています。固有値の絶対値の大きいものが行列Aを構成するのに寄与が大きいことになります。

対称行列の場合

対象行列の場合、異なる固有値に対応する固有ベクトル同士は直行し、

\boldsymbol{P}^{-1} = \boldsymbol{P}^\top

となり、

\begin{align*}
\boldsymbol{A}
 &= (\boldsymbol{p}_1,\cdots,\boldsymbol{p}_M) {\rm diag}(\lambda_1,\cdots,\lambda_M) \left(
  \begin{array}{cc}
   \boldsymbol{p_1}^\top \\
   \vdots \\
   \boldsymbol{p_M}^\top \\
  \end{array}
 \right) \\
 &= \sum_{i=1}^M \lambda_i \boldsymbol{p}_i \boldsymbol{p}_i^\top
\end{align*}

となります。

行列の積の固有値・固有ベクトル表現

任意のM次元ベクトルbは、M本の線形独立なベクトルの線形和で表せます。よって、M×Mの正方行列AのM本の固有ベクトルを用いて

\begin{align*}
\boldsymbol{b}= \sum_{i=1}^M w_i \boldsymbol{p}_i
\end{align*}

と表せます。Aとbの積は

\begin{align*}
\boldsymbol{A} \boldsymbol{b}
 &= \boldsymbol{P} {\rm diag}(\boldsymbol{\lambda}) \boldsymbol{P}^{-1} \sum_{i=1}^M w_i \boldsymbol{p}_i \\
 &= \sum_{i=1}^M w_i \boldsymbol{P} {\rm diag}(\boldsymbol{\lambda}) \boldsymbol{P}^{-1} \boldsymbol{p}_i \\
 &= \sum_{i=1}^M w_i \boldsymbol{P} {\rm diag}(\boldsymbol{\lambda}) \boldsymbol{e}_i \\
 &= \sum_{i=1}^M w_i \boldsymbol{P} \lambda_i \boldsymbol{e}_i \\
 &= \sum_{i=1}^M w_i \lambda_i \boldsymbol{p}_i
\end{align*}

となり、行列を掛けるということは、行列の各固有ベクトル方向に固有値倍していることがわかります。

行列式

行列式は固有値の積でした。「行列を掛けると、行列の各固有ベクトル方向に固有値倍」なので、行列式は行列を掛けたときの体積の拡大率になります。

固有値・固有ベクトルの求め方:べき乗法

bにAをn回掛けると

\begin{align*}
(\boldsymbol{A})^n \boldsymbol{b}
 &= \sum_{i=1}^M w_i (\lambda_i)^n \boldsymbol{p}_i \\
 &= \sum_{i=1}^M w_i \lambda_{\rm max}^n \left( \frac{\lambda_i}{\lambda_{\rm max}} \right)^n \boldsymbol{p}_i \\
\end{align*}

となり、絶対値が最大の固有値・固有ベクトルが支配的になっていきます。このベクトルを正規化することで、絶対値が最も大きい固有値に対応する固有ベクトルが得られます。求まった固有ベクトルにAを掛けて、何倍になったかを求めることで固有値が得られます。

逆行列の固有値・固有ベクトル表現

固有値・固有ベクトルは

\begin{align*}
\boldsymbol{A} \boldsymbol{p} = \lambda \boldsymbol{p}
\end{align*}

でした。両辺にA^-1を左から掛けて、λで割ると

\begin{align*}
\frac{1}{\lambda} \boldsymbol{p} = \boldsymbol{A}^{-1} \boldsymbol{p}
\end{align*}

となります。よって、Aの固有値の逆数λ^-1と固有ベクトルpは、逆行列A^-1の固有値・固有ベクトルになります。
A^-1をAの固有値・固有ベクトルで表現すると

\begin{align*}
\boldsymbol{A}^{-1} = \boldsymbol{P} {\rm diag}(\boldsymbol{\lambda})^{-1} \boldsymbol{P}^{-1}
\end{align*}

と書けます。確認すると、たしかに

\begin{align*}
\boldsymbol{A} \boldsymbol{A}^{-1} &= \Big( \boldsymbol{P} {\rm diag}(\boldsymbol{\lambda}) \boldsymbol{P}^{-1} \Big) \Big( \boldsymbol{P} {\rm diag}(\boldsymbol{\lambda})^{-1} \boldsymbol{P}^{-1} \Big) = \boldsymbol{I} \\
(\boldsymbol{A}^{-1}) \boldsymbol{A} &= \Big( \boldsymbol{P} {\rm diag}(\boldsymbol{\lambda})^{-1} \boldsymbol{P}^{-1} \Big) \Big( \boldsymbol{P} {\rm diag}(\boldsymbol{\lambda}) \boldsymbol{P}^{-1} \Big) = \boldsymbol{I}
\end{align*}

となります。

特異値分解

M×N行列Aに対して長さMのベクトルu、長さNのベクトルvと実数σが

\begin{align}
\boldsymbol{A} \boldsymbol{v}= \sigma \boldsymbol{u}
\end{align}

かつ

\begin{align}
\boldsymbol{A}^\top \boldsymbol{u}= \sigma \boldsymbol{v}
\end{align}

を満たすとき、σを特異値、uを左特異ベクトル、vを右特異ベクトルと呼びます。uとvは正規化されたものを考えます。一般に少なくとも一つ、多くともmin(M,N)組の特異値・左特異ベクトル・右特異ベクトルの組を持ちます。また、異なる特異値に属する左特異ベクトル同士、右特異ベクトル同士は直交します。

M > NでN個の特異値があるとき、これらを並べて

\begin{align*}
\boldsymbol{A} (\boldsymbol{v}_1,\cdots,\boldsymbol{v}_N)
 &= (\sigma_1 \boldsymbol{u}_1, \cdots, \sigma_N \boldsymbol{u}_N) \\
 &= (\boldsymbol{u}_1, \cdots, \boldsymbol{u}_N) {\rm diag}(\sigma_1, \cdots, \sigma_N)
\end{align*}

と書けます。

\begin{align*}
\boldsymbol{V} &= (\boldsymbol{v}_1,\cdots,\boldsymbol{v}_N) \\
\boldsymbol{U} &= (\boldsymbol{u}_1, \cdots, \boldsymbol{u}_N) \\
\boldsymbol{\sigma} &= (\sigma_1, \cdots, \sigma_N)^\top
\end{align*}

と置くと、

\begin{align*}
\boldsymbol{A} \boldsymbol{V} = \boldsymbol{U} {\rm diag}(\boldsymbol{\sigma})
\end{align*}

です。左特異ベクトル同士、右特異ベクトル同士は直交するので、

\begin{align*}
\boldsymbol{U}^\top \boldsymbol{U} &= \boldsymbol{I} \\
\boldsymbol{V}^\top \boldsymbol{V} &= \boldsymbol{I}
\end{align*}

で、VはN×N行列なのでV^Tは逆行列です。両辺に右から掛けると

\begin{align*}
\boldsymbol{A} = \boldsymbol{U} {\rm diag}(\boldsymbol{\sigma}) \boldsymbol{V}^\top
 = \sum_{i=1}^N \sigma_i \boldsymbol{u}_i \boldsymbol{v}_i^\top
\end{align*}

が得られ、行列Aを特異値と左特異ベクトル、右特異ベクトルで表現できました。

特異値分解の求め方

AA^T は

\begin{align*}
\boldsymbol{A} \boldsymbol{A}^\top
 &= \boldsymbol{U} {\rm diag}(\boldsymbol{\sigma}) \boldsymbol{V}^\top \boldsymbol{V} {\rm diag}(\boldsymbol{\sigma}) \boldsymbol{U}^\top \\
 &= \boldsymbol{U} {\rm diag}(\sigma_1^2, \cdots, \sigma_D^2) \boldsymbol{U}^\top
\end{align*}

なので、UはAA^Tの固有ベクトルを求めることで得られます。AA^Tは対象行列なので、異なる固有値に対応する固有ベクトル同士は直行します。あるいは、VはA^T Aの固有ベクトルを求めることで得られます。特異値分解の定義より、どちらか一方が求まれば他方が求まります。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした