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没有复杂的数据结构,它注重于以整体方式进行操作。