リストがソートされているかどうかを確認するPythonの方法


145

リストがすでに並べ替えられているASCかどうかを確認するためのpython的な方法はありますかDESC

listtimestamps = [1, 2, 3, 5, 6, 7]

以下のような何かisttimestamps.isSorted()それを返しますTrueFalse

一部のメッセージのタイムスタンプのリストを入力し、トランザクションが正しい順序で表示されるかどうかを確認したいと思います。

回答:


212

実際には、anijhawが求めている答えは提供していません。ここに1つのライナーがあります:

all(l[i] <= l[i+1] for i in xrange(len(l)-1))

Python 3の場合:

all(l[i] <= l[i+1] for i in range(len(l)-1))

2
それはすばらしい。key使用する関数を渡すことができるように、関数でそれをラップしたい場合があります。key=lambda x, y: x < yデフォルトとして適切です。
aaronasterling 2010

3
いくつかの解決策のdef isSorted(x, key = lambda x: x): return all([key(x[i]) <= key(x[i + 1]) for i in xrange(len(x) - 1)])
組み合わせ

2
@aaronasterling:operator.leラムダより高速である必要があります
Marian

これは私には機能しません(python --version = 2.6.4)l = [1, 2, 3, 4, 1, 6, 7, 8, 7] all(l[i] <= l[i+1] for i in xrange(len(l)-1)) 結果として印刷:True
prodev_paris '19

1
Python 3.xにはxrangeもうないようですrange。を使用してください。私が取得NameError: name 'xrange' is not defined私はそのコードを実行したとき。range代わりに使用するように切り替えましたが、問題なくxrange動作します。参照:stackoverflow.com/questions/15014310/…–
Cale Sweeney

78

私はただ使う

if sorted(lst) == lst:
    # code here

非常に大きなリストでない限り、カスタム関数を作成する必要があります。

ソートされていない場合にソートするだけであれば、チェックを忘れてソートしてください。

lst.sort()

それについてあまり考えないでください。

カスタム関数が必要な場合は、次のようなことができます

def is_sorted(lst, key=lambda x: x):
    for i, el in enumerate(lst[1:]):
        if key(el) < key(lst[i]): # i is the index of the previous element
            return False
    return True

リストが既に並べ替えられている場合(そしてforループでO(n)がそうである場合)、これはO(n)になります。そのため、ほとんどの場合、並べ替えられていない(そしてかなりランダムに)と予想されない限り、もう一度、リストを並べ替えるだけです。


10
それがあなたがやろうとしていることなら、次のように言うこともできます:条件チェックなしのlst.sort();-)
SapphireSun

5
そのnlognは、単純なforループを使用してO(n)で明らかに高速な方法があります。
anijhaw 2010

1
@SapphireSun。それが私が言ったことです;)
arronasterling

@anijhaw、あなたがコメントを残している間に私が行った更新を見てください。チェックはO(n)で、ソートはO(nlgn)です。O(n)のコストが発生し、向きを変えてO(nlgn)を追加するか、またはソート済みリストをソートするコストを取るだけで、timsortのO(n)になると思います。
aaronasterling 2010

@ Aaron:元の質問の編集を確認してください
anijhaw

44

このイテレータ形式は、整数インデックスを使用するよりも10〜15%高速です。

# python2 only
if str is bytes:
    from itertools import izip as zip

def is_sorted(l):
    return all(a <= b for a, b in zip(l, l[1:]))

私は自分のマシン上の有意差は見ないgist.github.com/735259を @Nathanファリントンの答えから修正#7変種が2倍高速であるstackoverflow.com/questions/3755136/...
JFS

これは、リストのような「インデックス付け可能な」コンテナに対してのみ機能します。この場合、スライスによって2つの新しいリストが作成されます。一般的なイテレータには、アレクサンダーの解法を好みます
Bas Swinckels、2016年

1
エレガントな答えです。itertoolsを使用izipisliceて、それをより高速にすることができます。
Elmex80s

