Подтвердить что ты не робот

Эффективно вычислить парное квадратичное евклидово расстояние в Matlab

Учитывая два набора d -мерных точек. Как я могу наиболее эффективно вычислить парную квадратичную эвклидовую матрицу расстояний в Matlab?

Обозначения: Set one задается символом (numA,d) -matrix A, а set two задается символом (numB,d) -matrix B. Результирующая матрица расстояния должна иметь формат (numA,numB).

Пример точек:

d = 4;            % dimension
numA = 100;       % number of set 1 points
numB = 200;       % number of set 2 points
A = rand(numA,d); % set 1 given as matrix A
B = rand(numB,d); % set 2 given as matrix B
4b9b3361

Ответ 1

Обычно данный ответ основан на bsxfun (см., например, [1]). Мой предложенный подход основан на матричном умножении и оказывается намного быстрее, чем любой сопоставимый алгоритм, который я мог бы найти:

helpA = zeros(numA,3*d);
helpB = zeros(numB,3*d);
for idx = 1:d
    helpA(:,3*idx-2:3*idx) = [ones(numA,1), -2*A(:,idx), A(:,idx).^2 ];
    helpB(:,3*idx-2:3*idx) = [B(:,idx).^2 ,    B(:,idx), ones(numB,1)];
end
distMat = helpA * helpB';

Обратите внимание: Для константы d можно заменить for -loop жестко запрограммированными реализациями, например

helpA(:,3*idx-2:3*idx) = [ones(numA,1), -2*A(:,1), A(:,1).^2, ... % d == 2
                          ones(numA,1), -2*A(:,2), A(:,2).^2 ];   % etc.

Оценка:

%% create some points
d = 2; % dimension
numA = 20000;
numB = 20000;
A = rand(numA,d);
B = rand(numB,d);

%% pairwise distance matrix
% proposed method:
tic;
helpA = zeros(numA,3*d);
helpB = zeros(numB,3*d);
for idx = 1:d
    helpA(:,3*idx-2:3*idx) = [ones(numA,1), -2*A(:,idx), A(:,idx).^2 ];
    helpB(:,3*idx-2:3*idx) = [B(:,idx).^2 ,    B(:,idx), ones(numB,1)];
end
distMat = helpA * helpB';
toc;

% compare to pdist2:
tic;
pdist2(A,B).^2;
toc;

% compare to [1]:
tic;
bsxfun(@plus,dot(A,A,2),dot(B,B,2)')-2*(A*B');
toc;

% Another method: added 07/2014
% compare to ndgrid method (cf. Dan comment)
tic;
[idxA,idxB] = ndgrid(1:numA,1:numB);
distMat = zeros(numA,numB);
distMat(:) = sum((A(idxA,:) - B(idxB,:)).^2,2);
toc;

Результат:

Elapsed time is 1.796201 seconds.
Elapsed time is 5.653246 seconds.
Elapsed time is 3.551636 seconds.
Elapsed time is 22.461185 seconds.

Для более подробной оценки w.r.t. размер и количество точек данных следуют приведенному ниже обсуждению (@comments). Оказывается, что разные альгосы должны быть предпочтительными в разных условиях. В некритических ситуациях просто используйте версию pdist2.

Дальнейшая разработка: Можно думать о замене квадрата евклидова любой другой метрикой, основанной на том же принципе:

help = zeros(numA,numB,d);
for idx = 1:d
    help(:,:,idx) = [ones(numA,1), A(:,idx)     ] * ...
                    [B(:,idx)'   ; -ones(1,numB)];
end
distMat = sum(ANYFUNCTION(help),3);

Тем не менее, это занимает много времени. Полезно было бы заменить на меньшую d трехмерную матрицу help на d 2-мерные матрицы. Специально для d = 1 он предоставляет метод вычисления попарной разности простым матричным умножением:

pairDiffs = [ones(numA,1), A ] * [B'; -ones(1,numB)];

Есть ли у вас какие-либо дальнейшие идеи?