Связать последний элемент списка с равным первым элементом другого списка

это уже заняло у меня половину дня... Я пытаюсь связать последний и первый элементы вложенного списка. Я проиллюстрирую это на простом примере:

input = [(8, 2), (8, 5), (2, 12), (12, 13), (5, 6), (6, 7), (13, 14), 
         (14, 3), (7, 4), (3, 4)]
result = [(8, 2), (2, 12), (12, 13), (13, 14), (14, 3), (3, 4), (4, 7),
          (7, 6), (6, 5), (5, 8)]

Я пробовал следующий код:

def ChainNew(L):
    R = copy.deepcopy(L)
    for pair in R:
        pair.reverse()
    stop = len(L)
    chain = [L.pop(0)]
    L = sorted(L)
    R = sorted(R)
    while len(chain) < stop:
        for i, j in zip(L, R):
            if i[0] == chain[len(chain)-1][1]:
                chain.append((i))
                L.remove(i)
                R.remove(i[::-1])
                break
            if j[0] == chain[len(chain)-1][1]:
                chain.append((j))
                L.remove(j[::-1])
                R.remove(j)
                break
    return chain

но это не только неэффективно, но и содержит ошибки: кажется, что он не возвращает все элементы исходного списка. Например:

L =  [[20, 56], [23, 24], [23, 12], [22, 21], [26, 48], [26, 24],
      [55, 48], [55, 39], [56, 40], [19, 6], [19, 12], [6, 15],
      [40, 39], [21, 57], [14, 15], [14, 16], [57, 50], [45, 9],
      [45, 53], [18, 42], [18, 9], [38, 53], [38, 44], [50, 42],
      [16, 17], [17, 35], [36, 37], [36, 35], [37, 44]]

return = [[20, 56], [56, 40], [40, 39], [39, 55], [55, 48], [48, 26],
          [26, 24], [24, 23], [23, 12], [12, 19], [19, 6], [6, 15], 
          [15, 14], [14, 16], [16, 17], [17, 35], [35, 36], [36, 37], 
          [37, 44], [44, 38], [38, 53], [53, 45], [45, 9], [9, 18], 
          [18, 42], [42, 50], [50, 57]]

Должен быть более простой способ сделать это...

РЕДАКТИРОВАНИЕ: извините! Я забыл упомянуть, что целые числа внутри каждого списка (пары) можно поменять местами. Например, от (7, 4) до (4, 7).

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

РЕДАКТИРОВАТЬ СНОВА: правильный результат списка L будет таким:

return = [[22, 21], [21, 57], [57, 50], [50, 42], [42, 18], [18, 9],
          [9, 45], [45, 53], [53, 38], [38, 44], [44, 37], [37, 36],
          [36, 35], [35, 17], [17, 16], [16, 14], [14, 15], [15, 6],
          [6, 19], [19, 12], [12, 23], [23, 24], [24, 26], [26, 48],
          [48, 55], [55, 39], [39, 40], [40, 56], [56, 20]]

Спасибо!


person grasshopper    schedule 05.10.2013    source источник
comment
Вы имеете в виду, что хотите переупорядочить свой список так, чтобы первый элемент в элементе был равен последнему элементу в предыдущем элементе?   -  person Ofir Israel    schedule 05.10.2013
comment
Да, это почти все. Может быть, это было бы лучшим названием для вопроса! :)   -  person grasshopper    schedule 05.10.2013
comment
Я думаю, вы ищете эйлеров след, где ваши кортежи описывают ребра на графе (цепь если вы хотите закончить там, где вы начали). В Python есть множество доступных реализаций для изучения.   -  person DSM    schedule 05.10.2013
comment
Я добавил ответ, но ваш пример в верхней части сообщения не содержит (4,7) во входных данных. Итак, вы также хотите изменить порядок внутренних элементов?   -  person Ofir Israel    schedule 05.10.2013
comment
Ах да забыл упомянуть. Элементы внутри каждого списка можно поменять местами. Я собираюсь отредактировать вопрос, чтобы уточнить это.   -  person grasshopper    schedule 05.10.2013
comment
Но каков порядок? Я имею в виду, если есть выбор между исходным списком и замененным значением - как выбирает алгоритм? Например, в вашем списке, когда он достигает «6», вы можете выбрать [6,15] или использовать инверсию для [19,6]..   -  person Ofir Israel    schedule 05.10.2013
comment
Если ваша цель состоит в том, чтобы упорядочить индексы полилинии, то почему бы просто не выбрать функцию упорядочивания и не упорядочить индексы по ней?   -  person Ofir Israel    schedule 05.10.2013
comment
Ну их можно свободно поменять местами. Но важно то, что в конце концов все числа внутри каждой пары связаны так, что пара [1] == (пара + 1) [0] И длина результирующего списка такая же, как исходный список.   -  person grasshopper    schedule 05.10.2013
comment
Я не уверен, что я вас понял. В любом случае полилинии у меня нет (это то, что я надеюсь получить в итоге). У меня есть только ребра и индексы вершин ребер.   -  person grasshopper    schedule 05.10.2013