@jfs:「Nathan Farringtonの#7バリアント」は間違っています。本来の目的を果たさないだけなので、より高速です。そこで私のコメントを見てください。
olivecoder

1
最短の引数を使い果たすとzipが停止するため、ソリューションをzip(l、l [1:])に簡略化できます
Gelineau

20

これを実装する美しい方法は、次のimap関数を使用することですitertools

from itertools import imap, tee
import operator

def is_sorted(iterable, compare=operator.le):
  a, b = tee(iterable)
  next(b, None)
  return all(imap(compare, a, b))

この実装は高速で、あらゆる反復可能オブジェクトで機能します。


4
いいけどバギー!is_sorted(iter([1,2,3,2,5,8]))または同等のジェネレータを試してください。あなたはのための独立したイテレータを使用する必要がtail試して、itertools.tee
Kos

それを覚えているiter(x) is xイテレータのために
コス

1
ああ、それは不愉快な驚きです!私はそれを修正しました。ありがとう!
Alexandre Vassalotti 2013

3
Python 3 itertools.imapではに名前が変更されていることに注意してください[__builtins__.]map
Nick T

10

私は、ベンチマークを実行し、sorted(lst, reverse=True) == lst長いリストのための最速だった、とall(l[i] >= l[i+1] for i in xrange(len(l)-1))短いリストの最速でした。これらのベンチマークは、MacBook Pro 2010 13インチ(Core2 Duo 2.66GHz、4GB 1067MHz DDR3 RAM、Mac OS X 10.6.5)で実行されました。

更新:自分のシステムで直接実行できるようにスクリプトを修正しました。以前のバージョンにはバグがありました。また、ソートされた入力とソートされていない入力の両方を追加しました。

  • 短いソートリストに最適: all(l[i] >= l[i+1] for i in xrange(len(l)-1))
  • 長いソートリストに最適: sorted(l, reverse=True) == l
  • ソートされていない短いリストに最適: all(l[i] >= l[i+1] for i in xrange(len(l)-1))
  • ソートされていない長いリストに最適: all(l[i] >= l[i+1] for i in xrange(len(l)-1))

したがって、ほとんどの場合、明確な勝者がいます。

