【问题标题】:MATLAB: words matching between cell arrays of stringsMATLAB:字符串元胞数组之间的单词匹配
【发布时间】:2011-11-08 11:36:16
【问题描述】:

我正在尝试解决以下问题,并且我需要尽可能高效地完成它(即尽可能避免循环)。

我有两个元胞数组,分别是 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 中每个 char 字符串中有多少单词也出现在 B 的每个 char 字符串中。

对于前面的例子,一个合适的结果可能是这样的:

match = [2 1 0 0
1 0 1 0]

第一行表示A的第一个char字符串中有多少个单词出现在B的四个char字符串中。第二行,A的第二个char字符串相同。

双循环实现很简单,但非常耗时,尤其是因为元胞数组 B 的长度(超过 300 万个元胞)。

有什么想法吗?非常感谢。

泽维尔

【问题讨论】:

    标签: string algorithm matlab comparison vectorization


    【解决方案1】:

    让我通过发布“直截了当”的解决方案开始这件事(至少其他人有一个基线可以比较):

    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
    

    更好的算法可能是构建某种索引或哈希表...

    【讨论】:

      【解决方案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 中。 如果真的有很多单词,可能会将它们放入数据库中。这将为您建立索引。

      【讨论】:

        【解决方案3】:

        您可以利用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 迭代 vs. #wordsA*#​​wordsB 的嵌套循环解决方案。

        【讨论】:

        • 您是否针对@Amro 的解决方案计时?
        • 似乎时钟大致相同,即使使用更大的数据集
        猜你喜欢
        • 1970-01-01
        • 2011-03-14
        • 2011-02-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-08
        相关资源
        最近更新 更多