определение точек из множества попарных расстояний

учитывая матрицу расстояний между точками существует ли алгоритм для определения набора n-мерных точек, которые имеют эти расстояния? (или по крайней мере минимизирует ошибки)

вроде как n-мерная версия проблемы магистрали.

лучшее, что я могу придумать, используя многомерное шкалирование.

4 ответов


вы на правильном пути с многомерным масштабированием (MDS), но MDS непрактичен для больших наборов данных, так как его временная сложность квадратична по количеству точек. Вы можете посмотреть на FastMap, который имеет линейную временную сложность и лучше подходит для индексирования. См.:

Кристос Фалутсос и Кинг-ИП Лин: "FastMap: быстрый алгоритм для Индексирование, интеллектуальный анализ данных и Визуализация традиционных и Мультимедийные наборы данных, в Proc. SIGMOD, 1995, doi: 10.1145 / 223784.223812


вы можете "обмануть" и использовать для этого итерационный численный метод. Возьмите все точки, чтобы быть в некоторых "случайных" положениях изначально, а затем петля через них, перемещая их друг от друга пропорционально требуемому расстоянию. Это предпочтет некоторые точки, но принимая среднее количество ходов перед их применением, то применение среднего снимет эту проблему. Это алгоритм O(n2), но очень простой в реализации и понимании. В примере 2-d ниже ошибка

Пример C++:

#include <conio.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#define DAMPING_FACTOR 0.99f

class point
{
public:
    float x;
    float y;
public:
    point() : x(0), y(0) {}
};

// symmetric matrix with distances
float matrix[5][5] =    {
                            { 0.0f, 4.5f, 1.5f, 2.0f, 4.0f },
                            { 4.5f, 0.0f, 4.0f, 3.0f, 3.5f },
                            { 1.5f, 4.0f, 0.0f, 1.0f, 5.0f },
                            { 2.0f, 3.0f, 1.0f, 0.0f, 4.5f },
                            { 4.0f, 3.5f, 5.0f, 4.5f, 0.0f }
                        };
int main(int argc, char** argv)
{
    point p[5];
    for(unsigned int i = 0; i < 5; ++i)
    {
        p[i].x = (float)(rand()%100)*0.1f;
        p[i].y = (float)(rand()%100)*0.1f;
    }

    // do 1000 iterations
    float dx = 0.0f, dy = 0.0f, d = 0.0f;
    float xmoves[5], ymoves[5];

    for(unsigned int c = 0; c < 1000; ++c)
    {
        for(unsigned int i = 0; i < 5; ++i) xmoves[i] = ymoves[i] = 0.0f;
        // iterate across each point x each point to work out the results of all of the constraints in the matrix
        // collect moves together which are slightly less than enough (DAMPING_FACTOR) to correct half the distance between each pair of points
        for(unsigned int i = 0; i < 5; ++i)
        for(unsigned int j = 0; j < 5; ++j)
        {
            if(i==j) continue;
            dx = p[i].x - p[j].x;
            dy = p[i].y - p[j].y;
            d = sqrt(dx*dx + dy*dy);
            dx /= d;
            dy /= d;
            d = (d - matrix[i][j])*DAMPING_FACTOR*0.5f*0.2f;

            xmoves[i] -= d*dx;
            ymoves[i] -= d*dy;

            xmoves[j] += d*dx;
            ymoves[j] += d*dy;
        }

        // apply all at once
        for(unsigned int i = 0; i < 5; ++i)
        {
            p[i].x += xmoves[i];
            p[i].y += ymoves[i];
        }
    }

    // output results
    printf("Result:\r\n");
    for(unsigned int i = 0; i < 5; ++i)
    {
        for(unsigned int j = 0; j < 5; ++j)
        {
            dx = p[i].x - p[j].x;
            dy = p[i].y - p[j].y;
            printf("%f ", sqrt(dx*dx + dy*dy));
        }
        printf("\r\n");
    }

    printf("\r\nDesired:\r\n");
    for(unsigned int i = 0; i < 5; ++i)
    {
        for(unsigned int j = 0; j < 5; ++j)
        {
            printf("%f ", matrix[i][j]);
        }
        printf("\r\n");
    }

    printf("Absolute difference:\r\n");
    for(unsigned int i = 0; i < 5; ++i)
    {
        for(unsigned int j = 0; j < 5; ++j)
        {
            dx = p[i].x - p[j].x;
            dy = p[i].y - p[j].y;
            printf("%f ", abs(sqrt(dx*dx + dy*dy) - matrix[i][j]));
        }
        printf("\r\n");
    }

    printf("Press any key to continue...");

    while(!_kbhit());

    return 0;
}

есть алгоритм для этого в Программирование Коллективного Разума, p. 49, "просмотр данных в двух измерениях", которые могут быть адаптированы для n-измерений.

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


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

OP имеет входную матрицу NxN расстояний. Он хочет создать выходной массив размером N из N-мерных координат, представляющих точки, где расстояние между каждой точкой хранится во входной матрице.

отметим, что это не разрешимо в общем случае:

Предположим, у меня есть такая матрица

   A  B  C  
A  x  1  2  
B     x  0  
C        x  

A - 1 единица расстояние (скажем, 1 метр) от B, а A-один метр от C. Но B и C находятся в одном и том же месте.

в этом конкретном случае минимальная сумма ошибок составляет 1 метр, и существует бесконечное множество решений, которые достигают этого результата