2016-10-10 8 views
8

У меня есть массив ячеек A измерения m * k.Найти уникальные строки массива ячеек, учитывая все возможные перестановки в каждой строке

Я хочу, чтобы строки A были уникальными до порядка k ячеек.

«сложно» часть «вплоть до порядка K клеток»: рассмотреть k клетки в i-й строки A, A(i,:); там может быть ряд j из A, A(j,:), что эквивалентно A(i,:) до повторного упорядочения своих k клеток, а это означает, что, например, если k=4 это может быть, что:

A{i,1}=A{j,2} 
A{i,2}=A{j,3} 
A{i,3}=A{j,1} 
A{i,4}=A{j,4} 

Что я делаю в текущий момент:

G=[0 -1 1; 0 -1 2; 0 -1 3; 0 -1 4; 0 -1 5; 1 -1 6; 1 0 6; 1 1 6; 2 -1 6; 2 0 6; 2 1 6; 3 -1 6; 3 0 6; 3 1 6]; 
h=7; 
M=reshape(G(nchoosek(1:size(G,1),h),:),[],h,size(G,2)); 
A=cell(size(M,1),2); 
for p=1:size(M,1) 
    A{p,1}=squeeze(M(p,:,:)); 
    left=~ismember(G, A{p,1}, 'rows'); 
    A{p,2}=G(left,:); 
end 

%To find equivalent rows up to order I use a double loop (VERY slow). 
indices=[]; 
for j=1:size(A,1) 
    if ismember(j,indices)==0 %if we have not already identified j as a duplicate 
     for i=1:size(A,1) 
      if i~=j 
       if (isequal(A{j,1},A{i,1}) || isequal(A{j,1},A{i,2}))... 
        &&... 
        (isequal(A{j,2},A{i,1}) || isequal(A{j,2},A{i,2}))... 
        indices=[indices;i]; 
       end 
      end 
     end 
    end 
end 
A(indices,:)=[]; 

Это работает, но это слишком медленно. Я надеюсь, что есть что-то более быстрое, что я могу использовать.

+0

Hi! Вопрос не закончен. Вы добавили: «То, что я делаю в данный момент:«, у него нет части », и это не работает, потому что:« –

+0

Спасибо, я добавил эту часть. – user3285148

+0

Можете ли вы быть более наглядным? Я не знаю, что * до порядка k подкатегорий * означает, и я не могу вызвать его из кода. –

ответ

6

Я хотел бы предложить еще одну идею, которая имеет некоторые Concep tual сходство с erfan's. Моя идея использует hash functions, и, в частности, GetMD5 FEX submission.

Основная задача - «уменьшить» каждую строку в A до единственного репрезентативного значения (например, символьного вектора), а затем найти уникальные записи этого вектора.

Судя по эталону противдругие предложения, мой ответ не работает так же хорошо, как один из альтернатив, но я думаю, что его raison d'être заключается в том, что он полностью агрегирован по типу данных (в пределах ограничений GetMD5 ), что алгоритм очень понятен, это замена взамен, поскольку он работает на A и что результирующий массив точно равен тому, который получен исходным методом. Конечно, для этого требуется, чтобы компилятор работал и имел риск возникновения хеш-коллизий (что может повлиять на результат в ОЧЕНЬ ОЧЕНЬ редких случаях).

Здесь приведены результаты типичного прогона на моем компьютере, а затем код:

Original method timing:  8.764601s 
Dev-iL's method timing:  0.053672s 
erfan's method timing:  0.481716s 
rahnema1's method timing: 0.009771s 

function q39955559 
G=[0 -1 1; 0 -1 2; 0 -1 3; 0 -1 4; 0 -1 5; 1 -1 6; 1 0 6; 1 1 6; 2 -1 6; 2 0 6; 2 1 6; 3 -1 6; 3 0 6; 3 1 6]; 
h=7; 
M=reshape(G(nchoosek(1:size(G,1),h),:),[],h,size(G,2)); 
A=cell(size(M,1),2); 
for p=1:size(M,1) 
    A{p,1}=squeeze(M(p,:,:)); 
    left=~ismember(G, A{p,1}, 'rows'); 
    A{p,2}=G(left,:); 
end 

%% Benchmark: 
tic 
A1 = orig_sort(A); 
fprintf(1,'Original method timing:\t\t%fs\n',toc); 

tic 
A2 = hash_sort(A); 
fprintf(1,'Dev-iL''s method timing:\t\t%fs\n',toc); 

