Алгоритм поиска «самых распространенных элементов» в разных массивах - PullRequest
5 голосов
/ 18 февраля 2011

У меня есть, например, 5 массивов с некоторыми вставленными элементами (числами):

1, 4 , 8,101,2,3, 4 , 11,152 1008 * 4 *, 20,21 2 30

Мне нужно найти наиболее распространенных элементов в этих массивах, и каждый элемент должен пройти весь путь до конца (см. Пример ниже).В этом примере это будет жирная комбинация (или та же самая, но с «30» на конце, это «то же самое»), потому что она содержит наименьшее количество различных элементов (только два, 4 и 2/30).

Эта комбинация (см. Ниже) не хороша, потому что, если у меня есть, например.«4» должно «идти» до конца (следующий массив не должен содержать «4»).Таким образом, комбинация должна идти до конца.

1, 4 , 8,101, 2 * * тысяча двадцать-два, 3,4,11,15 2 , 4,20,21 2 30

РЕДАКТИРОВАТЬ2: ИЛИ

1, 4 , 8,101,2,3, 4 , 11,15 2 , 4,20,21 2 30

ИЛИ что-нибудь еще НЕ в порядке.

Есть ли какой-нибудь алгоритм для ускорения этой вещи (если у меня есть тысячи массивов с сотнями элементов в каждом)?

Чтобы было понятно - решение должно содержать наименьшее количество различных элементов, а группы (из одинаковых номеров) должны быть сгруппированы от первых - более крупные до последних - наименьшие.Таким образом, в верхнем примере 4,4,4,2 лучше, чем 4,2,2,2, потому что в первом примере группа из 4 больше, чем группа из 2 * .

РЕДАКТИРОВАТЬ: КомуБолее конкретно.Решение должно содержать наименьшее количество различных элементов , и эти элементы должны быть сгруппированы от первого до последнего .Так что, если у меня есть три массива, как

1,2,31,4,54,5,6

Решение - 1,1,4 или 1,1,5 или 1,1,6 НЕ 2,5,5, потому что 1 имеют большую группу (две из них), чем 2 (только одна).

Спасибо.

