iPX社員によるブログ

iPX社員が"社の動向"から"自身の知見や趣味"、"セミナーなどのおすすめ情報"に至るまで幅広い話題を投下していくブログ。社の雰囲気を感じ取っていただけたら幸いです。

Simulinkによる順次シミュレーションを作る

今回のブログ担当のシロハです。
今回のテーマは、私が業務で使用することのあるMATLAB/Simulinkというツールについてです。

初めに

今回の記事で扱う内容は、mファイルを用いたSimulinkの順次シミュレーションの実行についてです。
また、MATLAB/Simulinkの基礎(mスクリプトの扱い方、Simulinkでのモデル作成やシミュレーションの実行方法)を知らないと理解が難しい内容が含まれます。

順次シミュレーションとは

初めに、順次シミュレーションとは、事前に複数パターンのシミュレーションの仕込みをしておいて、シミュレーションを1度実行すれば、全てのシミュレーションを終わらせてくれる、というものです。

順次シミュレーションの目的

1Dシミュレーションでは一般的に、3Dシミュレーションよりも1回のシミュレーションに時間が短いですが、その分複数パターンの検証が必要になることが多い、ということを何度か経験しました。
ただし3Dよりは早いといっても、モデルと検証条件によっては1パターンで1時間以上かかることもよくあります。
ということで今回の目標は、
順次シミュレーションを用いることで、帰宅前や金曜日の帰り際にシミュレーションを実行する⇒次の出勤時にシミュレーション結果が確認できる、というものを目指します。

順次シミュレーションの流れ

まず、順次シミュレーションを行う為にMATLAB/Simulinkにどのような作業をして欲しいのか、について考えます。

  1. 複数パターンのシミュレーションをコンピュータに順次行ってもらうために、mスクリプトからシミュレーションを実行する必要があります。また、実行したいパターン数だけシミュレーションを繰り返す必要があります。
  2. 全く同じシミュレーションを繰り返しても意味はないので、シミュレーションのたびにブロックの配置や結線・パラメータなどを書き換える必要があります。
  3. シミュレーションを実行した後で結果が残っていないのでは意味がありません。シミュレーション結果を保存して、外部にエクスポートする必要があります。

では、これらの作業をmファイルに書き込んでみましょう。

  1. matlabスクリプトSimulinkのシミュレーションを実行する場合は、sim関数を用います。
    使い方:sim('シミュレーションしたいSimulinkのモデル名')
    また、複数のパターンをシミュレーションする必要があるため、sim関数や、この後説明する要素をfor文の中に記述する必要があります。

  2. スクリプトからブロックの配置や結線・パラメータ書き換えを実行します。ブロックを配置・パラメータ変更するためにはブロックの持つパラメータ名を知っておく必要があります。
    知っておく必要のある関数について
    ブロックパラメータの変更:set_param
    ブロックパラメータの取得:get_param
    ブロックの配置:add_block
    ブロックの結線:add_line
    説明すると長くなるため、関数の詳細はヘルプを参照してください。

  3. :ブロックパラメータ名の調べ方
    その1:以下のページでブロックパラメータ名を調べることが出来ます。
    https://jp.mathworks.com/help/simulink/slref/block-specific-parameters.html

    その2:get_param関数を用いてブロックパラメータ名を調べます。
    ブロックパラメータは、関数add_blockでブロックを配置したり、set_paramでブロックパラメータを変更する際に必要になります。
    a:パラメータ名を調べたいブロックをダブルクリックして、調べたいパラメータの値を適当な値(11111111など)にします。

    b:パラメータ名を調べたいブロックをクリックします。
    (手順b~cまでの間に他のブロックをクリックしないこと。分かる方向けにいうとgcbを用いているため)

    c:次のコードをエディタ、またはコマンドウィンドウで実行します。
    DATA=get_param(gcb,’ObjectParameters’);
    PARAM=fieldnames(DATA);
    for i=1:length(PARAM)
    PARAM{i,2} = get_param(gcb,PARAM{i,1});
    end

    d:実行した結果がワークスペースのセル配列'PARAM'に保存されています。
    PARAMの1列目がパラメータ名、2列目が1列目に対応したデータとなっています。
    セル配列 PARAM の中に、先ほど設定した適当な値があるので、その隣の1列目に書いてあるのが目的のパラメータ名となります。

  4. シミュレーション結果を保存する方法はいくつかありますが、今回はSimulinkに「To Workspace」ブロックを配置してから、xlswriteでエクスポートすることでデータを保存します。
    ちなみに、単純にデータを保存するだけならばSimulinkに「To File」ブロックを置くのが一番簡単です。ただ、Simulinkとの連携に限らず、xlswriteでデータをエクスポートするという手法が便利であること、またWorkSpaceに一度データを残すことで、WorkSpace上のデータを処理してからエクスポートすることが可能になるなど応用が利くため、今回はxlswriteを用います。

