2007年10月5日金曜日

matlab: なんちゃって構造体

空の構造体は

>> a = struct([])
a =
0x0 struct array with no fields.

とすれば簡単に作れる.では,「フィールドを持たないけど空ではない構造体」はどうすれば作れるか?

すぐに思いつくのは,rmfield を使って,フィールドを消して行くやり方だろう.

>> b.foo = []; a = rmfield(b, 'foo')
a =
1x1 struct array with no fields.

これはかっこ悪いので却下.だが実現は出来ることがわかる.
では,rmfield はどうやってこれを実現しているか?
rmfield は実は組み込み関数でなく .m 関数なので,type rmfield して中身をのぞくと,どうやら cell2struct を呼び出しているらしい.
ならばはじめから cell2struct を使えばいいのである.しかも cell2struct は組み込み関数だから,わりと根源的なやり方といえるだろう.

>> a = cell2struct(cell(0,1),{})
a =
1x1 struct array with no fields.

おお,できた.これでいいや.

しかし,恒例の whos でみると

>> whos a
  Name      Size                    Bytes  Class
  a         1x1                         0  struct array
Grand total is 0 elements using 0 bytes

のように,メモリが割り当てられていないではないか.しかも a の repmat で b を作ってみても,b も Bytes は 0 のままである.

実際にメモリを割り当てるにはフィールドを定義しないといけないらしい.AMD64 な Linux 上で AMD64 な MatlabR2006a を使った場合,

>> b = repmat(a,[100,1]);
>> whos b
  Name      Size                    Bytes  Class
  b       100x1                         0  struct array
Grand total is 0 elements using 0 bytes
>> b(1).foo = '';
>> whos b
  Name      Size                    Bytes  Class
  b       100x1                       952  struct array
Grand total is 100 elements using 952 bytes

となるが,同じことを IA32 な Windows 上で IA32 な Matlab6.5.1 を使って行った場合,

>> b=repmat(a,[100,1]);
>> whos b
  Name      Size                   Bytes  Class
  b       100x1                        0  struct array
Grand total is 0 elements using 0 bytes
>> b(1).foo = '';
>> whos b
  Name      Size                   Bytes  Class
  b       100x1                      520  struct array
Grand total is 100 elements using 520 bytes

となる.なんで違うんでしょう.




2007年8月19日日曜日

oblivion: なんとなく screenshot: 1


Sophia ※がある人を Cloud Ruler Temple に連れて行くと,集合した Blades の前で演説が行われた.

※Sophia = プレイヤー



2007年8月13日月曜日

matlab: なんちゃってcomplex

matlab (今回は 6.5.1 でしか試してません)は複素数について直感に反する挙動を示すことがある.例を見てみよう.

>> A = repmat(complex(0),[10,1]);
>> whos A
  Name      Size                   Bytes  Class
  A        10x1                      160  double array (complex)
Grand total is 10 elements using 160 bytes
>> A(end) = complex(1,0);
>> whos A
  Name      Size                   Bytes  Class
  A        10x1                       80  double array
Grand total is 10 elements using 80 bytes

complex じゃなくなってる.およ?一応確認すると,A に代入した値は実数でなく複素数と認識されているはずだ.

>> clear a; a=complex(1,0); whos a
  Name      Size                   Bytes  Class
  a         1x1                       16  double array (complex)
Grand total is 1 element using 16 bytes

となるからだ.ただし,入力引数に対する inputname のように,出力引数についての情報取得機構が関数 complex 内で働いているとすると,よくわからない話になるかもしれない.

一つ考えられる仮説は,「虚部が全て 0 の配列は実数型にされてしまう」というものだ.実際,その方がメモリ効率やら計算効率やらよくなる気はする.

念のため,虚部が 0 でない複素数を代入すれば,配列は実数型に変換されることは無い.

>> A = repmat(complex(0),[10,1]);
>> A(end)=complex(0,1);
>> whos A
  Name      Size                   Bytes  Class
  A        10x1                      160  double array (complex)
