Избегать псевдонимов объектов в python?

Я пытаюсь написать функцию, чтобы проверить, отсортирован ли список (возвращая True или False). Как я могу избежать нескольких переменных, указывающих на одно и то же?

def is_sorted(t):
    a = t
    a.sort()

Когда я это делаю, он сортирует как a, так и t. Как я могу этого избежать?


person Johnny    schedule 06.01.2011    source источник
comment
Как я могу этого избежать? Найдя лучший дизайн. Это, возможно, самый медленный способ сделать это. Единственное, что может быть медленнее, это реализация вашей собственной версии sort.   -  person S.Lott    schedule 07.01.2011
comment
В ответах, связанных с сортировкой списка, есть что-то эпистемологическое: совершенно маловероятно, что программа будет использовать результат, отличный от True, после того, как список уже отсортирован. Только в очень редких случаях ответ на исходный is_sorted() запрос все еще будет актуален. Кроме того, сортировка уже отсортированного списка достаточно близка к O(n), поэтому зачем заморачиваться с запросами вместо того, чтобы просто идти вперед и вызывать sort() или sorted()?   -  person Apalala    schedule 07.01.2011


Ответы (6)


Вот способ O (n) сделать это

>>> from itertools import islice, izip
>>> def is_sorted(L):
...     return all(i<=j for i,j in izip(L, islice(L,1,None)))
... 
>>> is_sorted(range(50))
True
>>> is_sorted(range(50)+[20])
False

Это короткое замыкание, поэтому, если список не отсортирован в самом начале, он будет очень быстрым.

Вот простая программа для сравнения некоторых альтернатив

import random
import time
from itertools import islice, izip

def is_sorted1(L):  # 0.0006s
    return all(i<=j for i,j in izip(L, islice(L,1,None)))

def is_sorted2(L):  # 0.117s
    return all(L[i] < L[i+1] for i in range(len(L)-1) )

def is_sorted3(L):  # 2.344s
    return L == sorted(L)

def is_sorted4(L):  # 0.0002s
    return all(L[i] < L[i+1] for i in xrange(len(L)-1) )

A = [range(random.randrange(100000)) for i in range(100)]
for a in A:
    random.shuffle(a)

for f in is_sorted1, is_sorted2, is_sorted3, is_sorted4:
    s=time.time()
    for a in A:
        f(a)
    print time.time() - s
person John La Rooy    schedule 06.01.2011
comment
Ну, я сомневаюсь, что Джонни понимает, что это делает ;p Может быть, это поможет: izip(L, islice(L,1,None)) надвигает окно на L. Это дает последовательность вроде [(L[0],L[1]), (L[1],L[2]), ...] - person Jochen Ritzel; 07.01.2011
comment
Это может быть просто придиркой, но индексирование списка Python должно быть намного дешевле, чем его нарезка и архивирование. - person Apalala; 07.01.2011
comment
@Apalala: должно быть намного дешевле? Пожалуйста, используйте timeit и опубликуйте сравнение, показывающее, что произошло на самом деле, а не то, что должно произойти. - person S.Lott; 07.01.2011
comment
@Apalala: индексирование выполняется быстрее при использовании CPython2.6 на этом компьютере, но обратите внимание, что было важно использовать xrange вместо range - person John La Rooy; 07.01.2011
comment
@ S.Lott Не нужно все измерять. Характеристики производительности интерпретатора Python и стандартных библиотек хорошо задокументированы, и Big O расскажет об остальном. Мое использование range вместо xrange было ошибкой новичка (xrange устарел в Python 3). - person Apalala; 07.01.2011
comment
@Apalala: range против xrange хорошо известно. индексирование списка Python должно быть намного дешевле, чем его нарезка и архивирование, что не так хорошо известно. Без фактических цифр это - по сути - предположение. С реальными цифрами это теория + доказательства == наука. - person S.Lott; 07.01.2011
comment
@ S.Lott Гораздо более известен тот факт, что проверка того, отсортирована ли последовательность, должна быть намного дешевле, чем ее сортировка. ;-) - person Apalala; 07.01.2011

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


Чтобы ответить на ваш конкретный вопрос, вы можете использовать copy.copy (или использовать синтаксис фрагмента [:]), чтобы создать копию исходного списка:

import copy

def is_sorted(t):
    a = copy.copy(t) # could also do: a = t[:]
    a.sort()
    return a == t

Однако лучшим методом было бы использование функции sorted для возврата отсортированной копии списка:

def is_sorted(t):
    return sorted(t) == t

