MATLAB的思維風格
最近做course project的時候,我發現所寫的程序中有幾個地方特別能反映MATLAB與別的高級語言很不一樣的風格。
例1:一批user去訪問文件,比如u1訪問了f11, f12, ..., u2訪問了f21, f22, ...,每個用戶訪問的文件數目可能不等,要求輸出一個表M, M(i, j)是用戶 i 和 j 都訪問過的文件的個數。
先用高級語言給出一個比較直接的解決吧,下面是python的代碼。(其實C++/C#/Java的也類似,不過python的代碼比較簡潔)
首先,建立一個訪問字典(或者叫map),記錄每個文件都被哪些人訪問過。
# input is a map named ufiles,
# ufiles[u] is a set of files accessed by u
acc_dict = {}
for u in users:
for f in ufiles[u]:
if f in acc_dict:
acc_dict[f].add(u) # add u to an existing set
else:
acc_dict[f] = set([u]) # initialize the set
然後,基於這個字典進行計數。對每一個文件,給每一對訪問過它的用戶的相應計數加1
# initialize the table
M = {}
for u1 in users:
for u2 in users:
M[(u1, u2)] = 0
# do counting
for f in acc_dict:
uset = acc_dict[f]
for u1 in uset:
for u2 in uset:
M[(u1, u2)] += 1
這樣就基本完成計數了。
MATLAB沒有像python那樣豐富的數據結構,比如set(集合)和dict(映射表,字典)。但是,在這個表面上需要依靠經典數據結構完成的問題,我們可以用MATLAB給出一個更加高效簡潔的解決方法
給出代碼之前,先做點數學分析。令F是一個矩陣,F(u, f) = 1代表u訪問過這個文件,否則F(u, f) = 0。那麼M其實就可以寫成:
M(u1, u2) = sum_f F(u1, f) F(u2, f) --- 很容易看出,這就是u1, u2都訪問過的文件的數目。寫成矩陣形式:
M = F * F'
下面的代碼就很順理成章了
% Input: ufiles is a cell array of indices of files accessed by each user
% ufiles(i) is a row vector of the indices accessed by user i
% construct F
n = length(ufiles)
% to produce usrs like {[1 1 1 1 ...], [2 2 2 ...], ...}
usrs = arrayfun(@(i) i * ones(1, length(ufiles{i})), 1:n, ...
'UniformOutput', false)
I = [usrs{:}] % concatenate the indices into one long vector
J = [ufiles{:}]
F = sparse(I, J, 1);
% compute M
M = F * F'
注意這裏用sparse matrix, 那麼運算複雜度會顯著降低,並且只依賴於實際訪問的數量。實際性能上,通過MATLAB的稀疏矩陣乘法比自己用高級語言實現更快。
熟悉kernel的朋友可能已經看出來,MATLAB的做法其實是用file access indicator vector作爲每個user的feature,構造出來的M則是相應的Gram matrix。不過,如果沒有這方面的思維習慣的話,未必能很快就把一個統計文件訪問量的問題聯想到矩陣乘法或者內積計算。
例2:給出一組數字,算出每個不同數字出現的次數。
python的實現
counts = {}用高級語言實現,一般需要一個映射表來快速定位某個數字的計數器。
for v in values:
if v in counts:
counts[v] += 1
else:
counts[v] = 1
Matlab的實現
svalues = sort(values);
dp = find(diff(svalues));
sp = [1, dp+1];
ep = [dp, length(svalues)];
U = svalues(sp); % U is the set of distinct values
C = ep - sp + 1; % C is the corresponding counts of these values
一般情況下,兩種實現的時間複雜度都是O(n log(n))。不過,它們體現了不太一樣的思維方式。高級語言傾向於通過數據結構加速每個元素的處理,而MATLAB沒有複雜的數據結構,它注重於以整體方式進行操作。