使用環境
MATLAB R2017a
課題
例えば
v = [1,2,3,4,2,2,1];
という配列があった時に、1が2つ、2が3つ、、というように数え上げるとします。
意外と分かりやすい名前の関数がなく、泣く泣くforループでプログラムを組んでいました。こんな感じ。
v = [1,2,3,4,2,2,1];
vq = unique(v);
N = length(vq);
numvq = zeros(N,1);
tic
for ii=1:N
numvq(ii) = sum(v==ii);
end
toc
やりたいことは実現できているんですが、配列数が巨大になると時間がかかるし、なにより美しくない。
そこで、forループを使わずに実現できないかと調べた3つの方法を紹介します。
それぞれの方法で計算にかかった時間の目安として、手元のlaptopで tic/toc を使用して計測した時間を書いておきます。
関数名 | 処理時間 |
---|---|
arrayfun | 0.16 秒 |
accumarray | 0.0013 秒 |
histcounts | 0.0016 秒 |
for ループ 使用した場合は 0.18 秒程度でした。accumarray 関数いいですね。
実行時間計測のために、1から1000までの整数をランダムに持つ 10万 x 1 のベクトル v を作って、それぞれの整数が何回現れるかを求めてみました。
以下それぞれのコードです。
arrayfun 関数
N = 1e5;
v = randi(1000,N,1);
numvq = arrayfun(@(x) sum(v == x), unique(v));
これは for ループは避けられましたが、速度はいまいち。
accumarray 関数
N = 1e5;
v = randi(1000,N,1);
numvq = accumarray(v,1);
関数の挙動を理解するのに時間がかかりましたが、計算速度はこれが最も速かった。
histcounts 関数
N = 1e5;
v = randi(1000,N,1);
numvq = histcounts(v,'BinMethod','integers');
histcounts 関数は R2014b で導入された比較的新しい関数ですが、このオプションは意識したことありませんでした。
参考
Q&AサイトMATLAB Answersより