Avoid object aliasing in python?
我正在尝试编写一个函数来检查列表是否已排序(返回True或False)。 如何避免多个变量指向同一个东西?
1 2 3
| def is_sorted(t):
a = t
a.sort() |
当我这样做时,它会对a和t进行排序。 我怎么能避免这个?
-
"我怎么能避免这种情况?" 通过寻找更好的设计。 这可能是最慢的方式。 唯一可能更慢的是实现自己的sort版本。
-
关于对列表进行排序的答案有一些认识论:在列表已经排序之后,程序完全不可能对True以外的结果有任何用处。 只有在非常罕见的情况下,原始is_sorted()查询的答案仍然是相关的。 此外,对已经排序的列表进行排序足够接近O(n),那么为什么还要查询,而不是只是继续调用sort()或sorted()?
这是O(n)方法
1 2 3 4 5 6 7 8
| >>> 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 |
它是短路的,所以如果列表在开头附近未分类,它将非常快
这是一个比较一些替代方案的简单程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 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 |
-
好吧,我怀疑约翰尼知道这是做什么的; p也许这有帮助:izip(L, islice(L,1,None))在L上滑动一个窗口。它给出了像[(L[0],L[1]), (L[1],L[2]), ...]这样的序列
-
它可能只是挑选,但索引Python列表应该比切片和压缩它便宜得多。
-
@Apalala:"应该便宜得多"?请使用timeit并发布比较,显示实际发生的事情,而不是应该发生的事情。
-
@Apalala:在这台计算机上使用CPython2.6进行索引更快,但请注意,使用xrange range的整数是必不可少的
-
@ S.Lott一个人不应该测量一切。 Python解释器和标准库的性能特征已有详细记录,Big O告诉其余部分。我使用range而不是xrange是一个新手的错误(xrange已在Python 3中弃用)。
-
@Apalala:range vs. xrange众所周知。"索引Python列表应该比切片和压缩它便宜得多"并不是众所周知的。 没有实际数字,它实际上就是猜想。 根据实际数字,它是理论+证据==科学。
-
@ S.Lott更为人所知的是,验证序列是否排序应该比排序要便宜得多。;-)
编辑:请参阅此答案以了解正确的方法。我会在这里留下我的答案给后人(处理这种情况的正确方法是什么?),但它不应该被认为是问题的最佳答案,即使这是对问题的正确答案。
要回答您的具体问题,您可以使用copy.copy(或使用切片语法[:])来创建原始列表的副本:
1 2 3 4 5 6
| import copy
def is_sorted(t):
a = copy.copy(t) # could also do: a = t[:]
a.sort()
return a == t |
但是,更好的方法是使用sorted函数返回列表的排序副本:
1 2
| def is_sorted(t):
return sorted(t) == t |
或者:is_sorted = lambda t: sorted(t) == t
-
根据规格,deepcopy是过度的。 copy或[:]就足够了,因为列表中的项目未被修改。
-
@aaronasterling:好点。我将修改我的答案以删除deepcopy引用。
-
测试序列是否排序是O(n)比较和O(0)交换。对于理智测试的排序是O(n log(n))。
-
@Apalala,虽然python代码比C代码慢大约100倍,但实际上滥用排序通常更快,因为log(n)在n非常大时才捕获100。如果列表非常未排序,那么python循环应该快得多
-
@gnibbler交换成本高于比较数量级,而O(n)仅用于完全排序的序列。这是一个很大的常数,它在排序时乘以n log(n)。你在答案中给出的解决方案是正确的。
这个最小答案的功劳属于@gnibbler
1
| is_sorted = all(q[i] < q[i+1] for i in xrange(len(q)-1) ) |
-
即使第一个元素大于第二个元素,这也会迭代整个列表。你可以在这里使用all()而不是reduce,代码也会更清晰。 <5233>
-
@gnibbler它不迭代整个列表,因为它是一个生成器表达式(不是列表生成器),并且and_运算符以短路方式求值:它将在找到True时使用输入迭代器。 all的解决方案更好。
-
reduce()不知道它在找到True时可以停止,因此生成器表达式需要完成到最后。
-
+1:不要愚弄排序和比较。只需评估必要的谓词。快多了。而且也更简单。
-
@gnibbler你对reduce()是正确的。我的回答已被提出,所以我纠正了。评论和答案包含更改的跟踪,但原始reduce(operator.and_, q[i] < q[i+1] for i in range(len(q)-1))除外
通过使用以下内容创建列表的副本:
1 2 3
| def is_sorted(t):
a = t[:] # create a copy
a.sort() |
-
你不应该通过直接回答这个问题来鼓励这种糟糕的设计。这是一个糟糕的问题。
-
@ S.Lott:真诚地感谢你的建议:)
-
我应该说"虽然你的答案非常好,但问题非常糟糕。"
您可以复制t然后进行排序,如下所示:a = t[:],或使用sorted,它返回一个新的排序列表。
或者,您可以使用blist中的sortedlist结构,然后您的列表将始终排序。
就性能而言,这是一种非常糟糕的方式,用于检查列表是否已订购。您应该迭代检查每个元素是否大于或等于前一个元素。
-
赫弗曼,从理论上讲,你是对的。但是,与实际的python代码相比,Python的排序实现速度非常快,只是将列表与自身的排序版本进行比较通常会更快。
-
@aaron Point采取。应该有这样做的内置函数可以避免创建列表副本的开销,只是为了检查它是单调的!哦,这让我感觉很脏,只是想着它。
-
在某些情况下它可能会很好,但我明白为什么它不存在。大多数情况下,如果一个人想知道一个序列是否被排序,那是因为人们想要对序列进行排序,在这种情况下,可以使用list.sort或sorted。如果它只是在未排序的情况下排序,那么TimSort(Python使用的排序算法)在排序列表上是O(n),这是检查单调性的开始。再次,只需对列表进行排序。
-
@aaron好吧,但我似乎无法动摇那种肮脏的感觉...... ;-)