更新:アーロンスターリングの回答(#6と#7)は、実際にはすべてのケースで最速です。#7は、キーを検索するための間接的なレイヤーがないため、最速です。

#!/usr/bin/env python

import itertools
import time

def benchmark(f, *args):
    t1 = time.time()
    for i in xrange(1000000):
        f(*args)
    t2 = time.time()
    return t2-t1

L1 = range(4, 0, -1)
L2 = range(100, 0, -1)
L3 = range(0, 4)
L4 = range(0, 100)

# 1.
def isNonIncreasing(l, key=lambda x,y: x >= y): 
    return all(key(l[i],l[i+1]) for i in xrange(len(l)-1))
print benchmark(isNonIncreasing, L1) # 2.47253704071
print benchmark(isNonIncreasing, L2) # 34.5398209095
print benchmark(isNonIncreasing, L3) # 2.1916718483
print benchmark(isNonIncreasing, L4) # 2.19576501846

# 2.
def isNonIncreasing(l):
    return all(l[i] >= l[i+1] for i in xrange(len(l)-1))
print benchmark(isNonIncreasing, L1) # 1.86919999123
print benchmark(isNonIncreasing, L2) # 21.8603689671
print benchmark(isNonIncreasing, L3) # 1.95684289932
print benchmark(isNonIncreasing, L4) # 1.95272517204

# 3.
def isNonIncreasing(l, key=lambda x,y: x >= y): 
    return all(key(a,b) for (a,b) in itertools.izip(l[:-1],l[1:]))
print benchmark(isNonIncreasing, L1) # 2.65468883514
print benchmark(isNonIncreasing, L2) # 29.7504849434
print benchmark(isNonIncreasing, L3) # 2.78062295914
print benchmark(isNonIncreasing, L4) # 3.73436689377

# 4.
def isNonIncreasing(l):
    return all(a >= b for (a,b) in itertools.izip(l[:-1],l[1:]))
print benchmark(isNonIncreasing, L1) # 2.06947803497
print benchmark(isNonIncreasing, L2) # 15.6351969242
print benchmark(isNonIncreasing, L3) # 2.45671010017
print benchmark(isNonIncreasing, L4) # 3.48461818695

# 5.
def isNonIncreasing(l):
    return sorted(l, reverse=True) == l
print benchmark(isNonIncreasing, L1) # 2.01579380035
print benchmark(isNonIncreasing, L2) # 5.44593787193
print benchmark(isNonIncreasing, L3) # 2.01813793182
print benchmark(isNonIncreasing, L4) # 4.97615599632

# 6.
def isNonIncreasing(l, key=lambda x, y: x >= y): 
    for i, el in enumerate(l[1:]):
        if key(el, l[i-1]):
            return False
    return True
print benchmark(isNonIncreasing, L1) # 1.06842684746
print benchmark(isNonIncreasing, L2) # 1.67291283607
print benchmark(isNonIncreasing, L3) # 1.39491200447
print benchmark(isNonIncreasing, L4) # 1.80557894707

# 7.
def isNonIncreasing(l):
    for i, el in enumerate(l[1:]):
        if el >= l[i-1]:
            return False
    return True
print benchmark(isNonIncreasing, L1) # 0.883186101913
print benchmark(isNonIncreasing, L2) # 1.42852401733
print benchmark(isNonIncreasing, L3) # 1.09229516983
print benchmark(isNonIncreasing, L4) # 1.59502696991

1
あなたはベンチマークがジェネレータ式フォームの最悪のケースと私のソリューションの最良のケースをテストしています。ソートされていないリストに対してもテストしたい場合があります。次に、リストがほとんどの場合ソートされることを期待しない限り、ジェネレータ式の方が優れていることがわかります。
aaronasterling 2010

@aaronsterling、私はスクリプトを更新して、ソートされた入力とソートされていない入力の両方を持つようにしました。
Nathan Farrington、

のすべての機能enumerateが正しくありません。enumerate(l[1:])代替する必要がありますenumerate(l[1:], 1)
jfs

1
代わりに置き換えるのenumerate(l[1:])enumerate(l[1:], 1)置き換えることができますl[i-1]によってl[i]
jfs

たとえば、ランダム入力を追加するとL5=range(100); random.shuffle(L5)、#5は比較的遅くなります。この場合、変更された#7は全体的に高速ですcodepad.org/xmWPxHQY
jfs

9

私はこれを行います(多くの答えをここから盗みます[アーロンスターリング、ワイイップトゥン、ポールマクガイアからのソータ]、主にアーミンロナッチャー):

from itertools import tee, izip

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

def is_sorted(iterable, key=lambda a, b: a <= b):
    return all(key(a, b) for a, b in pairwise(iterable))

すばらしい点の1つは、シリーズの2番目の反復可能オブジェクトを理解する必要がないことです(リストスライスとは異なります)。


2
誤解を招くような名前keykeyアイテムを同等の値に変換するために使用する必要があります。
InQβ

4

私はnumpy.diff()に基づいてこのワンライナーを使用します:

def issorted(x):
    """Check if x is sorted"""
    return (numpy.diff(x) >= 0).all() # is diff between all consecutive entries >= 0?

私は他の方法に対して実際に時間を計測していませんが、numpy.diffのループは(おそらく)Cで直接実行されるため(n-1減算とそれに続くn)、純粋なPythonメソッドよりも高速だと思います。 -1の比較)。

ただし、xがunsigned intである場合は注意する必要があります。これにより、numpy.diff()でサイレント整数アンダーフローが発生し、誤検知が発生する可能性があります。変更されたバージョンは次のとおりです。

def issorted(x):
    """Check if x is sorted"""
    try:
        if x.dtype.kind == 'u':
            # x is unsigned int array, risk of int underflow in np.diff
            x = numpy.int64(x)
    except AttributeError:
        pass # no dtype, not an array
    return (numpy.diff(x) >= 0).all()

4

