MATLAB: сравнение массивов ячеек строки

У меня есть два массива ячеек строк, и я хочу проверить, содержат ли они одинаковые строки (они не обязательно должны быть в одном порядке, и мы не знаем, имеют ли они одинаковую длину).

Например:

a = {'2' '4' '1' '3'};
b = {'1' '2' '4' '3'};

or

a = {'2' '4' '1' '3' '5'};
b = {'1' '2' '4' '3'};

Сначала я подумал о strcmp, но это потребовало бы перебора содержимого одной ячейки и сравнения с другой. Я также рассмотрел ismember, используя что-то вроде:

ismember(a,b) & ismember(b,a)

но тогда мы не знаем заранее, что они одинаковой длины (очевидный случай неравенства). Итак, как бы вы провели это сравнение наиболее эффективным способом, не написав слишком много случаев if / else.


person Dave    schedule 12.07.2010    source источник


Ответы (3)


Вы можете использовать функцию SETXOR, которая вернет значения, которые не находятся на пересечении двух массивов ячеек. Если он возвращает пустой массив, то два массива ячеек содержат одинаковые значения:

arraysAreEqual = isempty(setxor(a,b));



РЕДАКТИРОВАТЬ: некоторые показатели эффективности ...

Поскольку вам было интересно узнать о показателях производительности, я подумал, что протестирую скорость своего решения по двум решениям, перечисленным в Amro (которые используют ISMEMBER и STRCMP / CELLFUN). Сначала я создал два больших массива ячеек:

a = cellstr(num2str((1:10000).'));  %'# A cell array with 10,000 strings
b = cellstr(num2str((1:10001).'));  %'# A cell array with 10,001 strings

Затем я запускал каждое решение по 100 раз, чтобы получить среднее время выполнения. Затем я поменял местами a и b и перезапустил его. Вот результаты:

    Method     |      Time     |  a and b swapped
---------------+---------------+------------------
Using SETXOR   |   0.0549 sec  |    0.0578 sec
Using ISMEMBER |   0.0856 sec  |    0.0426 sec
Using STRCMP   |       too long to bother ;)

Обратите внимание, что решение SETXOR имеет стабильно быстрое время. Решение ISMEMBER действительно будет работать немного быстрее, если a имеет элементы, которых нет в b. Это связано с коротким замыканием &&, которое пропускает вторую половину вычисления (потому что мы уже знаем, что a и b не содержат одинаковых значений). Однако, если все значения в a также находятся в b, ISMEMBER работает значительно медленнее.

person gnovice    schedule 12.07.2010
comment
Чтобы измерить производительность, вам понадобится другое решение для сравнения, например, предложение, которое вы сделали с помощью цикла и STRCMP. Думаю, с производительностью будет все в порядке, но если вы обнаружите, что использование SETXOR действительно становится узким местом в вашей обработке, вы можете попробовать посмотреть его исходный код (type setxor или edit setxor) и переписать его, убрав некоторые проверки на ошибки и т. д. - person gnovice; 12.07.2010
comment
спасибо, я думаю, я понимаю, что пытался сделать @Mikhail. А как насчет производительности? кажется, что XOR двух наборов - дорогостоящая операция, когда все, что мне нужно, - это тип ответа true / false - person Dave; 12.07.2010

Вы по-прежнему можете использовать функцию ISMEMBER, как и с небольшой модификацией:

arraysAreEqual = all(ismember(a,b)) && all(ismember(b,a))

Кроме того, вы можете записать версию цикла с помощью STRCMP как одну строку:

arraysAreEqual = all( cellfun(@(s)any(strcmp(s,b)), a) )

РЕДАКТИРОВАТЬ: я добавляю третье решение, адаптированное из другого ИТОГО вопрос:

g = grp2idx([a;b]);
v = all( unique(g(1:numel(a))) == unique(g(numel(a)+1:end)) );

В том же духе Im провел сравнение времени (используя функцию TIMEIT):

function perfTests()
    a = cellstr( num2str((1:10000)') );            %#' fix SO highlighting
    b = a( randperm(length(a)) );

    timeit( @() func1(a,b) )
    timeit( @() func2(a,b) )
    timeit( @() func3(a,b) )
    timeit( @() func4(a,b) )
end

function v = func1(a,b)
    v = isempty(setxor(a,b));                      %# @gnovice answer
end

function v = func2(a,b)
    v = all(ismember(a,b)) && all(ismember(b,a));
end

function v = func3(a,b)
    v = all( cellfun(@(s)any(strcmp(s,b)), a) );
end

function v = func4(a,b)
    g = grp2idx([a;b]);
    v = all( unique(g(1:numel(a))) == unique(g(numel(a)+1:end)) );
end

и результаты в том же порядке функций (чем ниже, тем лучше):

ans =
     0.032527
ans =
     0.055853
ans =
       8.6431
ans =
     0.022362
person Amro    schedule 12.07.2010

Взгляните на функцию intersect

Что говорит справка MATLAB:

[c, ia, ib] = intersect(a, b) также возвращает векторы индекса столбца ia и ib, так что c = a(ia) и b(ib) (или c = a(ia,:) и b(ib,:)).

person Mikhail    schedule 12.07.2010
comment
Я не уверен, как получить решение по результату intersect - person Dave; 12.07.2010
comment
Это зависит от того, что именно вам нужно делать. Если вам нужно скалярное логическое значение, в котором оба вектора содержат одинаковые строки, тогда решение от gnovice - правильный ответ. - person Mikhail; 12.07.2010