ウィンカーリレー

バイクのウィンカーが常点灯になってしまった。H氏に相談したところ、こんな時は電球を換えれば治ることが多いらしい。リレーでやっているとしたら電球は切替回路とは独立じゃないのかなあ。電球交換で直る理屈が分からない。しかし換えたら確かに治ってしまった。なぜだろうか。

まずは、ウィンカーの回路の仕組みを考えることにした。コンデンサが云々というのをヒントに自力で考えたところ、リレー2個コンデンサ2個で組めるという所までは考えついた。しかし正解は、巻数の違うコイル2本とコンデンサ1個を使ったリレーで実現してしまうらしい。正解の回路は http://xj750e.cocolog-nifty.com/blog/2010/02/post-d020.html を参照したが、どうも回路図が間違っている。また、動作の説明に中途半端な所があるので、修正を考えて書き直した。

ウィンカーリレー

R2は左電球、R3は右電球である。L1の右の接点はb接点である。つまり、L1にもL2にも電流が流れていないときは、接点はバネの力で閉じている。接点には磁石がついている。コイルL1に電流が流れると接点を開く方向に力がかかり、コイルL2に電流が流れると接点を閉じる方向に力がかかる。L2の方が巻数が多いので、両方に同じだけ電流が流れた場合は、接点は閉じたままである。


さて、今、左ウィンカーを出すべく3接点スイッチをR2に切り替えたとする。すると、回路に電流が流れ、左電球が点灯する。最初はL1とL2にほぼ同じだけ電流が流れるので、接点は閉じたままである。C1に電荷が溜まると共に、C1-L2の所にはあまり電流が流れなくなってくる。じきにL1が接点を開く力の方がL2とバネが接点を閉じる力より強くなり、接点が開く。L1とR2には電流は流れなくなり、左電球は消灯する。一方L2には、C1からの放電によって引き続き電流が流れるのだが、この電流は、今までとは向きが逆である!したがって、L2は、今までとはうって変わって接点を開く方向に力をかける。こうして、C1の電圧が下がりL2が接点を開く力の大きさがバネの力の大きさを下回るまで、電球は消灯を続ける。接点が閉じた時点で、電球がまた点灯し、一連の動きの繰り返しとなる。

なお、接点が閉じる時点ではコンデンサ電荷が少し残っているため、2回目以降の点灯時間は一番最初より少しだけ短くなる。

常点灯が電球交換で直る仕組み

電球交換で起きることは限られている。一番ありそうなことは接点の改善による抵抗値の減少である。電球と切替回路が直列だったので、R2が大きくなれば切替回路に流れる電流が減る。それで、L1が接点を開く力がバネの力より弱くなってしまったのかもしれない。バネがそこまで強いとは思えないので、接点の潤滑が悪くなって余計な力が必要になっているのかもしれない。抵抗がどれだけ増して電流がどれだけ減っているのか、テスタで調べてみれば分かるだろうが、大変である。今回は、ここまで理解できた事をもってよしとした。

僕らの履物戦争

トイレにて、履き物の入船率が高い。
初めは、周りは入船派ばかりか、と訝しんでいたのだが……。

出船数の増減

トイレに入る際、出船と入船両方の履物があれば、出船を使うのが人の常である。
出船で脱ぐ人(出船派)も、履く時に両方有ればわざわざ入船を選んで履くことはない。
したがって、出船数の増減数は下表の通り。

出船数≧1 出船数=0
出船派 ±0 +1
入船派 -1 ±0

まとめ

入船で脱ぐ人が一人でもいる限り、出船数は0と1の間で振動する。

『出船で脱いで、皆で快適にトイレを使おう』と大半の人が思っていたとしても、
入船派がたった一人居るだけで*1出船派の思惑が台無しになってしまう。
出船派がこれを覆すには、『入船を選んで履き、出船で脱ぐ』という自己犠牲を行わねばならない。


出船派の思惑が叶えられる日は遠いようである。

*1:なお、入船派の比率は、出船数0への遭遇率で判定できる。(ただし生活時間の偏りや利用頻度の偏りはないものとする)

explicit復習

後輩にexplicitを説明してもすぐにはピンと来ない様子だったので、具体例を作った。

#include <iostream>

class A{
  private:
  int a;
  public:
  explicit A(int x): a(x) {}
  void print() const { std::cout<<a<<std::endl; }
};

void func(const A hoge){ hoge.print(); }

int main(){
  A b(0);
  func(b); // 普通の呼び出し方。
  func(4); // explicitが無いとこの呼び出しが通り、コーディングミスの温床になる。
}

このように型変換の話なので、1変数以外のコンストラクタにはexplicitは不要。当然、同クラスを引数に取るコンストラクタにも不要。

FFTW3の謎の挙動

下記のようなプログラムを実行すると、out[1]からout[3]はちゃんと出るのだが、out[0]の出力だけ総0になってしまう。data[i]の中身を入れ替えてみてもout[0]だけ総0。なぜなんだ。

→解決しました。プラン作成時に入出力メモリが破壊されるのが原因。忘れてた…。ちゃんとconst使えってことなんですが、FFTW使ってると『ここは非constだけど非破壊』みたいなのをつい使ってしまい、だんだんいい加減に…

