web-dev-qa-db-ja.com

リストにPython

リストに別のリストが含まれている(つまり、連続したサブシーケンスである)かどうかをテストするにはどうすればよいですか。と呼ばれる関数があったとしましょう:

contains([1,2], [-1, 0, 1, 2]) # Returns [2, 3] (contains returns [start, end])
contains([1,3], [-1, 0, 1, 2]) # Returns False
contains([1, 2], [[1, 2], 3]) # Returns False
contains([[1, 2]], [[1, 2], 3]) # Returns [0, 0]

編集:

contains([2, 1], [-1, 0, 1, 2]) # Returns False
contains([-1, 1, 2], [-1, 0, 1, 2]) # Returns False
contains([0, 1, 2], [-1, 0, 1, 2]) # Returns [1, 3]
42
None

これが私のバージョンです:

def contains(small, big):
    for i in xrange(len(big)-len(small)+1):
        for j in xrange(len(small)):
            if big[i+j] != small[j]:
                break
        else:
            return i, i+len(small)
    return False

Andrew Jaffeが彼のコメントで指摘しているように、私はそれがよりPythonicだと思うので、(start、end + 1)のTupleを返します。サブリストはスライスされないため、合理的に効率的です。

初心者にとって興味深い点の1つは、 forステートメントのその他の句 を使用することです。これは、私が頻繁に使用するものではありませんが、このような状況では非常に貴重です。

これは、文字列内の部分文字列を見つけることと同じです。したがって、大きなリストの場合は、 Boyer-Moore algorithm のようなものを実装する方が効率的です。

17
Dave Kirby

すべてのアイテムが一意であれば、セットを使用できます。

>>> items = set([-1, 0, 1, 2])
>>> set([1, 2]).issubset(items)
True
>>> set([1, 3]).issubset(items)
False
48
Thomas O

これを行うall()およびany()関数があります。 list1にlist2のすべての要素が含まれているかどうかを確認するには

result = all(elem in list1 for elem in list2)

List1にlist2の要素が含まれているかどうかを確認するには

result = any(elem in list1 for elem in list2)

変数の結果はブール値(TRUE/FALSE)になります。

13
ericyan3000

bigリストが本当に大きい場合、 Rabin-Karpアルゴリズム を謙虚に提案できますか。リンクには、ほぼPythonでほぼ使用可能なコードも含まれています。

4
9000

OPの編集後:

def contains(small, big):
    for i in xrange(1 + len(big) - len(small)):
        if small == big[i:i+len(small)]:
            return i, i + len(small) - 1
    return False
3
eumiro

これは、組み込みのlist.index()メソッドと==演算子:

def contains(sub, pri):
    M, N = len(pri), len(sub)
    i, LAST = 0, M-N+1
    while True:
        try:
            found = pri.index(sub[0], i, LAST) # find first elem in sub
        except ValueError:
            return False
        if pri[found:found+N] == sub:
            return [found, found+N-1]
        else:
            i = found+1
2
martineau

リストメソッドを使用する簡単なアルゴリズムを次に示します。

#!/usr/bin/env python

def list_find(what, where):
    """Find `what` list in the `where` list.

    Return index in `where` where `what` starts
    or -1 if no such index.

    >>> f = list_find
    >>> f([2, 1], [-1, 0, 1, 2])
    -1
    >>> f([-1, 1, 2], [-1, 0, 1, 2])
    -1
    >>> f([0, 1, 2], [-1, 0, 1, 2])
    1
    >>> f([1,2], [-1, 0, 1, 2])
    2
    >>> f([1,3], [-1, 0, 1, 2])
    -1
    >>> f([1, 2], [[1, 2], 3])
    -1
    >>> f([[1, 2]], [[1, 2], 3])
    0
    """
    if not what: # empty list is always found
        return 0
    try:
        index = 0
        while True:
            index = where.index(what[0], index)
            if where[index:index+len(what)] == what:
                return index # found
            index += 1 # try next position
    except ValueError:
        return -1 # not found

def contains(what, where):
    """Return [start, end+1] if found else empty list."""
    i = list_find(what, where)
    return [i, i + len(what)] if i >= 0 else [] #NOTE: bool([]) == False

if __name__=="__main__":
    import doctest; doctest.testmod()
1
jfs