Grand total is 10 elements using 160 bytes

そして,要素を虚部が 0 の複素数に順に置き換えていくと,

>> A=complex([1,2],[3,4]);
>> A(1)=complex(real(A(1))); whos A
  Name      Size                   Bytes  Class
  A         1x2                       32  double array (complex)
Grand total is 2 elements using 32 bytes
>> A(2)=complex(real(A(2))); whos A
  Name      Size                   Bytes  Class
  A         1x2                       16  double array
Grand total is 2 elements using 16 bytes

となった.ここまでは前述の仮説で当たっていそうである.

しかし,もちろん話はこれで終わらない.次の例を見てみよう.

>> A=zeros(1,2);
>> A(:)=complex(0);
>> whos A
  Name      Size                   Bytes  Class
  A         1x2                       32  double array (complex)
Grand total is 2 elements using 32 bytes

なんと配列は複素数のままであり,これは前述の仮説と矛盾する.ところがさらに,代入の方法を変えると,

>> A=zeros(1,2);
>> A(1:end)=complex(0);
>> whos A
  Name      Size                   Bytes  Class
  A         1x2                       16  double array
Grand total is 2 elements using 16 bytes

となって前述の仮説が復活する.どうなってるのという話である.

ここで新たに,「全代入(whole substition とか?)」という概念を考えてみる.
全代入と考えるのは次の4つ.

  1. A = B;
  2. A(:) = B;
  3. A = B(:);
  4. A(:) = B(:);
変数間の代入で虚部 0 でも complex を維持するには,どうやら厳密にこの4形式しか許されないようだ.
たとえば2次元配列で A(:,:) としてもダメだったし,列ベクトルで A(:,1) としてもダメだった.
この概念を利用して立てた仮説がこれだ:
「全代入では愚直な代入が行われ,それ以外では値の取り出し・代入いずれかで可能なら虚部が 0 の複素数は実数に落ちる」
いまのところこの仮説で説明できるような気がする.
この他に,左辺が全参照(全代入と同じ気分)で右辺が「適当な」式なら,やはり実数に落ちないようだ.以下の例を参照のこと.

>> A=complex(rand(3));
>> A(:)=complex(0); whos A
  Name      Size                   Bytes  Class
  A         3x3                      144  double array (complex)
Grand total is 9 elements using 144 bytes
>> A(:)=complex(zeros(3)); whos A
  Name      Size                   Bytes  Class
  A         3x3                      144  double array (complex)
Grand total is 9 elements using 144 bytes

いや~.重箱の隅つつくといろいろ出てくるなぁ.
ちなみに,大方の予想通り

>> A=complex(rand(3));
>> A(:,:)=complex(0); whos A

  Name      Size                   Bytes  Class
  A         3x3                       72  double array
Grand total is 9 elements using 72 bytes
>> A=complex(rand(3));
>> A(:)=complex(zeros(3)) + 1; whos A
  Name      Size                   Bytes  Class
  A         3x3                       72  double array
Grand total is 9 elements using 72 bytes


となる.もういいって?
今回の現象は,subsref, subsasgn, subsindex と型キャストの実装のコンボで生じるんだろうけど,意図された仕様なんですかねぇ.



2007年8月11日土曜日

MM: 携帯動画の変換(mp4へ)

以前の記事にも書いた携帯で録画した動画を何とかするのの続き.
YAMB は MP4Box のフロントエンドであるらしく,Options->General->MP4Box->View or Edit MP4Box Command Line. にチェックを入れると,どうやって MP4Box を呼び出しているかがわかる.
これを調べると,次のようなバッチファイルでいけそうだ.

"C:\Program Files\YAMB\MP4Box.exe" -avi 1 "%~f1"
"C:\Program Files\YAMB\MP4Box.exe" -raw 2 "%~f1"
"C:\Program Files\YAMB\MP4Box.exe" -add "%~d1%~p1%~n1_track1.avi" -add "%~d1%~p1%~n1_track2.amr" "%~f1.mp4"
del "%~d1%~p1%~n1_track1.avi"
del "%~d1%~p1%~n1_track2.amr"