基本的な骨組み

先ほどの1~3を組み合わせて、順次シミュレーションの骨組みを作ると以下のようになります。

S = シミュレーションのパターン数;
for i=1:S %S回シミュレーションを実行する
  switch i %case文の中にパラメータやソルバなどの、シミュレーション毎に変更する点を記述する
    case 1
      set_param(~)
      add_block(~)
      add_line(~)
    case 2
      set_param(~)
      add_block(~)
      add_line(~)
    case 3
    ・・・
    otherwise
  end
sim(‘モデル名’) %シミュレーションを実行
xlswrite(~)   %データをエクスポートする
end

この骨組みを基本として、Switch文の中に必要に応じて関数を追加すれば順次シミュレーションの完成です。

まとめ

MATLABは検索すると大体のことは知ることが出来るため、あえて今回は初心者向けではない内容にしております。MATLABの基本について知りたい方は「MATLAB 使い方」で検索すると良いと思います。

また、今回はあえてMATAB/Simulinkを連携させる形で順次シミュレーションを行っていますが、一般的にシミュレーションに掛かる時間を減らす場合は、シミュレーションの高速化や動的システムの並列シミュレーションを用いるようです。
尤も私がよく知らないために書けないのですが。

隠れマルコフモデルのお勉強

ご無沙汰しております。マルコフ部(非公認)部長の小川です。

業務上で隠れマルコフモデル(HMM)について理解する必要がでており、実装に際して理論の方のお勉強をしようと思います。

今回は第一回という事で条件付き確率についてお勉強した内容を記そうと思います。

続きを読む

NVIDIA GTC JAPAN 2018 のご案内

来週 9/13, 14 に NVIDIA 社主催の GPU Technology Conference (GTC) JAPAN 2018 が開催されます。
今年は弊社も SILVER SPONSOR として協賛させていただいております。

当日はスポンサーブースにて弊社の深層学習活動の一環として、 3D LiDAR でリアルタイムに取得した点群情報から GAN (Generative Adversarial Network) を利用して画像補完することで物体認識率を向上させるデモをご紹介させていただく予定です。

イベント参加の折には、是非弊社ブースへお立ち寄りください。

ねこ日記(3)

ご無沙汰しております、CPU(経営企画戦略室)所属のウメザワです。

前回ブログをいつ書いたのか調べてみたら約1年前でした。
(「ねこ日記(2)」が観たい方は"2017/7/7"をご参照ください)

「電気代を節約しながら朝まで起きずに快適に寝れるかどうかの瀬戸際をクーラーのおやすみタイマーで設定するのがプチブームです。」
とか書いてありましたが、そんなブームは過ぎ去り朝から晩まで(外出中も)つけっぱなしです。

まぁ今年はペットも危険なほどの猛暑なので、うちの可愛い子達の為にもつけっぱなしはやむを得ません。


ということでお待ちかねの癒し提供のターンに行ってみましょう!

続きを読む

Rustでデカルトの葉

近況

また出番が回ってきました砂子です。もう何回目の投稿になるのでしょうか?

今年は本当に暑いですね。普段エアコンは極力使わないようにしてきまたが、 さすがに暑かったのでを久しぶりに電源をいれ涼もうしました。しかし、どう も調子が悪いようなので修理を依頼しました。立ち会いの都合などで、一週間 ほど故障した状態が続いたのですがなんだか無性に暑く感じました。

新言語

さて、私はプログラマーであるわけで当然プログラム言語を記述して仕事をし ております。職務として長らく使ってきたのはC#言語となります。現職務は WEB開発のフロントエンドを担当しているのでTypeScriptをメインとしており ます。思えば、どちらもマイクロソフトに縁が深い言語ですね。

