我正在尝试解决以下问题,我需要尽可能高效地进行此操作(即尽量避免循环)。
我有两个单元格阵列,即A和B. A和B的每个单元格都包含一串字符。这些字符串的长度是可变的。让我们说:
A={‘life is wonderful’, ‘matlab makes your dreams come true’};
B={‘life would be meaningless without wonderful matlab’, ‘what a wonderful world’, ‘the shoemaker makes shoes’, ‘rock and roll baby’};
此外,单元阵列B的元素数量比单元阵列A大三个数量级。
我的目标是找到A中每个字符串中每个字符串的字数也出现在B的每个字符串中。
对于前面的示例,合适的结果可能是:
match = [2 1 0 0
1 0 1 0]
第一行表示A的第一个字符串中有多少单词出现在B的四个字符串中。第二行表示A的第二个字符串相同。
双循环实现很简单,但非常耗时,特别是因为单元阵列B的长度(超过300万个单元)。
有什么想法吗?非常感谢你。
济
答案 0 :(得分:2)
让我通过发布“直截了当”的解决方案(至少其他人有一个基线进行比较)来开始:
A = {'life is wonderful', 'matlab makes your dreams come true'};
B = {'life would be meaningless without wonderful matlab', 'what a wonderful world', 'the shoemaker makes shoes', 'rock and roll baby'};
count = zeros(numel(A),numel(B));
%# for each string
for i=1:numel(A)
%# split into words
str = textscan(A{i}, '%s', 'Delimiter',' '); str = str{1};
%# for each word
for j=1:numel(str)
%# count occurences
count(i,:) = count(i,:) + cellfun(@numel, strfind(B,str{j}));
end
end
结果:
>> count
count =
2 1 0 0
1 0 1 0
更好的算法可能是构建某种索引或哈希表...
答案 1 :(得分:2)
解决方案并不复杂。
将句子分开:
a_words = regexp(A,'(\w+)','match')
b_words = regexp(B,'(\w+)','match')
然后循环比较:
match = nan(numel(a_words),numel(b_words));
for i = 1:numel(a_words)
for j = 1:numel(b_words)
match(i,j) = sum(ismember(a_words{i},b_words{j}));
end
end
但为了更快 - 我不太确定。 你肯定可以把内循环放在一个应该并行化的parfor中。 如果它真的很多话可能会把它们放在数据库中。那将为你做索引。
答案 2 :(得分:1)
您可以利用Map,它为您提供基于字典的高效结构:
对于每个单词,保存显示每个字符串中出现次数的向量:
A = {'life is wonderful', 'matlab makes your dreams come true'};
B = {'life would be meaningless without wonderful matlab', 'what a wonderful world', 'the shoemaker makes shoes', 'rock and roll baby'};
mapA = containers.Map();
sizeA = size(A,2);
for i = 1:size(A,2) % for each string
a = regexpi(A(i),'\w+','match');
for w = a{:} % for each word extracted
str = cell2mat(w);
if(mapA.isKey(str)) % if word already indexed
occ = mapA(str);
else % new key
occ = zeros(1,sizeA);
end
occ(i) = occ(i)+1;
mapA(str) = occ;
end
end
% same for B
mapB = containers.Map();
sizeB = size(B,2);
for i = 1:size(B,2)
a = regexpi(B(i),'\w+','match');
for w = a{:}
str = cell2mat(w);
if(mapB.isKey(str))
occ = mapB(str);
else
occ = zeros(1,sizeB);
end
occ(i) = occ(i)+1;
mapB(str) = occ;
end
end
然后,对于在A中找到的每个唯一单词,计算与B的匹配
match = zeros(size(A,2),size(B,2));
for w = mapA.keys
str = cell2mat(w);
if (mapB.isKey(str))
match = match + diag(mapA(str))*ones(size(match))*diag(mapB(str));
end
end
结果:
match =
2 1 0 0
1 0 1 0
这样你就有了#wordsA + #wordsB + #singleWordsA的复杂性,而不是#wordsA *#wordsB
编辑:或者,如果您不喜欢Map
,则可以按字母顺序向量保存单词出现向量。然后你可以同时检查两个向量:
(假设我们使用的结构中w
属性是单词字符串,而occ
是出现向量)
i = 1; j = 1;
while(i<=size(wordsA,2) && i<=size(wordsB,2))
if(strcmp(wordsA(i).w, wordsB(j).w))
% update match
else
if(before(wordsA(i).w, wordsA(i).w)) % before: fancy function returning 1 if the first argument comes (alphabetically) before the second one (no builtin function comes to my mind)
i = i+1;
else
j = j+1;
end
end
如果你正在寻找'matlab'并且你知道在第10个位置存储'生命'是没有用的来检查之前的位置,因为矢量按字母顺序排列。所以我们有#wordsA +#wordsB迭代与嵌套循环解决方案的#wordsA * #wordsB。