この内容でバッチファイルを作り,そのショートカットを Send To フォルダに入れておく.
すると,hogehoge.3g2 を右クリックし,「送る」で hogehoge.3g2.mp4 に変換できるようになる.
このバッチファイルは,w43h で録画すると出来る .3g2 を前提にしており,track2 が amr 形式の音声トラックだと仮定している.

バッチパラメータ( %~f1 等)についてはykr さんのページや,Windows 一般に共通なのかわからないけどMicrosoft TechNetのページなどを参照.

ちなみに,track 1 の抽出を -raw 1 とすると .cmp ファイルができる.これでも .mp4 へ mux できたが,なぜか後ろの方が再生できなかったりした..avi 経由だと問題なかった.なんで??

さて,あとは mpeg2 に変換して DVD に焼くところだ.



2007年6月12日火曜日

matlab: 呼び出しの調査

matlab 上で関数を呼び出すと疑似コンパイル(p-code)され,メモリに残る.
メモリ上の p-code buffer に存在する m-file の名前を見るには,inmem という関数を使う.
これで,実際に呼ばれた関数の一覧が得られる.

関数のコードを走査して再帰的に依存を調べるには,depfun という関数を使う.
depfun の賢さは限定的なので,もれなくわかると期待してはいけない.depdir も同様.

profile なんかも結構役に立つ.単に echo してみるのも面白いかもしれない.



2007年5月4日金曜日

MM: 携帯で録画した動画

MM はまるちめでぃあのつもり.

携帯(W43HにmicroSD 2GB)で録画した動画(3gpp2 ってやつ?)を,USB で PC にもってきた.
PRIVATE\AU\DF\D_MA に入っている .3g2 な動画を再生してみると,
結構長時間連続録画したはずなのに,mpc でも vlc でも,再生時間が15秒と認識されてしまう.
いわゆる断片化ということに関連した問題らしい.

いじれそうなツールとしてYAMBを使ってみた.
YAMB からは長時間の動画として認識されたので,
動画を avi に,音声を amr に抽出して,mp4 に remux.
無事に全時間分再生できた.

もちっとワンタッチなやり方があるといいんだけど.
MP4Boxを直に使ってバッチ処理とか出来るんだろうか.



2007年5月3日木曜日

ゲーム: Oblivion

Elder Scrolls IV: Oblivion というゲームがあります.

RPGです.洋ゲーです.
4Gamer.net のレビュー開発元販売元

いくつかのプラットフォームで出ています.
今度日本語版も出るそうです.

日本語の wiki は Oblivion Wiki JP というのです.
プレイ日記も結構たくさんあるのです.
Simらしのなく頃に+Oblivion日記
あたりが面白いのです.

ですです.です?です‥‥ですっ!


ということで,通販で買ってしまいました.PC 英語版.
PC なのは MOD を入れたいから.英語版なのは趣味で.
このために6年ぶりに PC を刷新したといってもイイ(いずれ書くかも).
メモリも積んだしCPUも変えた!
キーボードまで diNovo Edgeengadget japanese, スタパブログ, マイコミジャーナルのレビュー)にしちまった(いずれ書くかも)!

そして Oblivion をプレイしてみた.

やばいです.楽しいです.
下手すると今後の記事がひたすらプレイ日記になってしまうかもしれない.
どうしよう.



2007年4月16日月曜日

matlab: end+1

Matlab では,配列の最終要素のインデクスを表す end という指定法がある.
この end にまつわる小ネタ.

配列に次々要素を追加していく,という処理をスクリプト中に書く場合,番号を決め打ちする書き方

a(1) = 1;
a(2) = 2;
a(3) = 3;

をすると,順番を入れ替えたいときや,間に要素を挟みたいときに一大事になる.