上記2つの言語を深く理解するのも大事ですが、新しいプログラミング言語を 習得し知見を広げることも大事と考え、Rustというものを学習をしてお ります。目下のところ、The Bookと呼ばれるオンラインドキュメントを 読みつつ学習しています。

公式ページにあるように速度、安全性、並行性を目的としてプログミン グ言語であるようです。最も学習を初めて間もないため3つのうち安全性に関 連した要素しか実感できておりません。此の実感した要素は安全性に関連する 「所有権」です。恐らく大半の言語では聞かない概念であると思います。

まあ、実感したというのはコードを書くうえで面倒だったということです。習得 しているプログランミング言語にもよりますが、2つ目以降の言語としてRust を学習する方々は必ずこの所有権の概念で同じことを思うでしょう。

Rustの学習がて簡単なプログラムを書いてこのブログ記事にしよと考えたち、 なにが題材としてよいか自宅にある書籍を物色しました。

デカルトの葉

いくつか書籍を眺めていたら古典的難問に学ぶ微分積分の序章にデカル トの葉が描かれてるのが目に止まりました。曲線を描くというのも難易度とし 難しくないと考えこれを題材にしようと考えました。

デカルトの葉とはWikipediaではデカルトの正葉線として説明がなされて いる代数曲線になります。大学で微積分を学んだ方なら教科書に描かれていたもの が記憶に残っているのではないでしょうか?

直行座標の方程式による定義は下記のようになります。

 { \displaystyle
   x^{3} + y^{3} - 3axy = 0
}

上記の数式はまたパラメータ表示として下記のようにも表わされます。

 { \displaystyle
   x = \frac{3at}{1+t{3}} \quad (t \neq -1)
}

 { \displaystyle
   y  = \frac{3at^{2}}{1+t^{3}} \quad (t \neq -1)
}

また、第一象限のループで囲まれる面積は下記のようになります。

 { \displaystyle
   S = \frac{3a^{2}}{2}
}

そこで3つのことをRustで実現しよう。

  • パラメータ表示による曲線のプロット
  • 直行座標の方程式を使用した乱数による曲線のプロット
  • ループで囲まれる面積の乱数を使用した近似値の計算

なお、以降では a=3として計算する。

Rust 準備

Rustのインストール等の説明は公式ページを見てください。 以下ではプロジェクト作成と使用したライブラリだけを記述します。

プロジェクトを作成。引数でライブラリではなく実行可能なアプリとして設定しています。

cargo new folium_of_descartes --bin

使用する外部ライブラリを設定します。 出来上がったプロジェクトフォルダ内のCargo.tomlを下記のように編集します。

[package]
name = "folium_of_descartes"
version = "0.1.0"

[dependencies]
gnuplot = "0.0.26"
rand = "0.5"

main.rsを編集後、プログラムを実行するには下記コマンド実行です。 初回はCargo.tomlで設定したライブラリのダウンロード等が行われます。 記述ミスがあった場合はこの段階でエラーなり、警告がでます。

cargo run

Rust ソースコード(main.rs)

そして、記述したコードを貼っておく。プロジェクトフォルダに出来上がったmain.rsに記述した。

extern crate gnuplot;
extern crate rand;

// プロット用のライブラリ
use gnuplot::*;

// 乱数発生用のライブラリ
use rand::prelude::*;
use rand::prng::XorShiftRng;

//// パラメータにより曲線を描画する
fn draw_curve_parameter() {

    // 3つの範囲で(x,y)を算出
    
    // -100 < t < -2 
    let ts1 = get_parameter(-100., -2., 0.1);
    let xs1 = get_x_from_parameter(&ts1);
    let ys1 = get_y_from_parameter(&ts1);
    
    // -0.5 < t < 0.0
    let ts2 = get_parameter(-0.5, 0.0, 0.05);
    let xs2 = get_x_from_parameter(&ts2);
    let ys2 = get_y_from_parameter(&ts2);

    // 0.0 < t < 5.0
    let ts3 = get_parameter(0.0, 5.0, 0.1);
    let xs3 = get_x_from_parameter(&ts3);
    let ys3 = get_y_from_parameter(&ts3);

    // -5.0 < x < 5.0, -5.0 < y < 5.0 をプロット
    let mut fg = Figure::new();

    // 
    fg.axes2d()
        .set_aspect_ratio(Fix(-1.0))
        .set_x_axis(true, &[])
        .set_y_axis(true, &[])
        .set_x_range(Fix(-5.), Fix(5.0))
        .set_y_range(Fix(-5.), Fix(5.0))
        .points(xs1, ys1, &[Color("green")])
        .points(xs2, ys2, &[Color("blue")])
        .points(xs3, ys3, &[Color("red")]);
    fg.show();
}

