Python – 如何检查列表单调性

什么是检查列表单调性的有效和pythonic方法?
即它有单调递增或递减值?

例子:

[0,1,2,3,3,4] # This is a monotonically increasing list [4.3,4.2,-2] # This is a monotonically decreasing list [2,3,1] # This is neither 
 def strictly_increasing(L): return all(x<y for x, y in zip(L, L[1:])) def strictly_decreasing(L): return all(x>y for x, y in zip(L, L[1:])) def non_increasing(L): return all(x>=y for x, y in zip(L, L[1:])) def non_decreasing(L): return all(x<=y for x, y in zip(L, L[1:])) 
 import itertools import operator def monotone_increasing(lst): pairs = zip(lst, lst[1:]) return all(itertools.starmap(operator.le, pairs)) def monotone_decreasing(lst): pairs = zip(lst, lst[1:]) return all(itertools.starmap(operator.ge, pairs)) def monotone(lst): return monotone_increasing(lst) or monotone_decreasing(lst) 

这种方法是O(N)在列表的长度。

如果你有大量的数字列表,最好使用numpy,如果你是:

 import numpy as np def monotonic(x): dx = np.diff(x) return np.all(dx <= 0) or np.all(dx >= 0) 

应该做的伎俩。

@ 6502拥有完美的列表代码,我只想添加一个适用于所有序列的通用版本:

 def pairwise(seq): items = iter(seq) last = next(items) for item in items: yield last, item last = item def strictly_increasing(L): return all(x<y for x, y in pairwise(L)) def strictly_decreasing(L): return all(x>y for x, y in pairwise(L)) def non_increasing(L): return all(x>=y for x, y in pairwise(L)) def non_decreasing(L): return all(x<=y for x, y in pairwise(L)) 

这是一个使用复杂度reduce的function性解决schemeO(n)

 is_increasing = lambda L: reduce(lambda a,b: b if a < b else 9999 , L)!=9999 is_decreasing = lambda L: reduce(lambda a,b: b if a > b else -9999 , L)!=-9999 

9999replace为值的上限, -9999replace为下限。 例如,如果您正在testing数字列表,则可以使用10-1


我用@ 6502的答案testing了它的性能,而且速度更快。

案例正确: [1,2,3,4,5,6,7,8,9]

 # my solution .. $ python -m timeit "inc = lambda L: reduce(lambda a,b: b if a < b else 9999 , L)!=9999; inc([1,2,3,4,5,6,7,8,9])" 1000000 loops, best of 3: 1.9 usec per loop # while the other solution: $ python -m timeit "inc = lambda L: all(x<y for x, y in zip(L, L[1:]));inc([1,2,3,4,5,6,7,8,9])" 100000 loops, best of 3: 2.77 usec per loop 

从第二个元素错误的案例: [4,2,3,4,5,6,7,8,7]

 # my solution .. $ python -m timeit "inc = lambda L: reduce(lambda a,b: b if a < b else 9999 , L)!=9999; inc([4,2,3,4,5,6,7,8,7])" 1000000 loops, best of 3: 1.87 usec per loop # while the other solution: $ python -m timeit "inc = lambda L: all(x<y for x, y in zip(L, L[1:]));inc([4,2,3,4,5,6,7,8,7])" 100000 loops, best of 3: 2.15 usec per loop 
 import operator, itertools def is_monotone(lst): op = operator.le # pick 'op' based upon trend between if not op(lst[0], lst[-1]): # first and last element in the 'lst' op = operator.ge return all(op(x,y) for x, y in itertools.izip(lst, lst[1:])) 

我在不同的条件下对这个问题的所有答案进行了计时,发现:

  • 如果名单已经单调增加,sorting是最快的
  • 如果列表是乱序/随机的,或者乱序元素的数量大于1,sorting是最慢的。 当然,列表越多,对应的结果越慢。
  • 迈克尔·J·理发师的方法是最快的,如果名单大多是单调递增的,或者是完全随机的。

这里是代码尝试一下:

 import timeit setup = ''' import random from itertools import izip, starmap, islice import operator def is_increasing_normal(lst): for i in range(0, len(lst) - 1): if lst[i] >= lst[i + 1]: return False return True def is_increasing_zip(lst): return all(x < y for x, y in izip(lst, islice(lst, 1, None))) def is_increasing_sorted(lst): return lst == sorted(lst) def is_increasing_starmap(lst): pairs = izip(lst, islice(lst, 1, None)) return all(starmap(operator.le, pairs)) if {list_method} in (1, 2): lst = list(range({n})) if {list_method} == 2: for _ in range(int({n} * 0.0001)): lst.insert(random.randrange(0, len(lst)), -random.randrange(1,100)) if {list_method} == 3: lst = [int(1000*random.random()) for i in xrange({n})] ''' n = 100000 iterations = 10000 list_method = 1 timeit.timeit('is_increasing_normal(lst)', setup=setup.format(n=n, list_method=list_method), number=iterations) timeit.timeit('is_increasing_zip(lst)', setup=setup.format(n=n, list_method=list_method), number=iterations) timeit.timeit('is_increasing_sorted(lst)', setup=setup.format(n=n, list_method=list_method), number=iterations) timeit.timeit('is_increasing_starmap(lst)', setup=setup.format(n=n, list_method=list_method), number=iterations) 

如果列表已经单调递增( list_method == 1 ),最快到最慢的是:

  1. 分类
  2. 星图
  3. 正常
  4. 压缩

如果列表大部分单调递增( list_method == 2 ),最快到最慢的是:

  1. 星图
  2. 压缩
  3. 正常
  4. 分类

(starmap或zip是否最快取决于执行,我无法识别一个模式,Starmap似乎通常更快)

如果列表完全是随机的( list_method == 3 ),最快到最慢的是:

  1. 星图
  2. 压缩
  3. 正常
  4. sorting(非常糟糕)
 L = [1,2,3] L == sorted(L) L == sorted(L, reverse=True) 
 >>> l = [0,1,2,3,3,4] >>> l == sorted(l) or l == sorted(l,reverse=True)