そこで,こんな書き方が楽チンだ.

a(1) = 1;
a(end+1) = 2;
a(end+1) = 3;

これなら入れ替えや追加をコピペで簡単に行える.

ちなみにこう書いた場合,リストは横に,つまり2次元目に伸びていく.上の例では,a は 1 x 3 のサイズになる.
もともとが 3 x 1 だったりした場合,4 x 1 に伸びる.もともとが 3 x 2 だったりした場合,

>> a=rand(3,2);
>> a(end+1)=3
???  In an assignment  A(I) = B, a matrix A cannot be resized.

のように怒られる.この場合でも,伸ばす次元が明示的になるような書き方,すなわち

>> a=rand(3,2);
>> a(3,end+1)=3
a =
    0.1934    0.5417         0
    0.6822    0.1509         0
    0.3028    0.6979    3.0000
>>

のようにすれば,各次元についての end としてちゃんと解釈される.

実は上記の怒られ方は end に限ったことではない.つまり,

>> a=rand(3,2);
>> a(7)=3
???  In an assignment  A(I) = B, a matrix A cannot be resized.

とこういうことなのだ.しかし,

>> a=rand(3,2);
>> a(5)=3
a =
    0.3412    0.3093
    0.5341    3.0000
    0.7271    0.5681

とこういうことでもある.ここで a(end-1) としてももちろん同じ.結局,
  • linear index で問題ないものはそのように解釈される
  • linear index で曖昧さが出る状況では怒られる
  • end は,それが解釈される次元における末尾のインデクスの数字と単純に置き換え
というルールだと思っていいのだろう.

補足.もちろん,end+1 に限らず end+10 のような指定も可能です.



2007年3月28日水曜日

matlab: 構造体配列の参照法とその周辺

構造体配列も,普通の配列(数値配列や文字配列)とは少し違う.
どちらかというとセル配列ライクである.matlab: セル配列の参照法とその周辺を参照.
試してみよう.まず,構造体配列を作る.
>> a=struct('f',{'foo','bar'})
a =
1x2 struct array with fields:
    f
次に,小括弧で普通に参照してみる.
>> a(1)
ans =
    f: 'foo'
構造体が一つ返ってくる.さらに,インデクスリストで参照してみる.
>> a(:)
ans =
2x1 struct array with fields:
    f
構造体配列が返される.コロン単独 : で参照すると一次元ベクトル(縦に並んだ配列)が返されるのは,
Matlab のお約束の振る舞いである.ちなみに,1:end で参照すると,振る舞いが異なる.この話はいずれ.
問題はここから.構造体配列のフィールドを参照する場合の話だ.
単一要素の参照なら,
>> a(1).f
ans =
foo
であって全く普通だ.しかし!インデクスで参照してフィールドを指定すると,
>> a(:).f
ans =
foo
ans =
bar
となり,また単にフィールドを指定した場合にも
>> a.f

ans =

foo

ans =

bar

のように,セル配列の記事でも述べた,例の「複数の文を評価した状態」になる.
あとの話はセル配列のときと同様になるわけだが,構造体配列でありがちな例を挙げておこう.
例:一件のレコードが一つの構造体になっているような構造体配列で,特定のものを見つける.
>> clear a b c
>> a.label='small';a.value=100;b.label='small';b.value=200;c.label='large';c.value=1000;d=[a,b,c]
d =
1x3 struct array with fields:
    label
    value
>> small_ones = find(strcmp({d.label},'small'))
small_ones =
     1     2
>> under150 = small_ones(find([d(small_ones).value]<150))
under150 =
     1
てな感じ.連結 {} や [] を使って単純に書ける.
関数 deal を使うともっと幅が広がるかもしれない.例えば,上で定義した構造体配列 d に対して
>> [d.label]=deal('tiny','little','huge');d.label
ans =
tiny
ans =
little
ans =
huge
なんてこともできる.


2007年3月22日木曜日