/// 指定された範囲のパラメータを取得する
fn get_parameter(start: f64, end: f64, step: f64) -> Vec<f64> {
    let mut tmp = Vec::new();

    let mut x = start;
    while x < end {
    tmp.push(x);
    x = x + step;
    }

    tmp
}

/// パラメータに対応したXを取得する
fn get_x_from_parameter(ts: &Vec<f64>) -> Vec<f64> {
    ts.into_iter().map(|t| (3.0*3.0*t)/(1.0+t.powi(3))).collect()
}

/// パラメータに対応したYを取得する
fn get_y_from_parameter(ts: &Vec<f64>) -> Vec<f64> {
    ts.into_iter().map(|t| (3.0*3.0*t.powi(2))/(1.0+t.powi(3))).collect()
}

/// 乱数により曲線を描く
fn draw_curve_random() {
    let seed = [0u8; 16];
    let mut rng = XorShiftRng::from_seed(seed);
    
    let mut xs: Vec<f64> = Vec::new();
    let mut ys: Vec<f64> = Vec::new();
    
    let mut i = 10000000;
    while i != 0  {
        let x = rng.gen_range(-5., 5.) as f64;
        let y = rng.gen_range(-5., 5.) as f64;
        let z = x.powi(3) + y.powi(3) - 3.0 * 3.0 * x * y;

        if z.abs() < 0.001 {
            xs.push(x);
            ys.push(y);
        }

        i = i - 1;
    }

    let mut fg = Figure::new();
    
    fg.axes2d()
        .set_aspect_ratio(Fix(-1.0))
        .set_x_axis(true, &[])
        .set_y_axis(true, &[])
        .set_x_range(Fix(-5.), Fix(5.0))
        .set_y_range(Fix(-5.), Fix(5.0))
        .points(xs, ys, &[Color("blue")]);
    fg.show();
}

/// 第一象限の面積を描く
fn draw_area() {

    // 乱数初期化
    let seed = [0u8; 16];
    let mut rng = XorShiftRng::from_seed(seed);
    
    let mut xs: Vec<f64> = Vec::new();
    let mut ys: Vec<f64> = Vec::new();

    let size = 100000;
    let mut i = size;
    while i != 0 {
        let x = rng.gen_range(0., 5.) as f64;
        let y = rng.gen_range(0., 5.) as f64;
        let z = x.powi(3) + y.powi(3) - 3.0 * 3.0 * x * y;

        if z < 0.001 {
            xs.push(x);
            ys.push(y);
        }

        i = i - 1;
    }

    // 曲線内部と判定された割合から面積を算出する
    let area = 5.0 * 5.0 * xs.len() as f64 / size as f64;

    let mut fg = Figure::new();
    
    fg.axes2d()
        .set_title(&format!("面積:{}", area), &[])
        .set_aspect_ratio(Fix(-1.0))
        .set_x_axis(true, &[])
        .set_y_axis(true, &[])
        .set_x_range(Fix(0.), Fix(5.0))
        .set_y_range(Fix(0.), Fix(5.0))
        .points(&xs, ys, &[Color("blue")]);
    fg.show();
}

fn main() {
    draw_curve_parameter();
    draw_curve_random();
    draw_area();
}

結果

新言語とはいえ単純な文法だけでも曲線は十分に描け、簡単な数値実験を行な うことができた。やはりドキュメントを読むだけでは飽きが来ることや理解の 度合いが感じづらいので簡単なゴールを設定してみるとモチベーションに繋が りやすい。

パラメータ表示による曲線のプロット

パラメータによる曲線の描画は関数draw_curve_parameter()で行った。プロットは下図のようになった。 緑は  - 100 \lt t \lt -2 で第一象限、 青は -0.5 \lt t \lt 0.0 で第二象限、赤は 0.0 \lt t \lt 5.0 で第4象限となります。 f:id:ipx-writer:20180727110853p:plain

