【问题标题】:string matching in an alpbetically ordered list (the MATLAB way)在一个按字母顺序排列的列表中匹配字符串(MATLAB 方式)
【发布时间】:2013-08-29 21:30:13
【问题描述】:

我有一个按字母顺序排列的大型字符串元胞数组(约 49.5 万),其中有很多重复项(它们彼此相邻,因为它是按字母顺序排列的)。

对于给定的查找字符串,我需要在列表中找到与我传入的字符串匹配的所有字符串。

我一直在使用 strcmp(lookUpString,list) 来执行此操作,但这非常慢 - 我认为它会遍历列表中的每个值进行比较,因为它不知道它是按字母顺序排序的。

我可以编写一个while循环来遍历列表以使用strcmp比较每个字符串,直到找到我想要的字符串块(然后停止),但我想知道是否有“matlab”方法这样做(即对排序数组执行逻辑比较操作)。

感谢您的帮助!

【问题讨论】:

  • 您使用的是哪个版本的 MATLAB?在我的项目中,当我创建一个由 400K 100 个字母的随机字符串组成的元胞数组并使用 strcmp 搜索其中一个时,需要 0.024816 秒。它实际上是一个 MEX 文件。我用的是 2011A。

标签: matlab


【解决方案1】:

更新:我对我之前的“方法 3”不满意,所以我只是重新调整了一下以获得更好的性能。现在,它的运行速度比单纯的 strcmp 快了近 10 倍。

strcmp 在我的机器上获胜(Linux Mint 12 上的 2011b)。特别是,它比ismember 好得多。但是,如果您自己进行一些手动预分类,您可以获得一些额外的速度。考虑以下速度测试:

NumIter = 100;
N = 495000;
K = N / 20;
List = cell(N, 1);
for i = 1:20
    List(i*K - K + 1:i*K) = cellstr(char(i+96));
end

StrToFind = cell(NumIter, 1);
for j = 1:NumIter
    StrToFind{j} = char(round(rand * 20) + 96);
end

%# METHOD 1 (ismember)
tic
for j = 1:NumIter
    Index1 = ismember(List, StrToFind{j});
    Soln1 = List(Index1);
end
toc

%#METHOD 2 (strcmp)
tic
for j = 1:NumIter
    Index2 = strcmp(StrToFind{j}, List);
    Soln2 = List(Index2);
end
toc

%#METHOD 3 (strmp WITH MANUAL PRE-SORTING)    
tic
for j = 1:NumIter
    CurStrToFind = StrToFind{j};
    K = 100;
    I1 = zeros(K, 2); I1(1, :) = ones(1, 2);
    I2 = zeros(K, 2); I2(end, 1) = 1; I2(end, 2) = N;
    KDiv = floor(N/K);
    for k = 2:K-1
        CurSearchNum = k * KDiv;
        CurListItem = List{CurSearchNum};
        if CurListItem < CurStrToFind; I1(k, 1) = 1; end;
        if CurListItem > CurStrToFind; I2(k, 1) = 1; end;
        I1(k, 2) = CurSearchNum; I2(k, 2) = CurSearchNum;
    end
    a = find(I1(:, 1), 1, 'last');
    b = find(I2(:, 1), 1, 'first');
    ShortList = List(I1(a, 2):I2(b, 2));
    Index3 = strcmp(CurStrToFind, ShortList);
    Soln3 = ShortList(Index3);
end
toc

输出是:

Elapsed time is 6.411537 seconds.
Elapsed time is 1.396239 seconds.
Elapsed time is 0.150143 seconds.

【讨论】:

  • +1:除了我可以建议的一些小优化(比较时不需要转换为双精度,在顶部只提取一次StrToFind{j},...),我想说这是最好的一个。
  • @RodyOldenhuis 干杯罗迪,我已经合并了您的优化。我想如果要添加更多if 语句(即将List 分成四分之一甚至八分之一),性能会有所提高,但我只愿意经历这么多痛苦:-)
  • 查看ismember 文档,您甚至不需要循环。它将第一个列表中的每个条目与第二个列表中的每个条目进行比较。
  • @BenVoigt 该循环用于速度测试,因此 tic toc 时间相当大。你会注意到我实际上是在循环整个方法,而不是List(即循环与ismember 无关)。将我代码顶部的 NumIter 设置为 1,你就会明白我的意思了 :-)
  • 等等,你没有循环到N.... 这意味着你只是在搜索在集合的早期找到的项目。虽然我猜如果它正在构建一个完整的匹配掩码,而不仅仅是返回第一个匹配,那应该没关系。
【解决方案2】:

ismember 是你的朋友。而不是线性搜索,it does binary search.

【讨论】:

  • 对于我设置的速度测试,ismember 的运行速度似乎比 strcmp 慢得多。请参阅我的答案以了解更多详细信息。
【解决方案3】:

尝试二分查找。

几乎快了 13(!) 倍:

Elapsed time is 7.828260 seconds.    % ismember
Elapsed time is 0.775260 seconds.    % strcmp
Elapsed time is 0.113533 seconds.    % strmp WITH MANUAL PRE-SORTING
Elapsed time is 0.008243 seconds.    % binsearch

这是我正在使用的 bin 搜索代码:

function ind = binSearch(key, cellstr)
    % BINSEARCH  that find index i such that cellstr(i)<= key <= cellstr(i+1)
    %
    % * Synopsis: ind = binSearch(key, cellstr)
    % * Input   : key = what to search for
    %           : cellstr = sorted cell-array of string (others might work, check strlexcmp())
    % * Output  : ind = index in x cellstr such that cellstr(i)<= key <= cellstr(i+1)
    % * Depends : strlexcmp() from Peter John Acklam’s string-utilities, 
    %             at: http://home.online.no/~pjacklam/matlab/software/util/strutil/
    %
    % Transcoded from a Java version at: http://googleresearch.blogspot.it/2006/06/extra-extra-read-all-about-it-nearly.html
    % ankostis, Aug 2013

    low = 1;
    high = numel(cellstr);

    while (low <= high)
        ind = fix((low + high) / 2);
        val = cellstr{ind};

        d = strlexcmp(val, key);
        if (d < 0)
            low = ind + 1;
        elseif (d > 0)
            high = ind - 1;
        else
            return;     %% Key found.
        end
    end
    ind = -(low);       %% Not found!
end

您可以从 Peter John Acklam 的 string-utilities 获取 strlexcmp(),地址为: http://home.online.no/~pjacklam/matlab/software/util/strutil/

最后这是我使用的测试脚本:

%#METHOD 4 (WITH BIN-SEARCH)    
tic
for j = 1:NumIter
    Index1 = binsearch(StrToFind{j}, List);
    Soln4 = List(Index1);
end

请注意,如果字符串越长,结果可能会有所不同。

【讨论】:

    猜你喜欢
    • 2013-05-14
    • 1970-01-01
    • 2013-05-14
    • 1970-01-01
    • 2013-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-07
    相关资源
    最近更新 更多