Ответы (2)


Как насчет:

def path(a, b):
    if not b:
        return a
    for n, (p, q) in enumerate(b):
        if p == a[-1][1]:
            return path(a + [(p, q)], b[:n] + b[n+1:])
        if q == a[-1][1]:
            return path(a + [(q, p)], b[:n] + b[n+1:])
    raise ValueError("no path", a, b)

L = [(8, 2), (8, 5), (2, 12), (12, 13), (5, 6), (6, 7), (13, 14), (14, 3), (7, 4), (3, 4)]
print path([L[0]], L[1:])
#[(8, 2), (2, 12), (12, 13), (13, 14), (14, 3), (3, 4), (4, 7), (7, 6), (6, 5), (5, 8)]
person georg    schedule 05.10.2013
comment
Кажется, это прекрасно работает! Мне придется изучить то, что вы сделали, так как я немного новичок :) - person grasshopper; 05.10.2013

Вот решение, которое находит все возможные цепочки:

def find_chains(points):
    """For a list of points, take the first point and finds all chains that
    could be made from the following points.

    Returns a list of lists of tuples. Each sublist is a possible chain.
    """

    def find_solutions(start, choices):
        """For a starting point and a list of choices, return all choices that
        might succeed that point.

        Returns a list of tuples. These tuples contain the next choice, and all
        of the remaining points should that choice be used.
        """

        ret = []
        for i, possible_choice in enumerate(choices):
            # Determine whether or not the choice is appropriate.
            added_choice = None
            if start[1] == possible_choice[0]:
                added_choice = possible_choice
            elif start[1] == possible_choice[-1]:
                added_choice = possible_choice[::-1]

            # If it is, add a tuple of that choice and all other choices to the
            # return list.
            if added_choice:
                ret.append((
                    added_choice,
                    [
                        k
                        for j, k
                        in enumerate(choices)
                        if i != j
                    ]
                ))

        return ret

    solutions = []
    start = points[0]
    other_points = points[1:]
    if not other_points:
        # If there aren't any remaining points, then the only possible path is
        # that of just the first point.
        return [[start]]

    # Find all chains through `other_points` and add them to our solutions.
    for next_point, remaining_points in find_solutions(start, other_points):
        for subchain in find_chains([next_point] + remaining_points):
            solutions.append([start] + subchain)
    return solutions

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

test = [(1, 2), (2, 3), (2, 4), (3, 4)]
print(find_chains(test))
# Finds two possible solutions:
# [[(1, 2), (2, 3), (3, 4), (4, 2)],
#  [(1, 2), (2, 4), (4, 3), (3, 2)]]

test = [(8, 2), (8, 5), (2, 12), (12, 13), (5, 6), (6, 7), (13, 14),
        (14, 3), (7, 4), (3, 4)]
print(find_chains(test))
# Finds the only solution:
# [[(8, 2), (2, 12), (12, 13), (13, 14), (14, 3), (3, 4), (4, 7), (7, 6), (6, 5), (5, 8)]]

test = [(1, 2), (3, 4)]
print(find_chains(test))
# There weren't any solutions:
# []
person Waleed Khan    schedule 05.10.2013
comment
Большой! На данный момент все полилинии у меня открытые не самопересекающиеся. В будущем, я думаю, я мог бы найти ваш код полезным для обнаружения таких событий самопересечения. - person grasshopper; 05.10.2013