matlab: セル配列の参照法とその周辺


※本記事を大幅に拡充して「matlab: セル配列で困っている人へ」を執筆しました.2013.06.10

セル配列は Matlab の配列の中でもやや異色である.
最も大きな特徴は,他の任意の型の変数(配列)を要素として扱えることだろうが,
もう一つ,中括弧がセル配列専用の参照演算子 {} として用意されているのも独特だ.
しかも,通常の参照演算子である小括弧 () でもアクセスできる.
そして,小括弧でアクセスした場合と中括弧でアクセスした場合には結果が異なるのである.
根本的には,小括弧は「セル配列の部分配列を取り出す」,中括弧は「要素セルに格納された中身を取り出す」と考えられる.
試してみよう.まず,セル配列を作る.
>> a={1,2};
次に,小括弧で普通にアクセスしてみる.
>> a(1)
ans =
    [1]
このとき,a(1) 自体はセル配列である.つまり,b=a(1) とすれば,b は 1 by 1 のセル配列となる.
さらに,インデクスリストでアクセスしてみる.
>> a(:)
ans =
    [1]
    [2]
ここでももちろん a(:) 自体はセル配列である.つまり,b=a(:) とすれば,b は 2 by 1 のセル配列となる.
では,中括弧でアクセスしてみよう.
>> a{1}
ans =
     1
このとき,a{1} は数値である.さらに,インデクスリストでアクセスしてみる.ここからが面白いわけですよ.
>> a{:}
ans =
     1
ans =
     2
なんと ans が 2 つ.つまり 2 つのセンテンスが評価されたことがわかる.
中括弧使用時のインデクスリストによるアクセスとは,一見単独の文に見えながら,複数の参照文をシリアルに実行しているのに他ならないのだ.
ということは,である.当然,この参照結果を一つの変数にそのまま代入することはできない.
>> b=a{:}
??? Illegal right hand side in assignment. Too many elements.
ただし b=a{1} のような単一要素の参照ならもちろん代入できる.
単純な代入が行えない,となると,中括弧でのインデクスリスト参照にはどんな使い道があるのか?
これがちゃんとあるのである.
matlab には,複数の文の評価結果を受け取る書式が存在する.
それは,小括弧 () と中括弧 {} と大括弧 [] の括弧 3 兄弟である.
ここでの小括弧 () は,関数呼び出しの引数指定に使う場合と,配列を参照する場合のものを指す.
例えば,
>> a={2,2};
>> b=[1,2;3,4];
>> b(2,2)
ans =
     4
>> b(a{:})
ans =
     4
となる.つまり,b(2,2) という指定の本質は,
「括弧内に 2 つの文があって,それぞれの評価結果が 2 になっている」
ということなのだ.だから,複数の文の評価をもたらす a{:} で置き換えることができた.
関数引数も同じ理屈だが,こちらはすぐにわかる実用性がある.varargin を利用して
function a = myfunc(varargin)
a = someotherfunc(varargin{:});
とすれば,即座にラッパーが書けるのだ.これは応用しやすい.
別に varargin でなくとも,一連の関数引数候補をセル配列にパックしておけば便利.多分.時々.
中括弧や大括弧は,複数の要素を連結するときに使う.
中括弧で連結すればセル配列になり,大括弧で連結すれば元の要素の属するクラスの配列になる.
このとき,要素を何で区切るか?といえば,カンマ , やセミコロン ; なわけで,これらも文の区切り記号であるところに注目である.
もっとも,連結するという文脈内では,カンマは 2 次元目連結,セミコロンは 1 次元目連結を示唆するものであって,
コンソールへの出力をする・しないという通常の文区切り時とはもちろん働きが違う.
セル配列の中括弧アクセスの場合,連結の括弧の中では全てカンマ区切りにみなされるらしい.
まとめると,セル配列への中括弧でのインデクスアクセスは,あくまで複数の参照文のシリアルな実行であるが故に,
元の配列の次元構成を保存できない.つまり,
>> a={1,2;3,4}
a =
    [1]    [2]
    [3]    [4]
