Проверка совместимых итоговых заказов по списку Python - PullRequest
1 голос
/ 03 июня 2011

Я использую IPython shell здесь.

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

In [1]: L1 = [1,3,4,5,2]

In [2]: L2 = [1,3,5,5,1]

Я бы хотел сказать, что L1 и L2 совместимы в том смысле, что порядок, генерируемый индексами возрастающего порядка элементов, совместим.

То есть L1 дает 0 <4 <1 <2 <3, в то время как <code>L2 дает {0,4} <1 <{2,3}. (Если stackoverflow принимает jsmath или MathJax, это было бы проще, мои извинения.) </p>

Редактировать: Как указано ниже, это не совсем проверяет, являются ли два заданных элемента <или <= в обоих. Мне нравится пример @ Cosmologicon о том, что <code>[1,2] и [1,1] совместимы, равно как и [1,1] и [2,1], но [2,1] и [1,2] - нет. Я надеюсь, что это проясняет, что я имею в виду.

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

In [3]: L3 = [1,2,3,4,5]

In [4]: L4 = [1,2,4,2,5]

Надеюсь, ясно, что порядок, заданный L3, равен 0 <1 <2 <3 <4; порядок, заданный <code>L4, равен 0 <{1,3} <2 <4, и несовместимость заключается в том, что в то время как 1 <= 3 в обоих порядках, 2 <3 в одном, а 3 <2 в другом. </p>

Другой, более сложный пример - совместимы ли [1,3,5,5,1] и [1,2,2,3,2]. Нестрогие итоговые заказы: {0,4} <1 <{2,3} и 0 <{1,2,4} <3 </p>

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

Отказ от ответственности с первого постера: это , а не вопрос о сортировке :) Я довольно долго искал это, но на самом деле нашел только больше вопросов типа программирования, которые почти всегда о сортировке или сравнении значений; это немного более тонко. На самом деле, это действительно математическое приложение, поэтому многим людям оно может показаться не таким «полезным», хотя оно будет весьма полезным для меня. Во всяком случае, это выше моего нынешнего уровня навыков, чтобы быстро это исправить, хотя я надеюсь, что когда-нибудь это станет «очевидным» для меня. Я тоже не думаю, что в этом есть что-то в itertools, хотя я бы хотел, чтобы меня ошиблись.

Ответы [ 6 ]

2 голосов
/ 03 июня 2011

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

Что-то вроде:

L1order = [t[1] for t in sorted(zip(L1, range(len(L1))))]
L2order = [t[1] for t in sorted(zip(L2, range(len(L2))))]
L1order == L2order

Превращение в функцию должно быть тривиальным.

1 голос
/ 03 июня 2011

Я сделал следующее, расширив ответ Шанга. Он учитывает особый факт, когда два значения одинаковы. Простое упорядочение списков и их сравнение может дать неправильный результат. Например, если порядок в списке 1 равен 0 <1 <2, а порядок в списке 2 равен 0 <1 <= 2, упорядочение второго списка может дать в результате [0,1,2] и [0,2 , 1], и в последнем случае метод Шанга потерпит неудачу. Это зависит от поведения процедуры сортировки. </p>

import operator

def order_indexes(l):
    tmp = list(enumerate(l))
    tmp.sort(key=operator.itemgetter(1))
    return map(operator.itemgetter(0), tmp)

def are_compatible(l1, l2):
    # Order one list, retaining the indexes
    ordered = order_indexes(l1)
    # For each pair of indexes on the list
    for i in xrange(len(ordered) - 1):
        pair = (ordered[i], ordered[i + 1])
        # See if the pairs in the other list are compatible
        # If a1 <= b1 then a2 must be <= b2 
        if l2[pair[0]] > l2[pair[1]]:
            return False
    # If all pairs are compatible, then the lists are compatible
    return True

if __name__ == '__main__':
    l1 = [1,3,4,5,2]
    l2 = [1,3,5,5,1]
    l3 = [1,2,3,4,5]
    l4 = [1,2,4,2,5]
    print "L1 X L2 ",are_compatible(l1, l2)
    print "L2 X L1 ",are_compatible(l2, l1)
    print "L3 X L4 ",are_compatible(l3, l4)
    print "L4 X L3 ",are_compatible(l4, l3)
1 голос
/ 03 июня 2011

Я не на 100% уверен, что это то, что вам нужно, но оно работает для приведенных вами примеров:

import operator

def compatible(l1,l2):
    return ordered_indices(l1) == ordered_indices(l2)

def ordered_indices(l):
    tmp = list(enumerate(l))
    tmp.sort(key=operator.itemgetter(1))
    return map(operator.itemgetter(0), tmp)
>>> compatible([1,3,4,5,2], [1,3,5,5,1])
True
>>> compatible([1,2,3,4,5], [1,2,4,2,5])
False

Обновленная версия:

import operator, itertools

def compatible(l1,l2):
    if len(l1) != len(l2): return False
    i1 = ordered_indices(l1)
    i2 = ordered_indices(l2)
    g1 = None
    g2 = None
    while i1 and i2:
        g1 = g1 or i1.pop(0)
        g2 = g2 or i2.pop(0)
        if len(g1) > len(g2):
            g1,g2 = g2,g1
            i1,i2 = i2,i1
        x = g1.pop()
        if x not in g2:
            return False
        g2.remove(x)
    return True