しかし、曲線上に均等に点が描画されると予想したがそうはならなかった。方程式の形で見ているとまるで 曲線上に点が密に分布するイメージを持つがそんなことはなかった。

直行座標の方程式を使用した乱数による曲線のプロット

関数draw_curve_with_random()で乱数で大量の座標 (x,y)を生成し、 abs( x^{3} + y^{3} - 3axy ) \lt 0.001に適合する座標のみをプロットした。 f:id:ipx-writer:20180727110844p:plain

こちらは均等に点が分布にすると考えたがならなかった。考えてみれば曲線の変化率は場所ことに違うので確かに均等に分布しないことになる。つまり、ある曲線の部分では変化率が小さいため上記の判定が真になる面積が大きいが、またある場所では変化率が大きいため上記判定で真になる面積は少ない。それが場所ごとの点の分布に現れるのではないか?

ループで囲まれる面積の乱数を使用した近似値の計算

関数draw_area_with_random()で第一象限のループ状の内部の面積を求めてみた。 上記曲線描画と同様に乱数で大量の座標 (x,y)を生成し、 x^{3} + y^{3} - 3axy \lt 0.001で判定された座標の個数と 生成した座標の割合を乱数生成の範囲の面積にかけて算出している。 f:id:ipx-writer:20180727110739p:plain

期待値は S = \frac{3a^{2}}{2} = 13.5 \quad (a=3)であるので、近似値がえられている。

所感

学習中のプログラミング言語でも自分としては有益な結果がでた。学生時代にこうゆうことをやっていれば微積分にも もっと興味をもって取り組めたのではないだろうか?

Rustの習熟度は正直かなり問題ありですね。いろいろあるが早急に下記は対応したい。

  • ドキュメント、コメントの書き方
  • ライブラリのドキュメントの読み方
  • 開発環境(Emacs)の整備

曲線の描画は興味深いテーマであったと思う。Rustの学習を進めながら曲線を調べて描画を行っていこう。 できれば学生時代の卒論であつかったMD6の実装を最終ゴールとしたい。ではこれにて。

下野花岡駅まで旅行

カノマタの番となりました
暑い日が続きますが、先週、思い切って小旅行をしました
いつも通り、「ブログどうしようかな」を鬱々とした日を送っておりました
そこで、小旅行をすることにしました

自宅から下野花岡駅まで歩き、そこから電車で宝積寺駅まで帰ってきて自宅へ帰ってくる行程です
自宅~下野花岡駅の4kmの距離、宝積寺~自宅の1kmの計5kmの小旅行です
下の地図のように歩きます
スタートの住所は、塩谷郡唯一のパチンコ店「テンダーボス」です

f:id:ipx-writer:20180721105603j:plain

スタート

スタートの時間は12:00頃
気温は32℃くらいでしょうか
自宅から少々の距離のパチンコ店「テンダーボス」がスタートです
高根沢町だけでなく、塩谷郡で1店舗しかないパチンコ店です
f:id:ipx-writer:20180721113029j:plain

少し東に歩くと、高根沢町に住んでいる社員の共通認識「三角のファミマ」
f:id:ipx-writer:20180721113323j:plain

高根沢町のメインストリート「10号線」との交差点
向こうに見える建物はホンダの寮です
f:id:ipx-writer:20180721113422j:plain

10号線沿いを歩く

10号線を北へ
f:id:ipx-writer:20180721114046j:plain
まずは下り坂
草木が多い

f:id:ipx-writer:20180721113851j:plain

うっそうと茂っていても、ごみは持って帰るべし
f:id:ipx-writer:20180721113923j:plain

左は田んぼ
f:id:ipx-writer:20180721114011j:plain

左へ曲がると、「高根沢町役場」
f:id:ipx-writer:20180721114826j:plain

こんなところに中古PCショップがある
結構長らくやっている
f:id:ipx-writer:20180721114234j:plain

田んぼと畑ばかりが続く
f:id:ipx-writer:20180721114323j:plain
f:id:ipx-writer:20180721114337j:plain

コイン精米機をよく見かけるのが、この地の特徴
f:id:ipx-writer:20180721114444j:plain

カーブして東へ

10号線は大きくカーブして東へ向かいます
f:id:ipx-writer:20180721115153j:plain

交通安全関所という、謎の建物
f:id:ipx-writer:20180721115245j:plain