tic 
A3 = erfan_sort(A); 
fprintf(1,'erfan''s method timing:\t\t%fs\n',toc); 

tic 
A4 = rahnema1_sort(G,h); 
fprintf(1,'rahnema1''s method timing:\t%fs\n',toc); 

assert(isequal(A1,A2)) 
assert(isequal(A1,A3)) 
assert(isequal(numel(A1),numel(A4))) % This is the best test I could come up with... 

function out = hash_sort(A) 
% Hash the contents: 
A_hashed = cellfun(@GetMD5,A,'UniformOutput',false); 
% Sort hashes of each row: 
A_hashed_sorted = A_hashed; 
for ind1 = 1:size(A_hashed,1) 
    A_hashed_sorted(ind1,:) = sort(A_hashed(ind1,:)); 
end 
A_hashed_sorted = cellstr(cell2mat(A_hashed_sorted)); 
% Find unique rows: 
[~,ia,~] = unique(A_hashed_sorted,'stable'); 
% Extract relevant rows of A: 
out = A(ia,:); 

function A = orig_sort(A) 
%To find equivalent rows up to order I use a double loop (VERY slow). 
indices=[]; 
for j=1:size(A,1) 
    if ismember(j,indices)==0 %if we have not already identified j as a duplicate 
     for i=1:size(A,1) 
      if i~=j 
       if (isequal(A{j,1},A{i,1}) || isequal(A{j,1},A{i,2}))... 
        &&... 
        (isequal(A{j,2},A{i,1}) || isequal(A{j,2},A{i,2}))... 
        indices=[indices;i]; 
       end 
      end 
     end 
    end 
end 
A(indices,:)=[]; 