def ordered_indices(l):
    tmp = list(enumerate(l))
    value = operator.itemgetter(1)
    index = operator.itemgetter(0)
    tmp.sort(key=value)
    groups = itertools.groupby(tmp, value)
    return [set(map(index, g)) for k, g in groups]
>>> compatible([1,3,5,5,1],[1,2,2,3,2])
True
0 голосов
/ 03 июня 2011

[РЕДАКТИРОВАТЬ] Хорошо, я тупой.Очевидно, это можно сделать еще проще:

def compatible_ordering(xs, ys):
    return all(
        y1 <= y2
            for (_, y1), (_, y2) in pairwise(sorted(izip(xs, ys)))
    )

[/ EDIT]

Вот решение O (n * log n):

from itertools import izip, tee

def pairwise(iterable):
    a, b = tee(iterable)
    next(b)
    return izip(a, b)

def compatible_ordering(xs, ys):
    return all(
        x1 == x2 or y1 <= y2
            for (x1, y1), (x2, y2) in pairwise(sorted(izip(xs, ys)))
    )

Это в основномодин вкладыш, если не считать рецепт pairwise() от itertools .Чувак, ты должен любить Python за это.

Кстати, обратите внимание на сходство с неправильным решением в конце.

Как это работает, может быть легче объяснить, если мы переписаем алгоритм в болеепроцедурная форма:

def compatible_ordering(xs, ys):             # line 0
    xys = zip(xs, ys)                        # line 1
    xys.sort()                               # line 2
    for (x1, y1), (x2, y2) in pairwise(xys): # line 3
        if x1 < x2 and y1 > y2:              # line 4
            return False                     # line 5
    return True                              # line 6

На этот раз мы не пытаемся выяснить, выполняется ли условие для всех элементов (= success ), но вместо этого выполняется условие для некоторого элемента (= Ошибка ), поэтому формула тестирования в основном сводится на нет.

Теперь для каждой пары соседних кортежей ((x1, y1), (x2, y2)):

  • Всегда x1 <= x2, так как мы отсортировали их таким образом.Это означает, что если x1 != x2, то x1 < x2.

  • Если x1 == x2, мы знаем, что y1 <= y2, опять же, потому что мы их отсортировали.

  • Если x1 < x2 и y1 <= y2, оба (x1, x2) и (y1, y2) имеют одинаковый порядок, и мы продолжаем.

  • В противном случае, если x1 < x2 и y1 > y2наши два списка имеют несовместимые упорядочения, и мы return False.

  • Если мы закончили итерацию и не обнаружили несовместимости, мы return True.

IOW: сортировка создает порядок для ys, определяемый xs и самим ys для равных элементов в xs (поскольку равные элементы xs не налагают никакого порядкана ys).Тогда все, что нам нужно сделать, это проверить, действительно ли заказано ys.

Вот пример.После строки 0 у нас есть, например:

xs == [4, 3, 4, 2, 5, 4, 0, 2, 0, 5]
ys == [4, 1, 5, 1, 5, 5, 2, 2, 1, 3]

В строке 2 мы архивируем их и получаем:

xys == [(4, 4), (3, 1), (4, 5), (2, 1), (5, 5), (4, 5), (0, 2), (2, 2), (0, 1), (5, 3)]

В строке 3 мы сортируем:

xys == [(0, 1), (0, 2), (2, 1), (2, 2), (3, 1), (4, 4), (4, 5), (4, 5), (5, 3), (5, 5)]

ВВ строке 4 мы перебираем все пары соседних кортежей отсортированного списка и тестируем в строке 5:

  x1  y1     x2  y2       x1     x2      y1    y2
( 0 , 1 ), ( 0 , 2 )  -->  0  <  0  and  1  >  2   -->  False  -->  continue
( 0 , 2 ), ( 2 , 1 )  -->  0  <  2  and  2  >  1   -->  True   -->  return False

КСТАТИ # 2: вполне возможно, что эта версия быстрее, чем oneliner.

[EDIT]

Ниже был мой первый и НЕПРАВИЛЬНЫЙ ответ на вопрос ОП.Тем не менее, я не удаляю его, как пример того, что происходит, если вы публикуете без прочтения.

Вот решение O (n):

def compatible_ordering(xs, ys):
    return all(
        (x1 <= x2) == (y1 <= y2)
            for (x1, x2), (y1, y2) in izip(pairwise(xs), pairwise(ys))
    )
0 голосов
/ 03 июня 2011

То есть L1 дает 0 <4 <1 <2 <3, в то время как L2 дает 0 <= 4 <1 <2 <= 3.Можно видеть, что если два из этих индексов <= друг друга в одном списке, это верно в обоих списках. </p>

Извините, приведенное здесь определение совместимости не соответствует вашему примеру.В L2 4 <= 0, но то же самое не верно в L1.Я подозреваю, что определение, которое вы хотели дать, это: если

0 голосов
/ 03 июня 2011

Один из возможных алгоритмов выглядит следующим образом:

def compatible
    temp1=L1[:]
    temp2=L2[:]
    max1=max(L1)+1
    max2=max(L2)+2
    order1=[]
    for i in range(len(L1)):
        pos=t1.index(min(t1))
        order1.append(pos)
        t1[pos]=max1
    for i in range(len(L2)):
        pos=t2.index(min(t2))
        order2.append(pos)
        t2[pos]=max2
    return order1==order2

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

...