목록 단 조성을 확인 하는 효율적이고 비단뱀적인 방법 은 무엇입니까 ?
즉, 단조롭게 증가하거나 감소하는 값이 있습니까?
예 :
[0, 1, 2, 3, 3, 4] # This is a monotonically increasing list
[4.3, 4.2, 4.2, -2] # This is a monotonically decreasing list
[2, 3, 1] # This is neither
목록 단 조성을 확인 하는 효율적이고 비단뱀적인 방법 은 무엇입니까 ?
즉, 단조롭게 증가하거나 감소하는 값이 있습니까?
예 :
[0, 1, 2, 3, 3, 4] # This is a monotonically increasing list
[4.3, 4.2, 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:]))
def monotonic(L):
return non_increasing(L) or non_decreasing(L)
itertools.izip
대신에 zip
조기 종료를 얻을 수 있음을 고려하십시오 (python 3에서는 zip
이미 반복자처럼 작동 함)
숫자 목록이 많은 경우 numpy를 사용하는 것이 가장 좋을 수 있으며 다음과 같은 경우에는
import numpy as np
def monotonic(x):
dx = np.diff(x)
return np.all(dx <= 0) or np.all(dx >= 0)
트릭을해야합니다.
dx[0]
수 NaN
? 입력 배열은 무엇입니까?
np.diff()
첫 번째 요소 를 만든 것으로 생각 NaN
했지만 실제로는 저를 깨 물었던 다른 코드 조각이었습니다. :)
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)
목록의 길이에 있습니다.
map
추상화가 정확히 필요한데 생성기 표현식으로 다시 생성하는 이유는 무엇입니까?
O(N)
하는 것도 마찬가지입니다. 당신은 만들 수 pairs = itertools.izip(lst, itertools.islice(lst, 1, None))
있습니다.
@ 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))
팬더의 패키지이 편리합니다.
import pandas as pd
다음 명령은 정수 또는 부동 소수점 목록과 함께 작동합니다.
pd.Series(mylist).is_monotonic_increasing
myseries = pd.Series(mylist)
myseries.is_unique and myseries.is_monotonic_increasing
문서화되지 않은 개인 방법을 사용하는 대안 :
pd.Index(mylist)._is_strictly_monotonic_increasing
pd.Series(mylist).is_monotonic_decreasing
myseries = pd.Series(mylist)
myseries.is_unique and myseries.is_monotonic_decreasing
문서화되지 않은 개인 방법을 사용하는 대안 :
pd.Index(mylist)._is_strictly_monotonic_decreasing
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:]))
다음은 reduce
복잡성을 사용하는 기능적 솔루션 입니다 O(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
9999
값의 상한선과 하한선으로 바꿉니다 -9999
. 예를 들어 숫자 목록을 테스트하는 경우 10
및 을 사용할 수 있습니다 -1
.
@ 6502의 답변 과 더 빠른 성능을 테스트했습니다 .
사례 True : [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
두 번째 요소의 경우 False :[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 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
), 가장 빠른 것에서 가장 느린 것 :
목록이 대부분 단조롭게 증가하는 경우 ( list_method == 2
), 가장 빠른 것에서 가장 느린 것 :
(스타 맵 또는 zip이 가장 빠른지 여부는 실행에 따라 달라졌 고 패턴을 식별 할 수 없었습니다. 스타 맵은 일반적으로 더 빠른 것으로 보였습니다)
목록이 완전히 무작위 인 경우 ( list_method == 3
), 가장 빠르거나 가장 느린 목록은 다음과 같습니다.
n
목록 의 크기 에 따라 크게 달라지며 100000에서 상당히 다를 수 있습니다
L = [1,2,3]
L == sorted(L)
L == sorted(L, reverse=True)
sorted()
실제로 아무것도 분류하지 않았다면 나는 갔을 것 입니다. 잘못된 이름-그렇지 않은 경우 술어처럼 들립니다.
sorted(L)[0]
대신 사용 min
?
@ 6502에는이를위한 우아한 파이썬 코드가 있습니다. 다음은 더 간단한 반복기와 잠재적으로 값 비싼 임시 조각이없는 대체 솔루션입니다.
def strictly_increasing(L):
return all(L[i] < L[i+1] for i in range(len(L)-1))
def strictly_decreasing(L):
return all(L[i] > L[i+1] for i in range(len(L)-1))
def non_increasing(L):
return all(L[i] >= L[i+1] for i in range(len(L)-1))
def non_decreasing(L):
return all(L[i] <= L[i+1] for i in range(len(L)-1))
def monotonic(L):
return non_increasing(L) or non_decreasing(L)
여기에 모두 받아들이는 변형의 구체화 및 비 구체화 시퀀스. 그것이인지 여부를 자동으로 결정 monotonic
하고, 그렇다면 그 방향 (예 : increasing
또는 decreasing
)과 strict
ness를 결정합니다. 독자를 돕기 위해 인라인 주석이 제공됩니다. 마지막에 제공된 테스트 케이스와 유사합니다.
def isMonotonic(seq):
"""
seq.............: - A Python sequence, materialized or not.
Returns.........:
(True,0,True): - Mono Const, Strict: Seq empty or 1-item.
(True,0,False): - Mono Const, Not-Strict: All 2+ Seq items same.
(True,+1,True): - Mono Incr, Strict.
(True,+1,False): - Mono Incr, Not-Strict.
(True,-1,True): - Mono Decr, Strict.
(True,-1,False): - Mono Decr, Not-Strict.
(False,None,None) - Not Monotonic.
"""
items = iter(seq) # Ensure iterator (i.e. that next(...) works).
prev_value = next(items, None) # Fetch 1st item, or None if empty.
if prev_value == None: return (True,0,True) # seq was empty.
# ============================================================
# The next for/loop scans until it finds first value-change.
# ============================================================
# Ex: [3,3,3,78,...] --or- [-5,-5,-5,-102,...]
# ============================================================
# -- If that 'change-value' represents an Increase or Decrease,
# then we know to look for Monotonically Increasing or
# Decreasing, respectively.
# -- If no value-change is found end-to-end (e.g. [3,3,3,...3]),
# then it's Monotonically Constant, Non-Strict.
# -- Finally, if the sequence was exhausted above, which means
# it had exactly one-element, then it Monotonically Constant,
# Strict.
# ============================================================
isSequenceExhausted = True
curr_value = prev_value
for item in items:
isSequenceExhausted = False # Tiny inefficiency.
if item == prev_value: continue
curr_value = item
break
else:
return (True,0,True) if isSequenceExhausted else (True,0,False)
# ============================================================
# ============================================================
# If we tricked down to here, then none of the above
# checked-cases applied (i.e. didn't short-circuit and
# 'return'); so we continue with the final step of
# iterating through the remaining sequence items to
# determine Monotonicity, direction and strictness.
# ============================================================
strict = True
if curr_value > prev_value: # Scan for Increasing Monotonicity.
for item in items:
if item < curr_value: return (False,None,None)
if item == curr_value: strict = False # Tiny inefficiency.
curr_value = item
return (True,+1,strict)
else: # Scan for Decreasing Monotonicity.
for item in items:
if item > curr_value: return (False,None,None)
if item == curr_value: strict = False # Tiny inefficiency.
curr_value = item
return (True,-1,strict)
# ============================================================
# Test cases ...
assert isMonotonic([1,2,3,4]) == (True,+1,True)
assert isMonotonic([4,3,2,1]) == (True,-1,True)
assert isMonotonic([-1,-2,-3,-4]) == (True,-1,True)
assert isMonotonic([]) == (True,0,True)
assert isMonotonic([20]) == (True,0,True)
assert isMonotonic([-20]) == (True,0,True)
assert isMonotonic([1,1]) == (True,0,False)
assert isMonotonic([1,-1]) == (True,-1,True)
assert isMonotonic([1,-1,-1]) == (True,-1,False)
assert isMonotonic([1,3,3]) == (True,+1,False)
assert isMonotonic([1,2,1]) == (False,None,None)
assert isMonotonic([0,0,0,0]) == (True,0,False)
나는이 더있을 수도있을 것 같군요 Pythonic
하지만 (예를 들어 중간 컬렉션을 만드는 피할 수 있기 때문에 까다로운 list
, genexps
등); 뿐만 아니라 다양한 경우를 필터링하기 위해 fall/trickle-through
및 short-circuit
접근 방식을 사용합니다 . 예 : Edge-sequences (예 : 빈 또는 단일 항목 시퀀스, 또는 모두 동일한 항목이있는 시퀀스); 증가 또는 감소하는 단조, 엄격함 등을 식별합니다. 도움이되기를 바랍니다.