これはトップの回答に似ていますが、明示的なインデックス付けを回避するため、私はそれが好きです。リストに名前があるとすると、次のようにしてリストからタプルをlst生成でき
(item, next_item)ますzip

all(x <= y for x,y in zip(lst, lst[1:]))

Python 3ではzipすでにジェネレータが返されますが、Python 2ではitertools.izipメモリ効率を向上させるために使用できます。

小さなデモ:

>>> lst = [1, 2, 3, 4]
>>> zip(lst, lst[1:])
[(1, 2), (2, 3), (3, 4)]
>>> all(x <= y for x,y in zip(lst, lst[1:]))
True
>>> 
>>> lst = [1, 2, 3, 2]
>>> zip(lst, lst[1:])
[(1, 2), (2, 3), (3, 2)]
>>> all(x <= y for x,y in zip(lst, lst[1:]))
False

最後の1つは、タプル(3, 2)が評価されるときに失敗します。

おまけ:インデックスを作成できない有限(!)ジェネレーターのチェック:

>>> def gen1():
...     yield 1
...     yield 2
...     yield 3
...     yield 4
...     
>>> def gen2():
...     yield 1
...     yield 2
...     yield 4
...     yield 3
... 
>>> g1_1 = gen1()
>>> g1_2 = gen1()
>>> next(g1_2)
1
>>> all(x <= y for x,y in zip(g1_1, g1_2))
True
>>>
>>> g2_1 = gen2()
>>> g2_2 = gen2()
>>> next(g2_2)
1
>>> all(x <= y for x,y in zip(g2_1, g2_2))
False

itertools.izipPython 2 を使用している場合は、必ずここを使用してください。そうしないと、ジェネレーターからリストを作成する必要がないという目的が無効になります。


2
を使用isliceしてスライスを最適化することもできます。itertoolsモジュールにもあります。all(x <= y for x, y in izip(lst, islice(lst, 1)))
Elmex80s

3

SapphireSunはかなり正しいです。そのまま使えますlst.sort()。Pythonのソート実装(TimSort)は、リストがすでにソートされているかどうかを確認します。その場合、sort()は線形時間で完了します。リストを確実にソートするためのPython的な方法のように聞こえます;)


20
リストが実際にソートされている場合のみ線形時間。そうでない場合は、実際の並べ替えタスクをスキップする短絡がないため、リストが長い場合、大きなペナルティが発生する可能性があります。
PaulMcG 2010

これは、タスクが「リストがソートされていることを確認し、ソートされていない場合は死ぬ」ことである場合、優れた答えです。これは、他の理由でソートする必要があるデータの健全性チェックとしてかなり一般的です。次に、エラーの場合のみが遅くなります。
Ed Avis

3

sorted組み込みでcmp関数がで呼び出される保証はないと思いますがi+1, i、CPythonではそうなります。

だからあなたは次のようなことをすることができます:

def my_cmp(x, y):
   cmpval = cmp(x, y)
   if cmpval < 0:
      raise ValueError
   return cmpval

def is_sorted(lst):
   try:
      sorted(lst, cmp=my_cmp)
      return True
   except ValueError:
      return False

print is_sorted([1,2,3,5,6,7])
print is_sorted([1,2,5,3,6,7])

または、この方法(ifステートメントなし-> EAFPが間違っていますか?;-)):

def my_cmp(x, y):
   assert(x >= y)
   return -1

def is_sorted(lst):
   try:
      sorted(lst, cmp=my_cmp)
      return True
   except AssertionError:
      return False

3

それほどPythonicではありませんが、少なくとも1つのreduce()答えが必要ですよね?

def is_sorted(iterable):
    prev_or_inf = lambda prev, i: i if prev <= i else float('inf')
    return reduce(prev_or_inf, iterable, float('-inf')) < float('inf')

アキュムレータ変数は、その最後にチェックされた値を格納するだけであり、いずれかの値が前の値よりも小さい場合、アキュムレータは無限大に設定されます(したがって、「以前の値」は常に現在のもの)。


2

