Самый быстрый способ уменьшить количество точек широты и долготы

Я пытаюсь уменьшить и объединить несколько точек в центральную точку этих мест. Сейчас я грубо заставляю его найти ближайшую пару, объединяя их и повторяя до тех пор, пока я не уменьшу ее до своей цели (примечание на стороне: на самом деле я уменьшил проблему, отсортировав ее на (lat*lat+long*long), затем выполнив поиск по 10% с каждой стороны каждой точки, которая с моими испытаниями всегда находила кратчайшее расстояние в этом диапазоне).

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

Есть ли лучший алгоритм, который бы дал мне как можно более точные результаты? Или алгоритм с более быстрым расстоянием? Думаю, это нужно только быть точным на коротких расстояниях.


Сейчас я нахожу дистанцию ​​с (Википедия имела это под "сферической землей, проецируемой в плоскость" ):

double dLat = pos2.LatitudeR - pos1.LatitudeR;
double dLon = pos2.LongitudeR - pos1.LongitudeR;

double cosLatM = Math.Cos((pos2.LatitudeR + pos1.LatitudeR)/2) * dLon;
double a = dLat*dLat + cosLatM*cosLatM;

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


Изменить, чтобы описать, как работает мой текущий алгоритм (это идеальный способ найти результаты, которые я хочу, но гораздо более быстрое приближение):

Описывая это линейно, если у вас x=1,4,5,6,10,20,22

  • Он объединит 4 + 5 = 4,5 [первое расстояние, которое он найдет]
  • (4.5 * 2 + 6)/3 = 5 - x=1,5,10,20,22 [1,5 расстояние]
  • 20 + 22 = 21 - x=1,5,10,21 [расстояние 2.0]
  • (5 * 3 + 1)/4 = 4 - x=4,10,21 [расстояние 4.0]
  • (4 * 4 + 10)/5.2 - Итак, вы получите x=5.2,21. (Он отслеживает CombineCount, чтобы он мог найти правильный средний центр таким образом).

Результаты: Вот моя текущая функция расстояния, с генерацией таблицы поиска для cos ^ 2. Не успели проверить, насколько близко мои точки, так что не реализовано предложение Джоу аппроксимировать cos ^ 2, но это может улучшить скорость по таблице поиска здесь.

Алгоритм K-Cluster, который я пробовал (см. мой комментарий к этому ответу), не сочетал их так, как я хотел, в итоге получилось тонну точек вблизи центра карты и несколько точек к краям. Поэтому, если я не могу исправить это, я использую свой алгоритм медленнее.

public static double Distance(AddressCoords pos1, AddressCoords pos2, DistanceType type)
{
    if (LookupTable == null) LookupTable = BuildLookup();

    double R = (type == DistanceType.Miles) ? 3960 : 6371;

    double dLat = pos2.LatitudeR - pos1.LatitudeR;
    double dLon = pos2.LongitudeR - pos1.LongitudeR;

    double LatM = ((pos2.LatitudeR + pos1.LatitudeR)/2);
    if (LatM < 0) LatM = -LatM; //Don't allow any negative radian values
    double cosLatM2 = LookupTable[(int)(LatM * _cacheStepInverse)];
    double a = dLat*dLat + cosLatM2 * dLon*dLon;

    //a = Math.Sqrt(a);

    double d = a * R;

    return d;
}

private const double _cacheStep = 0.00002;
private const double _cacheStepInverse = 50000;

private static double[] LookupTable = null;

public static double[] BuildLookup()
{
    // set up array
    double maxRadian = Math.PI*2;
    int elements = (int)(maxRadian * _cacheStepInverse) + 1;

    double[] _arrayedCos2 = new double[elements];
    int i = 0;
    for (double angleRadians = 0; angleRadians <= maxRadian;
        angleRadians += _cacheStep)
    {
        double cos = Math.Cos(angleRadians);
        _arrayedCos2[i] = cos*cos;
        i++;
    }
    return _arrayedCos2;
}

Ответ 1

Чтобы ускорить разработку расстояний между точками:

Если вы выполните некоторую элементарную алгебру, вы получите:

D = R*Sqrt(Lat2^2 + Lat1^2 - 2*Lat1*Lat2 + cos^2((Lat2 + Lat1) /2)(Lon2^2 + Lon1^2 - 2*Lon1*Lon2))

Первое, что вы можете сделать, чтобы ускорить это, нормализуется радиусом Земли (R) и сравнивает квадратные расстояния, а не расстояния, избегая при этом квадратного корня и члена R, экономя себе 2 расчета за сравнение. Выход:

valToCompare = Lat2^2 + Lat1^2 - 2*Lat1*Lat2 + cos^2((Lat2 + Lat1) /2)(Lon2^2 + Lon1^2 - 2*Lon1*Lon2)

Еще одна вещь, которую вы могли бы сделать, - предусмотреть Lat ^ 2 и Lon ^ 2 для каждой координаты - уменьшить количество вычислений для каждого сравнения на 4.

Кроме того, если точки все относительно близко друг к другу по широте, вы можете приблизиться к члену cos ^ 2, предварительно вычислив его, используя широту случайной точки или среднюю широту всех точек, а не среднюю широту из двух сравниваемых точек. Это уменьшает количество вычислений для каждого сравнения на другое.

Наконец, вы можете предварительно вычислить 2 * Lat и 2 * Lon для каждой точки, вырезая еще 2 вычисления для каждого сравнения.

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

Ответ 2

Рассматривали ли вы просмотр алгоритмов K-Cluster?

Эти алгоритмы используются для группировки закрытых/связанных объектов (в вашем случае, точек) в кластеры на основе их ближайшего значения. Эти алгоритмы обычно довольно оптимизированы и построены для обработки большого количества данных. В случае 4000 пунктов → 1000 пунктов, вы будете запускать 1000-кластерный запуск ваших данных и получать 1000 групп точек, каждый из которых может быть объединен с одной точкой.

Ответ 3

Что же касается эффективного способа, рассмотрели ли вы прокладку сетки по карте и затем присвоили каждую точку своей соответствующей ячейке в сетке? Это должно иметь хорошую производительность.

Лучше (но медленнее) подход состоял бы в том, чтобы иметь динамические ячейки вместо фиксированных ячеек, подобных предложению выше. Вы начинаете без каких-либо ячеек. Затем отбросьте первую точку на карте и определите ячейку с определенными размерами вокруг нее. Затем опустите следующую точку на карте. Если он попадает в предыдущую ячейку, вы добавляете его к ней и, возможно, забираете ячейку вокруг двух точек. Если точка выходит за пределы ячейки, вы создаете для нее вторую ячейку. Теперь вы добавляете третью точку на карту и проверяете ее на две ячейки. Этот процесс продолжается до тех пор, пока вы не добавите все точки на карту. Надеюсь, вы поняли эту идею. Я думаю, вы могли бы приблизительно ограничить количество уменьшенных точек, изменив размер ячеек.

EDIT (на основе комментария от rrenaud): вы можете начать использовать большой размер ячейки и применить один из вышеперечисленных алгоритмов. Если количество ячеек, в которые вы попадаете, слишком низкое, вы можете повторить алгоритм на каждой из ячеек и разделить их еще больше. Хотя это не позволит вам точно свести к фиксированному количеству очков, вы можете приблизиться.