環境: ubuntu 10.04 1 LTS, g++ 4.4.3, fftw3 3.2.2-1

#include <complex>
#include <iostream>
#include <cmath>
#include <fftw3.h>
#include <valarray>

using namespace std;

template <typename T>
T round2zero(T x){ return abs(x)<pow(2.0, -47) ? 0 : x ; }

template <typename T>
void print(const T &v){ for(unsigned i=0; i<v.size(); ++i){ cout<<round2zero(v[i])<<", "; } cout<<endl<<endl; }

int main(int argc, char **argv)
{
  const unsigned num=4, size=32;
  valarray<double> data[num];
  valarray<complex<double> > out[num];
  
  for(unsigned i=0; i<num; ++i){
    data[i].resize(size);
    out[i].resize(size);
    // data[i][j]に波数(j+1)のsin波を代入
    for(unsigned j=0; j<size; ++j) data[i][j]=sin(2*M_PI*(i+1)*j/static_cast<double>(size));
    print(data[i]);
  }
  
  fftw_plan p[num];
  for(unsigned i=0; i<num; ++i){
    p[i] = fftw_plan_dft_r2c_1d(size, &data[i][0], reinterpret_cast<fftw_complex*>(&out[i][0]), FFTW_MEASURE);
    fftw_execute(p[i]);
    print(out[i]);
  }
}

天災に備えて遠隔地バックアップを取ろう!

無くなると困るデータが年々増えてます。
別HDD等にバックアップを取るのはもちろんのこと、
天災に備えて遠隔地にもデータを持っておきたいもの。

私は知人宅に1台置いていますが、鯖を立ててあるわけでもなく、データの更新がなかなか難しいです。
そこで、オンラインストレージサービスSugarSyncを使うことにしました。
5GBの無料ストレージがあり、常時同期されるフォルダと上げっぱなしのバックアップ専用フォルダとで使い分けができるようです。任意のフォルダを好きなだけ同期フォルダに指定できます。便利です :-)

追加容量500MBつきのSugarSync登録リンクはこちら:
https://www.sugarsync.com/referral?rf=dxko37wvbghgg

コンテナベンチマーク

いくつかのコンテナについて、アクセス時間を測定した。
92^4要素の頭から尻尾まで異なる値を代入するのにかかった時間を64回計測し、中央値を採用。
Ubuntu 10.04 (LTS) 64bit, Core i7-960, g++ 4.4.3, -O3

multi_array
A.data()[raw] 0.585003
A[i][j][k][l] 0.688783
A[j][i][k][l] 0.727693
A[k][j][i][l] 0.78196
A[l][k][j][i] 2.3746
A.data()[((i*d+j)*h+k)*w+l] 0.690925
A.data()[((l*d+k)*h+j)*w+i] 2.3912
A.data()[((l*d+j)*h+k)*w+i] 2.35292

さすがに1変数で回すのが最も速い。4変数で回す場合、自前のアドレス計算でもmulti_array任せでも同じ。forループはi,j,k,lの順に回している。アクセスがメモリ上連続になるようにforループを組んでおかないと、かなり性能が落ちているのが分かる。

ちなみに最適化無しだと A.data()[raw] が0.87秒、A[i][j][k][l]が12秒。

vector >
A[i].data()[j] 0.725252
A[i][j][k][l] 1.0169

multi_arrayをvectorでくるむと、少し遅い。それぞれ、1.24倍と1.48倍。

valarray, 4重valarray
A[raw] 0.656968
(&A[0])[raw] 0.712089
A[i][j][k][l] 0.750151
vector, 4重vector
A[raw] 0.644847
(&A[0])[raw] 0.689829
A[i][j][k][l] 0.758916

valarrayとvectorは似たようなもの。

…multi_array.data()[raw]が一番速いのはなんでだろ

すべてを指導する | Everlead

先日、研究室で「見捨てるのが本人のため」と言った。しかし思い返してみると誤解を招きかねない表現だったので、補足しておく。

これは、指導者は何もするな、ということではない。どちらかといえば、「過干渉はやめるべきだ」というのが近い。

生徒に対して「そんなんでうまくいくわけねーだろ、こうしろボケ」と言って行動を強制すると、本人が考えて選択する機会を奪う。結果、「行動すれども学ばず」となる。指導側の人望によっては、生徒の「俺は奴のくだらない思い上がりのために不当な指導を受けている」といった反発を招く可能性もある。

それよりも、「そんな装備で大丈夫か?」と言うべきだ。「大丈夫だ、問題ない」で終わるのが怖ければ、「そのままだときっとこういう事になると思うよ」あるいは「こういう事になりかねないよ」と言っておくのもいい。これは結果の予想を伝えているだけで、行動の強制ではない。

いずれにせよ、言及(mention)に留めることが肝心だ。「愚者は経験に学び、賢者は歴史に学ぶ」という。言及で失敗を回避できる生徒は、失敗を経験する必要はない。言及があってもそのまま失敗するような生徒には、失敗させる必要がある。失敗から学ばせる必要がある。

こんな指導方針で大丈夫か?