@aaronsterlingで指摘されているように、配列がソートされて小さすぎない場合、次のソリューションが最短で最速のようです。defis_sorted(lst):return(sorted(lst)== lst)

ほとんどの場合、配列が並べ替えられていない場合は、配列全体をスキャンせず、並べ替えられていないプレフィックスが検出されるとすぐにFalseを返すソリューションを使用することが望ましいでしょう。以下は私が見つけた最も速い解決策ですが、特にエレガントではありません:

def is_sorted(lst):
    it = iter(lst)
    try:
        prev = it.next()
    except StopIteration:
        return True
    for x in it:
        if prev > x:
            return False
        prev = x
    return True

Nathan Farringtonのベンチマークを使用すると、大規模な並べ替えリストで実行する場合を除いて、すべての場合でsorted(lst)を使用するよりも実行時間が短縮されます。

これが私のコンピューターのベンチマーク結果です。

Sorted(lst)== lstソリューション

  • L1:1.23838591576
  • L2:4.19063091278
  • L3:1.17996287346
  • L4:4.68399500847

2番目の解決策:

  • L1:0.81095790863
  • L2:0.802397012711
  • L3:1.06135106087
  • L4:8.82761001587

2

numpy配列の最速の方法が必要な場合は、numbaを使用してくださいあなたがcondaを使用している場合、既にインストールされなければなりません

numbaによってコンパイルされるため、コードは高速になります

import numba
@numba.jit
def issorted(vec, ascending=True):
    if len(vec) < 2:
        return True
    if ascending:
        for i in range(1, len(vec)):
            if vec[i-1] > vec[i]:
                return False
        return True
    else:
        for i in range(1, len(vec)):
            if vec[i-1] < vec[i]:
                return False
        return True

その後:

>>> issorted(array([4,9,100]))
>>> True

2

(別のモジュールが必要な場合でも)別の方法を追加するだけですiteration_utilities.all_monotone

>>> from iteration_utilities import all_monotone
>>> listtimestamps = [1, 2, 3, 5, 6, 7]
>>> all_monotone(listtimestamps)
True

>>> all_monotone([1,2,1])
False

DESC注文を確認するには:

>>> all_monotone(listtimestamps, decreasing=True)
False

>>> all_monotone([3,2,1], decreasing=True)
True

strict厳密に(連続する要素が等しくない場合)単調なシーケンスをチェックする必要がある場合は、パラメーターもあります。

それはあなたの場合には問題ないのですが、場合、あなたのシーケンスが含まれているnan値をソートし、その後、いくつかの方法は、例えば、失敗します。

def is_sorted_using_sorted(iterable):
    return sorted(iterable) == iterable

>>> is_sorted_using_sorted([3, float('nan'), 1])  # definetly False, right?
True

>>> all_monotone([3, float('nan'), 1])
False

iteration_utilities.all_monotone特にソートされていない入力については、ここで説明されている他のソリューションと比較してパフォーマンスが速いことに注意してください(ベンチマークを参照)。


2

怠惰な

from itertools import tee

def is_sorted(l):
    l1, l2 = tee(l)
    next(l2, None)
    return all(a <= b for a, b in zip(l1, l2))

1
すごい!これは、それをワンライナーにするための改善です-iter()とnext()の代わりにスライシングを使用して同じ結果を出します:all(a <= b for a, b in zip(l, l[1:]))
Matt

1
@LiborJelinekは良いですが、私のバージョンlはジェネレーターであり、スライスをサポートしていない場合に機能します。
Sergey11g 16

2

Python 3.6.8

from more_itertools import pairwise

class AssertionHelper:
    @classmethod
    def is_ascending(cls, data: iter) -> bool:
        for a, b in pairwise(data):
            if a > b:
                return False
        return True

    @classmethod
    def is_descending(cls, data: iter) -> bool:
        for a, b in pairwise(data):
            if a < b:
                return False
        return True

    @classmethod
    def is_sorted(cls, data: iter) -> bool:
        return cls.is_ascending(data) or cls.is_descending(data)