>> {a{:,:}}
ans =
    [1]    [3]    [2]    [4]
となってしまうので気をつけよう.
多次元構成が保存されないことを除いてはなかなか便利に使える.例えば,数値のセル配列を数値配列にするには
b = [a{:}];
でいいわけだ.また,次元に関しては,代入先を予め設定しておいてから
b = zeros(size(a));
b(:) = [a{:}]'; %この転置はいるのか?手元の R2006a ではあってもなくてもエラーも出ないし結果も同じだった
のように代入するか,あるいは reshape を使って
b = reshape([a{:}],size(a));
などとすればよいだろう.
さて,セル配列のほかに不思議な振る舞いをする配列といえば構造体配列があるわけだが,こちらはまたの機会に.
こういった参照や連結の背景には subsref, subsasgn や subsindex といった参照用関数,そして cat, horzcat や vertcat といった連結用関数がある.
これらは matlab 上でオブジェクト指向プログラミングをしない限りあまり目にしないかもしれないが,背景として理解しておいてもいいと思う.

2013.06.10
本記事を大幅に拡充して「matlab: セル配列で困っている人へ」を執筆しました.


2007年3月18日日曜日

床屋: かゆいところは?

床屋さんで髪を洗ってもらっていると,「どこかかゆいところは?」と聞かれるわけです.
昔からよくネタになる質問ですね.

特に無い,と答えるのは何かに負けた気がするので,いつも無理矢理場所を捻出します.
右の前のほうとか左の真ん中ら辺とか,適当に.
すると,人によるでしょうが,そこら辺を指でわしゃしゃしゃっとしてくれると思います.

で,今日床屋さんに行ってきたのですが,今回は「ちょっと全体的に」と言ってみました.
曖昧な日本人らしい我ながら見事なぼかしっぷりです.
言ってから困らせたかという気もしましたが,まあ全体をわしゃしゃっとされるんだろうと思ったのです.
すると店員さんは,「じゃあ,」と慣れた調子で,「ブラシ通しましょうか.」とおっしゃいました.

ええ,ブラッシングされましたよ.全体的に.これが意外と気持ちよかったです.
人の指も気持ちいいと思いますが,それとはまた別の気持ちよさがありましたな.
昔飼ってた犬が,ブラッシングが好きだったことを思い出しました.
ブラシを持っていくと喜ぶ犬を見て,「ははは犬だなぁ」などと思っていましたが,
自分も同レベルだとは今日まで気付きませんでした.

あそこでブラシが出てくるのはよくあることなんでしょうか.自分としてはこれがはじめてでした.



2007年3月16日金曜日

matlab: 対角行列を効率的に生成

Matlab で対角行列の類を生成する場合がよくある.これを,メモリ・速度・コード量とも効率よく行うにはどうすればいいかという話.
普通の対角行列の場合.対角成分を並べたベクトルを w とすると,
W = diag(sparse(w));

が多分最良.
ブロック対角行列の場合はどうか.m by n の行列が k 個対角に並ぶ場合を考える.ブロック対角成分を並べた行列を B (m by n*k) とすると,
C1 = sparse(B);
C1(m*k,n*k) = 0;
C2 = reshape(C1,[m*k*n,k]);
C2(m*(k*n+1),k) = 0;
C = reshape(C2, [m*k,n*k+1]);
C(:,end) = [];

くらいでどうだろう.
blkdiag は引数の形式が馴染まないことが多いと思う.セル配列から展開するのか?あとは,単に自分が sparse や spdiags を使いこなせていないという噂もある.
※最後の行 20070424 に訂正した.bad->C(:,end-(n-1):end) = [];

行列からのブロック対角成分の抽出や,抽出した成分を用いてブロック対角行列を再構成する方法について,「matlab: ブロック対角成分の抽出」という記事も書きました.