リストにシーケンスとして別のリストが含まれているかどうかのテストについて説明する問題を改善する場合、答えは次のワンライナーになります。

def contains(subseq, inseq):
    return any(inseq[pos:pos + len(subseq)] == subseq for pos in range(0, len(inseq) - len(subseq) + 1))

ここで、このワンライナーを調整するために使用した単体テスト:

https://Gist.github.com/anonymous/6910a85b4978daee137f

1
Oleksiy

これが私の答えです。この関数は、BがAのサブリストであるかどうかを調べるのに役立ちます。時間の複雑さはO(n)です。

`def does_A_contain_B(A, B): #remember now A is the larger list
    b_size = len(B)
    for a_index in range(0, len(A)):
        if A[a_index : a_index+b_size]==B:
            return True
    else:
        return False`
1
Akila D. Perera

最小コード:

def contains(a,b):
    str(a)[1:-1].find(str(b)[1:-1])>=0
1
Bart Mensfort

これをできるだけ効率的にしようとしました。

ジェネレーターを使用します。これらの獣になじみのない人は ドキュメントyield式 をチェックアウトすることをお勧めします。

基本的に、サブシーケンスから値のジェネレーターを作成します。これは、真の値を送信することでリセットできます。ジェネレーターがリセットされると、subの先頭から再び降伏を開始します。

次に、sequenceの連続する値をジェネレータのyieldと比較し、一致しない場合はジェネレータをリセットします。

ジェネレータが値を使い果たすと、つまりリセットされずにsubの終わりに達すると、一致するものが見つかったことを意味します。

任意のシーケンスで機能するため、文字列でも使用できます。この場合、str.findの代わりにFalseを返すことを除いて、-1と同様に動作します。

さらに注意してください:返されるTupleの2番目の値は、Python標準に合わせて、通常は1つ高くなります。つまり"string"[0:2] == "st"。それがこの仕組みです。

これが汎用ルーチンであるか、特定の目標を実装するかによって異なります。後者の場合、汎用ルーチンを実装してから、戻り値を仕様に合わせて調整する関数でラップする方がよい場合があります。

def reiterator(sub):
    """Yield elements of a sequence, resetting if sent ``True``."""
    it = iter(sub)
    while True:
        if (yield it.next()):
            it = iter(sub)

def find_in_sequence(sub, sequence):
    """Find a subsequence in a sequence.

    >>> find_in_sequence([2, 1], [-1, 0, 1, 2])
    False
    >>> find_in_sequence([-1, 1, 2], [-1, 0, 1, 2])
    False
    >>> find_in_sequence([0, 1, 2], [-1, 0, 1, 2])
    (1, 3)
    >>> find_in_sequence("subsequence",
    ...                  "This sequence contains a subsequence.")
    (25, 35)
    >>> find_in_sequence("subsequence", "This one doesn't.")
    False

    """
    start = None
    sub_items = reiterator(sub)
    sub_item = sub_items.next()
    for index, item in enumerate(sequence):
        if item == sub_item:
            if start is None: start = index
        else:
            start = None
        try:
            sub_item = sub_items.send(start is None)
        except StopIteration:
            # If the subsequence is depleted, we win!
            return (start, index)
    return False
0
intuited

ほとんどの回答の問題は、リスト内の一意のアイテムに適していることです。アイテムが一意ではなく、交差点があるかどうかを知りたい場合は、アイテムをカウントする必要があります。

_from collections import Counter as count

def listContains(l1, l2):
  list1 = count(l1)
  list2 = count(l2)

  return list1&list2 == list1

print( listContains([1,1,2,5], [1,2,3,5,1,2,1]) ) # Returns True
print( listContains([1,1,2,8], [1,2,3,5,1,2,1]) ) # Returns False
_

''.join(list1&list2)を使用して交差点を返すこともできます

0
mafonya

これは速いと思います...

def issublist(subList, myList, start=0):
    if not subList: return 0
    lenList, lensubList = len(myList), len(subList)
    try:
        while lenList - start >= lensubList:
            start = myList.index(subList[0], start)
            for i in xrange(lensubList):
                if myList[start+i] != subList[i]:
                    break
            else:
                return start, start + lensubList - 1
            start += 1
        return False
    except:
        return False
0
ChessMaster