Алгоритм, ближайшая точка между элементами списка - PullRequest
0 голосов
/ 01 июля 2018

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

Например, если n = 3 для трех списков:

a = [14, 22, 36, 48]
b = [14, 23, 30, 72]
c = [1, 18, 24]

Выход должен быть (22,23,24), потому что:

mean(abs(22-23), abs(23-24), abs(22-24)) = 1.33333

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

Я попытался реализовать его в Python следующим образом

def aligner(aoa):
'''
read arrays of arrays of peaks and return closest peaks
'''
#one of arrays is empty
if not [y for x in aoa for y in x]:
    return None
# there is the same nr in all array no need to do anything
candidate = set.intersection(*map(set, aoa))
if candidate:
    # returns intersect
    return [max(list(candidate))] * len(aoa)
else:
    #tried cartesian product via bumpy malloc err
    pass

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

Мне не нужен код, я только намекаю, есть ли какой-нибудь эффективный способ решения этой проблемы, или грубая сила с n для циклов в перестановочных списках - единственный

EDIT

Что касается размера проблемы, то nr списка максимум 100 (фиксированный), в то время как nr элементов может варьироваться, но я бы сказал, что представленный пример с 4 или 5 точками в списке является реалистичным сценарием.

Все точки неотрицательны.

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

Ответы [ 4 ]

0 голосов
/ 02 июля 2018

Мы не очень много знаем о размерах вашей проблемы, то есть о том, сколько списков и сколько элементов в списке. Для начала и для установки базовой линии вы можете просто использовать itertools.product для итерации всех возможных комбинаций элементов из трех списков, не реализуя их в списке. Затем вы можете перебрать их и найти лучший или передать их непосредственно в min и использовать специальную функцию key, используя itertools.combinations и sum, чтобы найти ту, которая имеет наименьшее среднее расстояние (если сумма наименьшая , так же как и в среднем).

>>> a = [14, 22, 36, 48]
>>> b = [14, 23, 30, 72]
>>> c = [1, 18, 24]
>>> len(list(itertools.product(a, b, c)))
48
>>> min(itertools.product(a, b, c),
...     key=lambda t: sum(abs(n-m) for n, m in itertools.combinations(t, 2)))
(22, 23, 24)

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

0 голосов
/ 02 июля 2018

Этот метод является методом грубой силы, но использует метод исключения, аналогичный алгоритму Дейкстры, который приводит к гораздо меньшему числу случаев (создание алгоритма, который, скорее всего, на несколько порядков быстрее, особенно для больших списков или большого количества списков) , Скажи мне, если ты этого не понимаешь, и я могу уточнить. Реализация может быть найдена здесь: https://github.com/nerryoob/closestPoint

Что вы делаете, составляя список различных комбинаций чисел (то есть ответов)? Лучший в начале (индекс 0), худший в конце или наоборот, посмотрите, что работает лучше всего. Вы создадите список результатов только для первого списка ввода, полностью игнорируя остальные. Конечно, для одного списка все элементы являются решением - все они имеют общую разницу 0. Так что просто скопируйте первый входной список в список результатов

Далее, возможно, с циклом , в то время как , следуйте этому алгоритму. Возьмите верхний элемент и вытяните его из списка результатов. Сохраните его ценность. Перейдите к следующему списку ввода и для каждого элемента в этом следующем списке ввода сделайте копию верхнего элемента, который вы только что открыли, у которого также есть элемент следующего списка ввода. Найдите новую общую разницу и вставьте новый элемент на основе этого в список. Повторяйте до тех пор, пока в верхнем решении не появятся все списки. Это означает, что вы гарантируете, что у вас есть лучшее решение (по крайней мере, 1-го сочленения), при этом вы тратите гораздо меньше времени на комбинации, которые явно не являются решением

  • Пример ( число в скобках - это общая разница)

    [14, 22, 36, 48] [14, 23, 30, 72] [1, 18, 24]

Список результатов: [14(0), 22(0), 36(0), 48(0)]

  • Посмотрите на 14. Вставьте новые числа [14 и 14 (0), 22 (0), 36 (0), 48 (0), 14 и 23 (9), 14 и 30 (16), 14 и 72 (58)]
  • Посмотрите на 14 и 14. Вставьте новые числа [22 (0), 36 (0), 48 (0), 14 и 14 и 18 (8), 14 и 23 (9), 14 и 30 (16), 14 и 14 и 24 (20), 14 и 14 и 1 (26), 14 и 72 (58)]
  • Посмотрите на 22. Вставьте новые числа [36 (0), 48 (0), 22 и 23 (1), 14 и 14 и 18 (8), 22 и 14 (8), 22 и 30 (8), 14 и 23 (9), 14 и 30 (16), 14 и 14 и 24 (20), 14 и 14 и 1 (26), 22 и 72 (50), 14 и 72 (58)]

Продолжайте повторять, и вы получите 22, 23, 24 сверху. Поскольку в нем есть все списки n , вы можете остановиться и дать ответ

Для оптимизации:

  • Удалить дубликаты
  • Возможно, каким-то образом использовать упорядоченные списки
  • Подумайте о том, куда вы помещаете предметы с одинаковой общей разницей, возможно, предметы с большим числом чисел идут впереди

EDIT: Алгоритмическая сложность O (n ^ 2)

0 голосов
/ 02 июля 2018

Прежде всего, оптимизация среднего значения различий такая же, как и оптимизация суммы разностей.

Если вы смоделируете свою задачу как ориентированный граф, это можно решить:

Пусть ваши списки будут A, B, C. Каждая запись в ваших списках - это вершина графа v_ai, где a - список, а i - индекс.

Для каждого индекса i в A, j в B, добавить ребро v_ai -> v_bj с весом abs(A(i) - B(j))

Для каждого индекса i в B, j в C, добавить ребро v_bi -> v_cj с весом abs(B(i) - C(j))

Для каждого индекса i в C, j в A, добавить ребро v_ci -> v_aj с весом abs(C(i) - A(j))

То, что вы сейчас ищете, - это минимальный цикл на этом графике. Используйте этот ответ для алгоритма O (n ^ 3). (модифицированный алгоритм Флойда-Варшалла)

0 голосов
/ 01 июля 2018

Я не уверен, что лучше всего найти оптимальное решение, но одной эвристикой может быть проверка диапазонов. Если наши списки отсортированы, мы можем проверить, есть ли в списке элемент в диапазоне, используя бинарный поиск. Таким образом, мы можем разделить и победить, пытаясь сузить диапазоны, которые включают элемент из каждого списка. Из-за природы вычисления среднего значения мы, к сожалению, могли бы также интересоваться диапазонами, которые включают элементы из многих, но не из всех списков, поскольку набор очень близких чисел с несколькими выбросами может привести к меньшему среднему значению различий, чем большему разбросу в меньшем спектр; это несколько усложняет решение.

...