風景はあまり変化がありません
夏ですね、緑が映えます
夏でなくても緑ばかりの地ですが
f:id:ipx-writer:20180721115309j:plain

結構そこかしこにある中古車販売店
ここは外国の方がやっているお店
f:id:ipx-writer:20180721115409j:plain

水路に魚がたくさん泳いでる
f:id:ipx-writer:20180721115451j:plain

やっと中間付近

高根沢町みんなが利用する洗車場
昼は普通の人、夜はそういったの人が利用する
自分は夜に来ることが多いです
f:id:ipx-writer:20180721115630j:plain

後ろから声を掛けられました
一緒に仕事をしている人です
暑い中、10km離れた宇都宮の自宅から洗車をしに来たそうです
赤のラインのホイールは、最近買った20万円ぐらいのです
この方の車
f:id:ipx-writer:20180721120001j:plain

少々休憩

再び東へ

気温もさっきより上昇しているような気がします
こんなところにもディーラーが
f:id:ipx-writer:20180721120535j:plain

地元で人気のソバ屋「八溝」
行ったことはないです
f:id:ipx-writer:20180721120632j:plain

高根沢町にある運動公園の入口
f:id:ipx-writer:20180721120719j:plain

にある、バス停
f:id:ipx-writer:20180721120815j:plain

バスにやる気が感じられない
ここへバスできたら、どうやって帰るのか?
f:id:ipx-writer:20180721120848j:plain

人気のあるラーメン屋「赤大屋」
いつも結構人がいる
f:id:ipx-writer:20180721121027j:plain

10号線の六角形
f:id:ipx-writer:20180721121105j:plain

田んぼ、畑
f:id:ipx-writer:20180721121145j:plain

温泉施設「元気あっぷ村」
一度行ったことがあるけど、結構いい施設です
高根沢ちゃんぽん」というご当地メニューがあります
f:id:ipx-writer:20180721121311j:plain

下野花岡駅周辺

青看板で左に「さくら」とあったら左へ曲がります
県道181号線との交差点です
f:id:ipx-writer:20180721121414j:plain

駅前のセブンイレブンです
向こうに見える中華料理店「天昇」は定食が充実しています
f:id:ipx-writer:20180721121555j:plain

ここら辺も田んぼだらけ
f:id:ipx-writer:20180721121706j:plain

駅のバス停
f:id:ipx-writer:20180721121738j:plain

やる気が感じられない
f:id:ipx-writer:20180721121805j:plain

下野花岡駅に到着

しばらく歩くと、踏切が見えてきます
f:id:ipx-writer:20180721121905j:plain

踏切の右手に見えるのが、ゴールの「下野花岡駅」です
f:id:ipx-writer:20180721122005j:plain

ええ、無人駅です
だれもいません
f:id:ipx-writer:20180721122111j:plain

単線です
f:id:ipx-writer:20180721122126j:plain

駅構内
構内という表現が正しいのでしょうか
f:id:ipx-writer:20180721122240j:plainf:id:ipx-writer:20180721122251j:plain
f:id:ipx-writer:20180721122320j:plainf:id:ipx-writer:20180721122522j:plain

烏山線はワンマン列車で、カードが使えません
どのように乗り降りするのかドキドキです
宝積寺まで190円
f:id:ipx-writer:20180721122451j:plain

今「13時24分」、次の電車は...と
f:id:ipx-writer:20180721122629j:plain

次は「14時35分」
なんやねん!
1時間10分待って電車で帰ると自宅まで2時間弱
歩いて帰った方が早いじゃん

というわけで、35℃ぐらいのなか、歩いて帰ったとさ

何事も

計画が大事
何日も綿密に計画を立ててこそ、よい成果が出るのだと
今回の小旅行がいい例ですね

情報共有と情報伝達の重要性

EPLUの加藤です。

今回は、当社が抱えている課題について取り上げてみたいと思います。

情報共有と情報伝達の重要性

社内だけでなく社外も含めて、適切に情報を共有する、という事はビジネスを行う上で重要な活動のひとつです。

  • お客様に分かりやすく情報を提供する
  • お客様から必要な情報を入手する
  • プロジェクトメンバーにプロジェクトの内容を説明する

情報を共有するためには、いかに正しく情報を伝達するか、が鍵になります。

続きを読む