Or: is_sorted = lambda t: sorted(t) == t

person Alex Vidal    schedule 06.01.2011
comment
deepcopy - это излишество, учитывая спецификации. copy или [:] будет достаточно, поскольку элементы в списке не изменяются. - person aaronasterling; 06.01.2011
comment
@aaronasterling: Хороший вопрос. Я изменю свой ответ, чтобы удалить ссылки на глубокую копию. - person Alex Vidal; 06.01.2011
comment
Проверка того, отсортирована ли последовательность, — это O(n) сравнений и O(0) обменов. Сортировка для нормального теста - O (n log (n)). - person Apalala; 06.01.2011
comment
@Apalala, хотя код Python остается примерно в 100 раз медленнее, чем код C, на практике часто быстрее злоупотреблять сортировкой таким образом, потому что журнал (n) не улавливает 100, пока n не станет очень большим. Если список очень несортированный, цикл python должен быть намного быстрее. - person John La Rooy; 06.01.2011
comment
@gnibbler Обмен стоит на порядки больше, чем сравнение, а O (n) предназначен только для полностью отсортированных последовательностей. Есть большая константа, которая умножает n log(n) при сортировке. Решение, которое вы дали в своем ответе, является правильным. - person Apalala; 07.01.2011

создав копию вашего списка примерно так:

def is_sorted(t):  
    a = t[:]  # create a copy
    a.sort()
person mouad    schedule 06.01.2011
comment
Вы не должны поощрять такой плохой дизайн, отвечая на вопрос напрямую. Это плохой вопрос. - person S.Lott; 07.01.2011
comment
@S.Lott: Спасибо за совет искренне :) - person mouad; 07.01.2011
comment
Я должен сказать, что хотя ваш ответ очень хороший, вопрос был очень плохим. - person S.Lott; 07.01.2011

Кредит на этот минимальный ответ принадлежит @gnibbler

is_sorted = all(q[i] < q[i+1] for i in xrange(len(q)-1) )
person Apalala    schedule 06.01.2011
comment
это повторяет весь список, даже если первый элемент больше второго. Вы можете использовать здесь all() вместо сокращения, и код тоже будет понятнее. all(q[i] < q[i+1] for i in range(len(q)-1)) - person John La Rooy; 06.01.2011
comment
@gnibbler Он не выполняет итерацию всего списка, потому что это выражение генератора (а не генератор списка), а оператор and_ оценивает методом короткого замыкания: он будет потреблять входной итератор, пока он находит True. Решение с all лучше. - person Apalala; 06.01.2011
comment
reduce() не знает, что он может остановиться, когда найдет True, поэтому выражение генератора должно быть завершено до конца. - person John La Rooy; 07.01.2011
comment
+1: не пытайтесь сортировать и сравнивать. Просто оцените необходимый предикат. Намного быстрее. И проще тоже. - person S.Lott; 07.01.2011
comment
@gnibbler Ты прав насчет reduce(). За мой ответ уже проголосовали, поэтому я исправил его. Комментарии и ваш ответ содержат след изменений, кроме моего оригинального reduce(operator.and_, q[i] < q[i+1] for i in range(len(q)-1)) - person Apalala; 07.01.2011

Вы можете либо сделать копию t, а затем отсортировать, например: a = t[:], либо использовать sorted< /a>, который возвращает новый отсортированный список.

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

person Seth    schedule 06.01.2011

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

person David Heffernan    schedule 06.01.2011
comment
Хеффеман, теоретически ты прав. Однако реализация сортировки Python настолько быстра по сравнению с реальным кодом Python, что простое сравнение списка с отсортированной версией самого себя часто бывает быстрее. - person aaronasterling; 06.01.2011
comment
@aaron Точка взята. Должна быть встроенная функция, которая делает это, чтобы избежать накладных расходов на создание копии списка только для проверки его монотонности! О, я чувствую себя грязным, просто думая об этом. - person David Heffernan; 06.01.2011
comment
В некоторых случаях это может быть хорошо, но я понимаю, почему этого нет. В большинстве случаев, если кто-то хочет знать, отсортирована ли последовательность, это потому, что он хочет, чтобы последовательность была отсортирована, и в этом случае можно просто использовать list.sort или sorted. Если это сортировка только в несортированной ситуации, то TimSort (алгоритм сортировки, используемый Python) равен O (n) в отсортированном списке, с чего начинается проверка на монотонность. Итак, еще раз, просто отсортируйте список. - person aaronasterling; 06.01.2011