線形代数で忘れがちなこと
線形代数すっかり忘れてて、大事なことだけ自分ようにメモ。
- 行列の基本変形には3つあって、i行とj行を交換、i行目をc倍、i行目にj行目のc倍を加算、がある。左基本変形では行に関して、右基本変形では列に関して。これらの組み合わせで(p, q)をかなめとして吐き出す操作が可能。また、これらの操作をする行列はすべて正則であり、rankなども保存される。
- 一次方程式では拡大係数行列を作りの形にする。左基本変形と最後の列以外の列の交換を行うことによって形を作る。
- 方程式が解をもつためには、係数行列の階数と拡大係数行列の階数が一致することが必要十分条件である。
- のcを0にして にしたものを斉次一次方程式といい、これは、n-r(Aのrank)個の非自明な解を持つ。
- 複素ベクトルの内積をエルミート積という。
- をAの随伴行列といい、で表す。
- のとき、Aをエルミート行列といい、特に実数の場合を実対称行列という。
- のとき、Aをユニタリ行列といい、特に実数の場合を直行行列という。
- ユニタリ行列であることと、であること、であること、Aの列ベクトルに関してであることは同値である。
- n個の元からなる集合、たとえば{1,2…,n}の一対一変換をn文字の置換という。また、n文字の置換で二つの文字を置換し、他は動かさないものを互換という。
- 任意の置換は互換の積として表され、互換の数が偶数か奇数であるかははじめに与えた置換で決まる。
- はが偶置換のときプラス, 奇置換のときマイナス
- 個の変数の多項式をn次の行列式という。
- 転置行列の行列式は、もとの行列式と等しい。
- 行列式にはn重線形性と交代性があり、これが行列式を特徴づける。また逆に、n個のn項列ベクトルの組に対してある数を対応させる写像Fがn重線形性と交代性を持つならば、Fは定数倍を除いて写像detに一致する。
- n重線形性とは、detにおいてある列がx+yだとしたとき、そのx, yを分けてdetに入れて足しても同じという性質 + ある列にcをかけてdetをとった数とcを独立してかけた数は同じという性質のこと。
- 交代性とは、行列Aの列番号に置換τを施して得られた行列の行列式はsgnτ * |A|に等しいという性質。
- 上の二つの性質は行と列に関してそれぞれ成り立つ。
- n次正方行列Aの第i行目、第j列目を除いてできるn-1次行列式をAの(i,j)小行列式といい、それにをかけたものをAの(i,j)余因子といい、と表す。
- を(i, j)成分とするn次行列をAの余因子行列といい、と表す。
- となる。これは逆行列の式にもなっており、また、ここからクラメルの公式が導ける。
- となる正則行列Pが存在するとき」、AとBは相似であるという。これは同値関係ともいい、反射律、対称律、推移律が成り立つ。互いに同値なものの集合を類という。
- TをV->V'の線型写像といい、V->Vの線型変換という。また、VからV'への線型写像が一対一のとき、同型写像という。
- を線型関係といい、自明でない線型関係が成立するとき、線型従属であるという。その逆が線型独立である。
- Vに有限個のベクトルが存在して、Vの任意のベクトルがこれら有限個のベクトルの線型結合として表されるとき、Vは有限次元であるという。
- Vの有限個のベクトルが線型独立かつVの任意のベクトルをそれらの線型結合として表せるとき、これらを基底という。
- Vの基底の含むベクトルの数nを線型空間の次元といい、dim Vで表す。
- Vがn個のベクトルからなる基底を持つならば、n個より多くのベクトルは線型従属である。
- Vの部分集合Wが同じ演算に関してK上の線形空間になるとき、WをVの線型部分集合という。
- がVの部分空間であるとき、をの和集合であるという。これは+で表し、共通集合から生成される部分空間に他ならない。
- (m, n)型行列Aの階数を与える同値な条件は、Aを基本変形で標準形に変形したときに対角線上に並ぶ1の数、Aの0でない小行列式の最大次数、Aによって定まるからへの線型写像の像空間の次元、Aの線型独立な列ベクトルの最大数、Aの線型独立な行ベクトルの最大数、の5つである。
- 計量線型空間において、実数のときこれをユークリッド空間、複素数のときこれをユニタリ空間という。
- 計量空間Vのk個のベクトルがたがいに直交し、それぞれの長さが1に等しいとき、それらを正規直交系といい、それらがVの基底であるとき、正規直交基底という。そしてこれを作成する手続きをシュミットの直交化法という。
- 計量同型写像はユニタリ変換によってなされる。
うんたらかんたら制御方式
コンプライアンス制御やらアドミタンス制御やらいろいろ聞くのでまとめてみた(適当)
そしたら思ったより被った話ばっかりだった(ウケる)
インピーダンス制御はその名の通り機械インピーダンスを制御する。
環境と接触するときに機械インピーダンスを小さくすることで柔らかく、無理な力がかからないように制御できる。
ちなみに、ほんの少し違うっぽいけどアドミタンス制御も同じ意味。
コンプライアンス制御はその名の通りコンプライアンスを制御する。
コンプライアンスはバネ定数kの逆数であり、つまりばねの柔らかさの指標である。よって、大きければ大きいほど無理な力がかからず良い。
しかし、インピーダンス制御と違って、慣性項と粘性項を無視しているので動きが遅いロボットのみに適用するのが丸い。
バネ定数kそのものはスティフネスと呼ぶ。
ちなみに、スティフネス制御、剛性制御も同じ意味である。
rvalue, lvalueについて
なんだかあまり使わないからすぐに忘れるんですよね。ということで大事なところだけ書いておこう。
まず、rvalueってのは一時的に生成される無名のオブジェクトのこと。lvalueっていうのは実体のある名前付きのオブジェクトのこと。
rvalue referenceというのもある。これは、もう使わないからポインタすげかえちぇばいいや、ってときに使える表現。
rvalue は一時的でそれ以上使わないから、あるオブジェクトをrvalue referenceで受け取ってもらってそのポインタもらっちゃっていいよ~っていう意思表示として使う。
そのrvalue referenceで受け取るというのを表現するのにstd::move()を用いる。
また、argument deducationというのもあり、template
overload関数の増大を阻止。
最後に、argument deducationゆえに、もしtemplate
だから、引数をそのまま、rvalueならrvalue、lvalueならlvalueとして伝搬したいときがあるだろう。そこで使うのがstd::forwardである。
と、肝心なのはこれだけらしい。
CPUアーキテクチャ
x86_64とかamd64とかaarch64とか色々聞いて頭がおかしくなりそうなのでまとめる。あっているかは知らん。
CPUアーキテクチャの種類
- 命令セットアーキテクチャ
これはハードウェア全体の、ユーザーから見たインターフェイスのこと。データ型だとか命令セットだとかそういう話。いわゆるアーキテクチャと呼ばれる部分。
例えばx86とかamd64とかIA-32とかがこれに当てはまる。x86というのは、初期のintelプロセッサの名前が80286,80386,386と続いたことから80x86 -> x86へとなった。そしてx86の64bit拡張としてx86_64(x64)が生まれた。これにはAMD社が発表したamd64、inteleが発表したEM64Tなどを含む。x86とIA-32というのはx86とほぼ同義であるが、x86がamdなど他社の互換性のあるプロセッサなども含めた総称であるのに対して、intelのオリジナルだけを指すことが多い。IA-64はもはや全く互換性はなく限定的な要素でしか使われてない。
- マイクロアーキテクチャ
これは命令セットアーキテクチャよりさらに下位の各マイクロプロセッサの実装に関する部分のこと。パイプライン処理だとかキャッシュだとかそこら辺。例えばIA-32intelアーキテクチャとかCoreアーキテクチャとか。より具体的には、intel core iシリーズの第4世代であるHaswellとかそういうこと。
- システムアーキテクチャ
これは他のコンピュータのハードウェア全般に関する部分。
- ARMアーキテクチャ
これは命令セットアーキテクチャの一つ、つまりx86とかと対比するものですが、別に書いてみました。x86はCISCなのに対して、ARMはRISCを採用しています。x86は高性能、ARMは安価で低消費電力、なのです。今まではPC市場ではIntelが独占してきましが、今後は組み込み系で活躍してきたARMの進出も期待できるそうです。実際最近のスマホとかタブレットはarm多いですし。ちなみに、arm64とaarch64というのがありますが、元々別々だったのですが、ひとつの公式なものにマージされてaarch64になるそうですね。
リンクとかマウントとかコンテナとか
mountとかlinkのあたりの話がだんだんとごっちゃになってきてしまったので一回まとめます。
ファイルシステム
ファイルシステムっていうのはディレクトリがあって、そこにディレクトリエントリと呼ばれるファイルなどの情報があります。ディレクトリエントリからファイルのinodeにアクセスして、その先のファイルの実体にアクセスします。
ハードリンクとシンボリックリンク
これらリンクは結局は、別名でアクセスできるよ〜というものです。ハードリンクは別々のディレクトリエントリが同じinodeを参照することでこの別名アクセスを実現しています。つまり、dentry -> inode -> 実体 という構成の中で、inodeは共通でdentryのみがことなっているのです。これは
$ echo "nyan" > test1 $ ln test1 test2 //シンボリックリンクなら -s をつける $ ln -li test1 test2
とやってinodeの値が一緒になることで確認できます。inodeはファイルシステムごとに違うため、別々のファイルシステム間でハードリンクを作ることはできません。また、基本的にはファイルに関するハードリンクしかできなく、ディレクトリは微妙っぽいです。また、ハードリンクはinodeを参照しているため元のファイルを移動しても全然大丈夫で、元のファイルを削除しても作成したハードリンクは壊れません。これは、inodeがいくつのエントリから参照されているかを管理していて、それが0になったらファイルが削除されるからです。
シンボリックリンクはパス、つまり/home/hogeとかを参照することで別々アクセスを実現しています。パスで参照するため、別々のファイルシステムでも大丈夫であり、ディレクトリに対しても適用することが可能です。パスで指定するため、実体は一緒ですがinodeは異なっています。これはハードリンクの項と同じように確認できます。パスで指定することによるデメリットとして、元のファイルの場所が移動するとシンボリックリンクは壊れてしまいます。元のファイルを削除するとシンボリックリンクは残りますが、壊れて使えません。
こんな感じですが、アーカイブにするときも注意が必要で、tarなら基本的にハードリンクもシンボリックリンクも考慮してくれ、元のファイルを含まずシンボリックリンクだけのときでも -h オプションで実体を取り込んでくれるそうです。zipはハードリンクは考慮してくれないそうです。
mount - bind mount
mountとは一般に、外部デバイスをOSに認識させて使えるようにする行為だが、bind mountというとても便利なものもあります。bind mountはディレクトリをディレクトリにmountすることが可能です。簡単に言うと、シンボリックリンクの強いバージョンです。なぜシンボリックリンクより強いかというと、例えばchroot環境での作業が挙げられます。chrootとはファイルシステムのルートを変えることによって外部にアクセスできないように安全に作業を行ったりするときに使いますが、シンボリックリンクはパス指定なため当然外部へのアクセスができなくなってしまいます。それに対して、bind mountはディレクトリエントリ下で実現しているためできるそうです。bind mountは所詮mount --bindというmountのオプションなので、/etc/fstabに記述することが可能です。/homeと/varだけは外付けのHDDに格納しよう、とか、一旦ファイルをコピーして書き換えてbind mountしてテスト、とかが使い道っぽいです。
コンテナ
chrootが挙がりましたが、他にもdockerとかlxcとかありますよね。chrootは基本的に他のディレクトリの部分に侵食されないように制限をかけるものですが、lxcはさらに隔離します。その技術がcgroupとnamespaceで、cgroupはcpuやmemoryなどの物理的なリソースを隔離するための機能、namespaceはOSが使用するリソースであるネットワークスタックやPIDを隔離するための機能です。それに対してdockerはlxcにバージョン管理や自動ビルドなどの機能を追加したエコシステムだそうです。
std::future, promise, async, packaged_task…使ったことなし。
C++、人が使ってるのは見たことあっても実際に使ったことのないもの多すぎですね。
今回はstd::future, std::promise, std::async, std::packaged_taskについて書きます。
std::futureは、非同期操作の結果にアクセスするためのメカニズムを提供するもの。単体では使いません。多分。
std::promiseは、std:futureとstd::threadと一緒に用いて非同期操作を実現するもの。非同期に取得した値を格納するのに使う。
std::asyncは、非同期に関数を実行し、その結果を出力するstd::future。
std::packaged_taskは、std::asyncと似ているが少し違う。非同期実行の結果をstd::futureに書き込むもの。
ごちゃごちゃ言ってないでコードを見てみましょう。
std::promiseを使う例
#include <iostream> #include <thread> #include <chrono> #include <future> int main() { std::promise<int> prms; std::future<int> ftr = prms.get_future(); std::thread th([&]() { int sum = 0; for(int i=1; i<=10; i++){ sum += i; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } prms.set_value(sum); }); std::cout << ftr.get() << std::endl; th.join(); return 0; }
まず、格納したい値の型をテンプレート引数としたpromiseを作成します。そして、そのpromiseが将来的に返すであろう、futureを取得しておきます。
非同期で勝手にスレッドを回しておきます。futureのgetメソッドを呼び出した時には、futureに値が入ってくるまでブロックします。これだけです。
スレッドはとりあえず回しておいて、値が欲しくなったらfuture::getを呼びだせばいいわけです。便利。
ちなみに、future::getは一度しか呼び出せない。もし何度も呼び出したいのであればfuture
std::asyncを使う例
#include <iostream> #include <thread> #include <chrono> #include <future> int main() { std::future<int> ftr = std::async(std::launch::async, [&]() { int sum = 0; for(int i=1; i<=10; i++){ sum += i; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } return sum; }); std::cout << ftr.get() << std::endl; return 0; }
前のpromiseを使った例の簡単バージョンみたいになる。前ではpromiseを用意して、threadでpromiseを設定してfutureからgetする、という感じだった。
しかしasyncを使えば、returnした値をfutureの中にそのまま格納してくれるため、スレッドを作る必要も、promiseを用意する必要もない。簡単!
asyncにはpolicyというのがあって、std::asyncの第一引数の値に
- std::launch::asyncを指定すると別スレッドで実行
- std::launch::deferredを指定すると遅延評価
- 何も指定しないと上記どちらになるかは実装依存
となる。
std::packaged_taskを使った例
#include <iostream> #include <thread> #include <chrono> #include <future> int main() { std::packaged_task<int()> ptask([]() { int sum = 0; for (int i = 1; i <= 10; i++) { sum += i; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } return sum; }); std::future<int> ftr = ptask.get_future(); std::thread th(std::move(ptask)); std::cout << ftr.get() << std::endl; th.join(); return 0; }
なんとも微妙…。packaged_taskに非同期で結果が欲しいタスクを登録する。そしてそのfutureを取っておく。packaged_taskをthreadで実行しておいて、future::getで値が入るまでブロッキング。
あまり用途が理解できないが、汎用性がある?w
まぁいいや
condition_variable使ったことなし。
そういえばcondition_variableとかってなんだかんだ使ったことないなぁ、と思って書きました。
condition_variableとは、"条件変数"のことで、ある条件が満たされるまでスレッドを待機させるのに使います。
同期プリミティブとか言うんだって。
これがあれば、この条件が満たされるまでは〜〜をして、あの条件が満たされるまでは〜〜をして、みたいなマルチスレッド処理が可能になります。
condition_variableを使うときに使用するのがunique_lockであり、これはlock_guardみたいにコンストラクタでlockしてデストラクタでunlockするだけ!みたいな簡単なものではなく、むしろ色々、なんでもできるようにしてしまったmutexです。
unique_lock - cpprefjp C++日本語リファレンス
ここを見ればわかる通り、所有権の移譲だとか、途中でlockしたりだとか、try_lockだとかができます。
とりあえずコードを見てみましょう。
#include <iostream> #include <thread> #include <condition_variable> #include <mutex> #include <chrono> int main() { std::mutex mtx; std::condition_variable cv; bool ready = false; std::thread prepare_thread([&](){ std::cout << "#prepare thread start" << std::endl; std::this_thread::sleep_for(std::chrono::seconds(3)); { std::lock_guard<std::mutex> lock(mtx); ready = true; } cv.notify_one(); std::cout << "#prepare thread finished" << std::endl; }); std::thread main_thread([&](){ std::cout << "#main thread start" << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); { std::unique_lock<std::mutex> uniq(mtx); cv.wait(uniq, [&]{return ready;}); } std::cout << "#main thread finished" << std::endl; }); prepare_thread.join(); main_thread.join(); return 0; }
これは、prepareスレッドで準備をして、終わったよ!と通知が来たらmainの処理を実行するような感じのものです。よくありそうですね。
ここではまず、prepare_threadが3秒のスリープに入ります。次に、main_threadは1秒待って、mtxのunique_lockを取得し、condition_variableのwaitに入ります。
このwaitでは、std::unique_lockとfunctionを受け取ります。そしてunique_lockをunlockし、通知が来るまで待ちます。通知が来たときにfunctionがtrueになっていたらunique_lockをlockし、終了します。簡単ですね。
あれ?もしnotify_oneよりwaitの方が後にきちゃったら、もう通知終わってるからwaitが抜けられなくない?と思うかもしれませんが、cv.waitを呼び出した時点で一度functionを評価するので、そのときにfunctionがtrueになっていれば大丈夫です。
んんん??これはもはやreadyの存在意義がわからない…。そうなんです。これはsprious wakeupと言って、勝手にスレッドが目覚める、つまり、擬似的に通知を受け取ったような状態になってしまうことがあるらしいので、それによってwaitを抜けないようにするために必要だそうです。
ちなみにnotify_oneとnotify_allというのがあるらしいのですが、allは眠っているスレッド全てに通知をいかせるためオーバーヘッドがのるとか。oneは最大で一つのスレッドのwaitしか解放しないらしい。
自主プロジェクト
機械系で自主プロジェクトなるものがありました。
ということで、動画とスライドを一応挙げたいと思います。
(本番音声がうまく動かなくて辛かった。)
ていうか、3分で発表するんだったら、こういう多機能系のものではなく、もっと視覚的にパッとわかりやすいものにすべきだったなぁと思いましたね。
まぁ個人的には割と頑張ったので良かったと思います。
はい。
ブラウザさえあればどこからでも触れます。
スマホからでもタブレットからでも動かしたり、喋らせたり、displayにメッセージを表示したり。
インタラクティブに色々してくれます。
しゃべったり踊ったり。
ほっぺたに表情や天気予報を表示したりしてくれます。
全く外に配線が出ていないのが売りです。(ビデオではバッテリーだけ外に出していますが)
そう!コンパクト!可愛い!ブラウザがあればどこでも!インタラクティブ!
ソースはこちらへ
github.com
スライド
www.slideshare.net
トマトロボット…
なんというか、トマトをロボットで収穫してきました。
今回のこの体験、かなり面白い意義を含んでいる気がします。
自然のものをロボットで取る、というのは恐ろしく難しいです。
それでいて必ずこれから多く出てくる話題でもあるんです。
ロボットの研究にどんなものがあるのか聞きました。
ロボットの制御や認識、機械学習などソフトウェア。
腱駆動ロボットなど、人に近いロボットの制御。
新しいロボットの作成などハードウェア。
インタラクティブなロボット。
結局何がやりたいのかを明確にすることが大切ですね。
今回で、自然とやりとりするロボットにはすごい興味を持ちました。
まぁどちらかというとこれは知能の研究の方に引っ張られるのかな。
とりあえず、やりたいことを探します。
arduinoとAQM1248Aをspi通信させてみた。
SPIを使ってarduinoとAQM1248Aを通信させて、U8glibを使って文字を出力させてみた。
せっかくSPIとか真面目に勉強したので色々メモを書いていきたいと思う
SPI通信について
- SPIは1対nの同期式シリアル通信で、基本的には基板間などの短い距離で使う通信方式
- Masterが1で、nがSlaveとなって通信を行う
- 信号線は基本的には4種類あって、SCK(serial clock)、MOSI(Master out Slave in)、MISO(Master in Slave out)、SS(slave select)
- Slaveがいくつかある場合は、MasterにSlave分だけSSを用意して、それぞれをSlaveのSSとつなぐ
- MasterはSSから常時Highを出していて、通信したいSlaveにつながっているSSをLowにすることでそのSlaveと通信を始める。
- MasterとSlaveはシフトレジスタによってリング状に接続される。Masterは上位1bitを送りshiftし、同時にSlaveはそれを下位1bitに入れ、上位1bitをMasterに送りshiftする。これを8bit繰り返して、MasterとSlaveのレジスタ値をswapする。
AQM1248Aについて
- 超小型グラフィックLCDである。安い。変換基板を今回はそのまま買った。
- 付属の説明書を読めば正直つなぎ方はわかるが一応。(LCD -> arduino)
- VDD -> 3.3V | CS -> 10(SS) | RESET -> 8 | RS -> 9 | SCLK -> 13(SCLK) | SDI -> 11(MOSI) | GND -> GND
- U8glibを使いたいのだが、残念ながらこのハードウェアには対応していない。ただ、同じコントローラICであるST7565を搭載したNHD-C12864のコンストラクタを流用できるそうなので使ってみる。NHD-C12864は128x64dotであり、AQM1248Aは128x48dotなので、U8glibにおいて16bitまでは使用できない。よって16~64のみを使用して描画してほしい。
- https://code.google.com/p/u8glib/wiki/u8glib?tm=6 のProjectHomeからdownloadし、arduinoのライブラリを使用-> add libraryでそれを選択する。
- 使い方についてはReference Manualにすべて書いてあるからそれを見ちくり
一応U8glibを使わずに表示するsampleと、U8glibを使ってMerry Christmasと表示させるsampleを貼っておきます。
#include <SPI.h> #define RS_PIN 9 #define CS_PIN 10 #define RATE 10000000L void LcdCommand(uint8_t cmd) { digitalWrite(CS_PIN,LOW); digitalWrite(RS_PIN,LOW); SPI.transfer(cmd); digitalWrite(CS_PIN,HIGH); } // LcdCommand void LcdData(uint8_t dat) { digitalWrite(CS_PIN,LOW); digitalWrite(RS_PIN,HIGH); SPI.transfer(dat); digitalWrite(CS_PIN,HIGH); } // LcdCommand void InitializeLcd() { SPI.setBitOrder(MSBFIRST); SPI.setClockDivider(SPI_CLOCK_DIV4); //default SPI.setDataMode(SPI_MODE3); LcdCommand(0xae); LcdCommand(0xa0); LcdCommand(0xc8); LcdCommand(0xa3); LcdCommand(0x2c); delay(2); LcdCommand(0x2e); delay(2); LcdCommand(0x2f); LcdCommand(0x23); LcdCommand(0x81); LcdCommand(0x1c); LcdCommand(0xa4); LcdCommand(0x40); LcdCommand(0xa6); LcdCommand(0xaf); } // InitializeLCD void setup() { pinMode(RS_PIN, OUTPUT); pinMode(CS_PIN, OUTPUT); digitalWrite(CS_PIN,HIGH); SPI.begin(); InitializeLcd(); } void loop() { for(int page=0; page<6; page++) { LcdCommand(0xb0+page); LcdCommand(0x10); LcdCommand(0x00); for(int x=0; x<128; x++) { LcdData(0); } } delay(1000); for(int page=0; page<6; page++) { LcdCommand(0xb0+page); LcdCommand(0x10); LcdCommand(0x00); for(int x=0; x<128; x++) { LcdData(x); } } delay(1000); }
#include<U8glib.h> #define ADAddr 0x48 int top = 16; int buttom = 64; U8GLIB_NHD_C12864 gd(13, 11, 10, 9); void setup() { gd.setContrast(0); gd.setRot180(); gd.setColorIndex(1); gd.setFont(u8g_font_unifont); delay(1000); } void loop() { gd.firstPage(); do{ drawPicture(); } while(gd.nextPage()); } void drawPicture() { gd.drawStr(0, 48, "Merry Christmas!"); }