>>> AssertionHelper.is_descending((1, 2, 3, 4))
False
>>> AssertionHelper.is_ascending((1, 2, 3, 4))
True
>>> AssertionHelper.is_sorted((1, 2, 3, 4))
True

0

最も簡単な方法:

def isSorted(arr):
  i = 1
  while i < len(arr):
    if(result[i] < result[i - 1]):
      return False
    i += 1
  return True

0
from functools import reduce

# myiterable can be of any iterable type (including list)
isSorted = reduce(lambda r, e: (r[0] and (r[1] or r[2] <= e), False, e), myiterable, (True, True, None))[0]

派生した削減値は、(sortedSoFarFlagfirstTimeFlaglastElementValue)の3つの部分からなるタプルです。これは、最初に(で始まりTrueTrueNone)、また、空のリストのための結果として使用されているが(なしアウト・オブ・オーダの要素が存在しないので、ソートとみなします)。各要素を処理するときに、タプルの新しい値を計算します(次のelementValueで前のタプル値を使用)。

[0] (sortedSoFarFlag) evaluates true if: prev_0 is true and (prev_1 is true or prev_2 <= elementValue)
[1] (firstTimeFlag): False
[2] (lastElementValue): elementValue

削減の最終結果は、次のタプルです。

[0]: True/False depending on whether the entire list was in sorted order
[1]: True/False depending on whether the list was empty
[2]: the last element value

最初の値は興味のある値なので[0]、reduceの結果からそれを取得するために使用します。


このソリューションは、相互に比較できる反復可能な包含要素タイプに対して機能することに注意してください。それはセットのブール(Falseの値が真の値の前に発生を確認)、数値、文字列のリストのリスト(アルファベット順)、リスト(サブセットはスーパーセットの前に起こる)などのリストを含んでいる
氏イタチ

0

上にこのオプションが表示されないので、すべての回答に追加します。リストをlで表すと、次のようになります。

import numpy as np

# Trasform the list to a numpy array
x = np.array(l)

# check if ascendent sorted:
all(x[:-1] <= x[1:])

# check if descendent sorted:
all(x[:-1] >= x[1:])

0

割り当て式を使用するソリューション(Python 3.8で追加):

def is_sorted(seq):
    seq_iter = iter(seq)
    cur = next(seq_iter, None)
    return all((prev := cur) <= (cur := nxt) for nxt in seq_iter)

z = list(range(10))
print(z)
print(is_sorted(z))

import random
random.shuffle(z)
print(z)
print(is_sorted(z))

z = []
print(z)
print(is_sorted(z))

与える:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
True
[1, 7, 5, 9, 4, 0, 8, 3, 2, 6]
False
[]
True

-1

これは実際、再帰を使用してそれを行う最も短い方法です:

ソートされている場合はTrueを出力し、そうでない場合はFalseを出力します

 def is_Sorted(lst):
    if len(lst) == 1:
       return True
    return lst[0] <= lst[1] and is_Sorted(lst[1:])

 any_list = [1,2,3,4]
 print is_Sorted(any_list)

これはRuntimeError: maximum recursion depth exceeded長いリストに対して発生することに注意してください。お試しくださいany_list = range(1000)
timgeb 2016

-1

これはどう ?シンプルでわかりやすい。

def is_list_sorted(al):

    llength =len(al)


    for i in range (llength):
        if (al[i-1] > al[i]):
            print(al[i])
            print(al[i+1])
            print('Not sorted')
            return -1

    else :
        print('sorted')
        return  true

-3

Python 3以降で整数または文字列に対して確実に機能します。

def tail(t):
    return t[:]

letters = ['a', 'b', 'c', 'd', 'e']
rest = tail(letters)
rest.sort()
if letters == rest:
    print ('Given list is SORTED.')
else:
    print ('List NOT Sorted.')

================================================== ===================

与えられたリストがソートされているかどうかを見つける別の方法

trees1 = list ([1, 4, 5, 3, 2])
trees2 = list (trees1)
trees2.sort()
if trees1 == trees2:
    print ('trees1 is SORTED')
else:
    print ('Not sorted')
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.