РЕДАКТИРОВАТЬ3: Я не могу быть более конкретным: (

РЕДАКТИРОВАТЬ 4: @spintheblack 1,1,1,2,4 - правильное решение, потому что число используется первымвремя (скажем, в позиции 1) не может быть использовано позже (за исключением того, что оно находится в той же группе 1). Я бы сказал, что группировка имеет «приоритет»? Кроме того, я не упомянул об этом (извините за это), ночисла в массивах НЕ сортируются каким-либо образом, я напечатал это таким образом в этом посте, потому что мне было легче следить.

Ответы [ 5 ]

3 голосов
/ 19 февраля 2011

Вот подход, который вы хотите использовать, если arrays - это массив, содержащий каждый отдельный массив.

  1. Начиная с i = 0
  2. current = arrays[i]
  3. Цикл i от i+1 до len(arrays)-1
  4. new = current & arrays[i] (установить пересечение, найти общие элементы)
  5. Если в new есть какие-либо элементы, выполнитешаг 6, в противном случае перейдите к 7
  6. current = new, вернитесь к шагу 3 (цикл продолжения)
  7. напечатайте или выведите элемент из текущего current = arrays[i], вернитесь к шагу 3 (цикл продолжения)

Вот реализация Python:

def mce(arrays):
  count = 1
  current = set(arrays[0])
  for i in range(1, len(arrays)):
    new = current & set(arrays[i])
    if new:
      count += 1
      current = new
    else:
      print " ".join([str(current.pop())] * count),
      count = 1
      current = set(arrays[i])
  print " ".join([str(current.pop())] * count)

>>> mce([[1, 4, 8, 10], [1, 2, 3, 4, 11, 15], [2, 4, 20, 21], [2, 30]])
4 4 4 2
2 голосов
/ 19 февраля 2011

Это теперь превратилось в проблему построения графиков с поворотом.

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

т.е.этот список сетов:

1,4,8,10           <-- stop A
1,2,3,4,11,15      <-- stop B
2,4,20,21          <-- stop C
2,30               <-- stop D, destination

Ему нужно выбрать линии, доступные на его остановке выхода, и остановке его прибытия, поэтому, например, он не может выбрать 10 из остановки A, потому что 10 не делаетперейти к остановке B.

Итак, это набор доступных линий и остановок, на которых они останавливаются:

A     B     C     D
line 1  -----X-----X-----------------
line 2  -----------X-----X-----X-----
line 3  -----------X-----------------
line 4  -----X-----X-----X-----------
line 8  -----X-----------------------
line 10 -----X-----------------------
line 11 -----------X-----------------
line 15 -----------X-----------------
line 20 -----------------X-----------
line 21 -----------------X-----------
line 30 -----------------------X-----

Если мы считаем, что рассматриваемая линия должна проходить как минимум между 2последовательные остановки, позвольте мне выделить возможные варианты линий с одинаковыми знаками:

             A     B     C     D
line 1  -----X=====X-----------------
line 2  -----------X=====X=====X-----
line 3  -----------X-----------------
line 4  -----X=====X=====X-----------
line 8  -----X-----------------------
line 10 -----X-----------------------
line 11 -----------X-----------------
line 15 -----------X-----------------
line 20 -----------------X-----------
line 21 -----------------X-----------
line 30 -----------------------X-----

Затем ему нужно выбрать путь, который переносит его из А в D, с минимальным числом переключателей линий.

Поскольку он объяснил, что сначала ему нужны самые длинные поездки, следующая последовательность кажется наилучшим решением:

  • взять линию 4 от остановки A до остановки C, а затем переключиться на линию 2 с C на D

Пример кода:

stops = [
    [1, 4, 8, 10],
    [1,2,3,4,11,15],
    [2,4,20,21],
    [2,30],
]

def calculate_possible_exit_lines(stops):
    """
    only return lines that are available at both exit
    and arrival stops, discard the rest.
    """

    result = []
    for index in range(0, len(stops) - 1):
        lines = []
        for value in stops[index]:
            if value in stops[index + 1]:
                lines.append(value)
        result.append(lines)
    return result

def all_combinations(lines):
    """
    produce all combinations which travel from one end
    of the journey to the other, across available lines.
    """

    if not lines:
        yield []
    else:
        for line in lines[0]:
            for rest_combination in all_combinations(lines[1:]):
                yield [line] + rest_combination

def reduce(combination):
    """
    reduce a combination by returning the number of
    times each value appear consecutively, ie.
    [1,1,4,4,3] would return [2,2,1] since
    the 1's appear twice, the 4's appear twice, and
    the 3 only appear once.
    """

    result = []
    while combination:
        count = 1
        value = combination[0]
        combination = combination[1:]
        while combination and combination[0] == value:
            combination = combination[1:]
            count += 1
        result.append(count)
    return tuple(result)

def calculate_best_choice(lines):
    """
    find the best choice by reducing each available
    combination down to the number of stops you can
    sit on a single line before having to switch,
    and then picking the one that has the most stops
    first, and then so on.
    """

    available = []
    for combination in all_combinations(lines):
        count_stops = reduce(combination)
        available.append((count_stops, combination))
    available = [k for k in reversed(sorted(available))]
    return available[0][1]

possible_lines = calculate_possible_exit_lines(stops)
print("possible lines: %s" % (str(possible_lines), ))
best_choice = calculate_best_choice(possible_lines)
print("best choice: %s" % (str(best_choice), ))

Этот код печатает:

possible lines: [[1, 4], [2, 4], [2]]
best choice: [4, 4, 2]

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

Таким образом, маршрут:

  • Перейдите на линию 4 востановитесь A и езжайте на этом до остановки B, затем до остановки C
  • Перейдите на линию 2 на остановке C и езжайте по ней до остановки D

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

Тем не менее, я не беспокоюсь больше об этом вопросе.ФП продемонстрировал полную неспособность изложить свой вопрос в четкой и сжатой форме, и я боюсь, что любые исправления в приведенном выше тексте и / или коде для учета последних комментариев будут только вызывать больше комментариев, что приводит к еще одной версиивопрос и так до бесконечности.ОП пошел на все, чтобы избежать ответов на прямые вопросы или объяснить проблему.

2 голосов
/ 19 февраля 2011

Если все являются числовыми списками, и все отсортированы, затем,

  1. Преобразовать в массив растровых изображений.
  2. Сохраняйте «И» битовые карты, пока не достигнете нуля.Позиция 1 в предыдущем значении указывает на первый элемент.
  3. Перезапустите шаг 2 со следующего элемента
1 голос
/ 19 февраля 2011

Я предполагаю, что «отдельные элементы» не обязательно должны быть различными, они могут повторяться в конечном решении.То есть, если указано [1], [2], [1], то очевидный ответ [1, 2, 1] допускается.Но мы бы посчитали, что это имеет 3 различных элемента.

Если так, то вот решение Python:

def find_best_run (first_array, *argv):
    # initialize data structures.
    this_array_best_run = {}
    for x in first_array:
        this_array_best_run[x] = (1, (1,), (x,))

    for this_array in argv:
        # find the best runs ending at each value in this_array
        last_array_best_run = this_array_best_run
        this_array_best_run = {}

        for x in this_array:
            for (y, pattern) in last_array_best_run.iteritems():
                (distinct_count, lengths, elements) = pattern
                if x == y:
                    lengths = tuple(lengths[:-1] + (lengths[-1] + 1,))
                else :
                    distinct_count += 1
                    lengths = tuple(lengths + (1,))
                    elements = tuple(elements + (x,))

                if x not in this_array_best_run:
                    this_array_best_run[x] = (distinct_count, lengths, elements)
                else:
                    (prev_count, prev_lengths, prev_elements) = this_array_best_run[x]
                    if distinct_count < prev_count or prev_lengths < lengths:
                        this_array_best_run[x] = (distinct_count, lengths, elements)

    # find the best overall run
    best_count = len(argv) + 10 # Needs to be bigger than any possible answer.
    for (distinct_count, lengths, elements) in this_array_best_run.itervalues():
        if distinct_count < best_count:
            best_count = distinct_count
            best_lengths = lengths
            best_elements = elements
        elif distinct_count == best_count and best_lengths < lengths:
            best_count = distinct_count
            best_lengths = lengths
            best_elements = elements

    # convert it into a more normal representation.                
    answer = []
    for (length, element) in zip(best_lengths, elements):
        answer.extend([element] * length)

    return answer

# example
print find_best_run(
    [1,4,8,10],
    [1,2,3,4,11,15],
    [2,4,20,21],
    [2,30]) # prints [4, 4, 4, 30]

Вот объяснение.Словари ...this_run имеют ключи, которые являются элементами в текущем массиве, и имеют значения, которые являются кортежами (distinct_count, lengths, elements).Мы пытаемся свести к минимуму Different_count, а затем максимизировать длину (длина является кортежем, поэтому он предпочтет элемент с наибольшим значением в первом месте) и отслеживаем элементы для конца.На каждом шаге я создаю все возможные прогоны, которые являются комбинацией прогона до предыдущего массива с этим элементом, следующим по порядку, и нахожу, какие из них лучше всего подходят для текущего.Когда я добираюсь до конца, я выбираю наилучший возможный общий прогон, затем превращаю его в обычное представление и возвращаю его.

Если у вас есть N массивы длины M, это должно занять O(N*M*M)время бежать.

1 голос
/ 19 февраля 2011

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

У нас есть N массивов, и мы пытаемся найти «наиболее распространенное» значение среди всех массивов, когда одно значение выбирается из каждого массива.Есть несколько ограничений: 1) Нам нужно наименьшее количество различных значений. 2) Наиболее распространенным является максимальная группировка похожих букв ( меняется для ясности сверху ).Таким образом, 4 t и 1 p бьют 3 x's 2 y *

Я не думаю, что любую проблему можно решить жадно - вот контрпример [[1,4], [1,2], [1,2], [2], [3,4]] - жадный алгоритм выберет [1,1,1,2,4] (3 различных числа) [4,2,2,2,4] (два различных числа)

Это похоже на проблему сопоставления двудольных, но я все еще придумываю формулировку ..

EDIT : игнорировать;Это другая проблема, но если кто-то может понять это, я был бы действительно заинтересованпример проблемы набора ударов, см. http://en.wikipedia.org/wiki/Vertex_cover#Hitting_set_and_set_cover. По существу, левая часть двудольного графа будет представлять собой массивы, а правая часть будет представлять собой числа, ребра будут отображаться между массивами, содержащими каждое число.К сожалению, это NP завершено, но описанные выше жадные решения по сути являются наилучшим приближением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...