function C = erfan_sort(A) 
STR = cellfun(@(x) num2str((x(:)).'), A, 'UniformOutput', false); 
[~, ~, id] = unique(STR); 
IC = sort(reshape(id, [], size(STR, 2)), 2); 
[~, col] = unique(IC, 'rows'); 
C = A(sort(col), :); % 'sort' makes the outputs exactly the same. 

function A1 = rahnema1_sort(G,h) 
idx = nchoosek(1:size(G,1),h); 
%concatenate complements 
M = [G(idx(1:size(idx,1)/2,:),:), G(idx(end:-1:size(idx,1)/2+1,:),:)]; 
%convert to cell so A1 is unique rows of A 
A1 = mat2cell(M,repmat(h,size(idx,1)/2,1),repmat(size(G,2),2,1)); 

- Если более сложные типы данных должны быть hashed, вместо этого можно использовать DataHash FEX submission, что несколько медленнее.

+2

Ницца! Фактически, чтобы быть наиболее эффективным при рассмотрении общего случая, следует использовать идею 'GetMD5' с моим методом сортировки! – erfan

4

Сформулируйте проблему: Идеальный выбор при идентификации уникальных строк в массиве - использовать C = unique(A,'rows'). Но здесь есть две серьезные проблемы, которые не позволяют нам использовать эту функцию в этом случае. Во-первых, вы хотите подсчитать во всех возможных перестановках каждой строки по сравнению с другими строками. Если A имеет 5 столбцов, то означает проверку 120 различных реорганизаций в строке! Звуки невозможны.

Вторая проблема связана с самим unique; Это does not accept cells except cell arrays of character vectors. Поэтому вы не можете просто пройти A до unique и получить то, что ожидаете.

Зачем искать альтернативу? Как вы знаете, потому что в настоящее время это очень медленно:

With nested loop method: 
------------------- Create the data (first loop): 
Elapsed time is 0.979059 seconds. 
------------------- Make it unique (second loop): 
Elapsed time is 14.218691 seconds. 

Мое решение:

  1. Сформировать другой массив ячеек, содержащий те же клетки, но преобразуются в строки (STR).
  2. Найдите здесь индекс всех уникальных элементов (id).
  3. Создайте связанную матрицу с уникальными индексами и сортируйте строки (IC).
  4. Поиск уникальных строк (rows).
  5. Соберите соответствующие строки A (C).

И это код: проверка

disp('------------------- Create the data:') 
tic 
G = [0 -1 1; 0 -1 2; 0 -1 3; 0 -1 4; 0 -1 5; 1 -1 6; 1 0 6; ... 
    1 1 6; 2 -1 6; 2 0 6; 2 1 6; 3 -1 6; 3 0 6; 3 1 6]; 
h = 7; 
M = reshape(G(nchoosek(1:size(G,1),h),:),[],h,size(G,2)); 
A = cell(size(M,1),2); 
for p = 1:size(M,1) 
    A{p, 1} = squeeze(M(p,:,:)); 
    left = ~ismember(G, A{p,1}, 'rows'); 
    A{p,2} = G(left,:); 
end 
STR = cellfun(@(x) num2str((x(:)).'), A, 'UniformOutput', false); 
toc 

disp('------------------- Make it unique (vectorized):') 
tic 
[~, ~, id] = unique(STR); 
IC = sort(reshape(id, [], size(STR, 2)), 2); 
[~, col] = unique(IC, 'rows'); 
C = A(sort(col), :); % 'sort' makes the outputs exactly the same. 
toc 

Производительность:

------------------- Create the data: 
Elapsed time is 1.664119 seconds. 
------------------- Make it unique (vectorized): 
Elapsed time is 0.017063 seconds. 

Хотя инициализации требуется немного больше времени и памяти, этот метод является чрезвычайно быстрее в поиске уникальных строк с рассмотрение всех перестановок. Время выполнения почти нечувствительно к числу столбцов в A.

3

Похоже, что G является вводящим в заблуждение пунктом. Вот результат nchoosek для небольшого числа

idx=nchoosek(1:4,2) 
ans = 

    1 2 
    1 3 
    1 4 
    2 3 
    2 4 
    3 4 

первая строка дополнение последней строки

второго ряда является дополнением одного до последней строки

.....

поэтому, если мы извлечем строки {1 , 2} от G, то его дополнением будут строки {3, 4} и так далее. Другими словами, если предположить, что число строк G равно 4, то G(idx(1,:),:) является дополнением G(idx(end,:),:).

Поскольку строки G являются уникальными, то все A{m,n} s всегда имеют одинаковый размер.

A{p,1} и A{p,2} являются дополнением друг к другу. и размер уникальных строк A является size(idx,1)/2

Так что нет необходимости в какой-либо петли или дальнейшего сравнения:

h=7; 
G = [0 -1 1; 0 -1 2; 0 -1 3; 0 -1 4; 0 -1 5; 1 -1 6; 1 0 6; ... 
    1 1 6; 2 -1 6; 2 0 6; 2 1 6; 3 -1 6; 3 0 6; 3 1 6]; 
idx = nchoosek(1:size(G,1),h); 
%concatenate complements 
M = [G(idx(1:size(idx,1)/2,:).',:), G(idx(end:-1:size(idx,1)/2+1,:).',:)]; 
%convert to cell so A1 is unique rows of A 
A1 = mat2cell(M,repmat(h,size(idx,1)/2,1),repmat(size(G,2),2,1)); 

Update: Выше метод работает лучше всего, однако, если идея заключается в том, чтобы получить A1 от А кроме GI предлагает следующий метод, основанный на erfan. Вместо того, чтобы преобразовать массив в строку, мы можем работать непосредственно с массивом:

STR=reshape([A.'{:}],numel(A{1,1}),numel(A)).'; 
[~, ~, id] = unique(STR,'rows'); 

IC = sort(reshape(id, size(A, 2),[]), 1).'; 
[~, col] = unique(IC, 'rows'); 
C1 = A(sort(col), :); 

Поскольку я использую октаву я не могу в настоящее время запустить MEX файл, то я не могу проверить Dev-iL «s метод

Результат:

erfan method (string): 4.54718 seconds. 
rahnema1 method (array): 0.012639 seconds. 

Online Demo

+0

Возможно, я ошибался, но я думаю, идея состоит не в том, чтобы создать 'A1' из' G', а из 'A'. Поэтому 'G' и' M' предназначены только для создания произвольных данных для вопроса. Предположим, что у вас есть 'A' в качестве входных данных, что бы вы сделали? – EBH

+1

@EBH Я не обязательно соглашаюсь с вами - это вопрос о том, что такое «настоящие» входные данные. Вся красота описания большей картины заключается в том, что кто-то может предложить совершенно другой подход, который работает намного лучше. Мне нравится именно этот аспект вышеупомянутого ответа. @ rahnema - Как вы убедитесь, что ваш 'A1' эквивалентен OP' A'? Поскольку порядок строк отличается, я думаю, вы должны включить некоторый код проверки. –

+0

@EBH Если ваше предположение верно, я, возможно, сделаю то, что сделал erfan! – rahnema1

 Смежные вопросы

  • Нет связанных вопросов^_^