2017-08-12 5 views
2

複数のイテラブルのリストがある場合、すべてのアイテムがdisjointであるかどうかをテストします。リストのすべての項目をテストするにはどうしたらいいですか?

二組は、それらが共通

例には要素がない場合互いに素であると言われている

iterables = ["AB", "CDE", "AF"] 
all_disjoint(iterables) 
# False 

iterables = ["AB", "CDE", "FG"] 
all_disjoint(iterables) 
# True 

Pythonのセットが動作isdisjoint方法を持っているが、それが設計されています一度に2つの要素をテストします。一つのアプローチは、素子の各ペアワイズグループにこの方法を適用することである:ここ

import itertools as it 


def pairwise_(iterable): 
    """s -> (s0,s1), (s1,s2), (s2,s3), ..., (sn,s0)""" 
    # Modified: the last element wraps back to the first element. 
    a, b = it.tee(iterable, 2) 
    first = next(b, None) 
    b = it.chain(b, [first]) 
    return zip(a, b) 


def all_disjoint(x): 
    return all((set(p0).isdisjoint(set(p1))) for p0, p1 in pairwise_(x)) 

Iは最初の要素を最後の時間を取り付けるpairwiseitertools recipe修飾しました。しかし、リスト内の他のすべての項目に対して、各項目ではなく隣接項目をテストするだけなので、これは正しくありません。より少ないコードでよりエレガントにすべての要素をテストしたいと思います。これを行う簡単な方法はありますか?

+0

あなたのコードは、 'x'の各反復可能なものが、その直前のものと直後のもの(それらが存在する場合)とは互いに素であるかどうかを調べるためにテストします。これは、すべてが他のすべてから切り離されているかどうかを判断するのと同じではありません。それはあなたの目標ですか?レシピの変更には何も問題はありません、btw。 – martineau

+1

あなたは正しいです。このコードは、隣接する項目が互いに素であるかどうかだけをテストします。むしろ、私は各項目が他のすべての項目と離れていることをテストしたいと思います。レシピを変更する場合は、コードを少なくするだけです。 – pylang

答えて

4

IIUCでは、文字列のリストを取得し、それらを結合し、結合された長さがその文字列と等価な集合の長さと等しいかどうかを確認できます。

あなたの文字列を結合し、あなたの関数を定義するために''.joinを使用することができます。

In [17]: def all_disjoint(iterables): 
    ...:  total = ''.join(iterables) 
    ...:  return len(total) == len(set(total)) 
    ...: 

さて、テスト:すべての

In [18]: all_disjoint(['AB', 'CDE', 'AF']) 
Out[18]: False 

In [19]: all_disjoint(['AB', 'CDE', 'FG']) 
Out[19]: True 
+1

'reduce 'は文字列を結合するのが難しい方法です。二次的な時間がかかるからです。 '' '.join'ははるかに優れています。 – user2357112

+0

@ user2357112合意。なぜ私が減らすことを考えたのか分からない。変更しました。 –

+0

これは文字列のためのうまく動作し、質問に答えます。マージされた文字列の長さとそれが設定されている長さを比較するというアイデアは、pythonです。ありがとう。 – pylang

1

まず、set(list('AB'))はセット{'A', 'B'}にもたらすであろう。

第2に、sを列挙してからfor s2 in s[n+1:]を使用すると、上部対角のみが表示され、値自体を他のペアと比較する必要はありません。たとえば、s = ['A', 'B', 'C']の場合、[(n + 1:])のs2を列挙するには、nの[(s1、s2)、s1の結果]は[('A', 'B'), ('A', 'C'), ('B', 'C')]となります。これはからcombinationsをインポートする場合はlist(combinations(s, 2))の結果と同じです。

与えられた通り、私はanyジェネレータを使用して、各サブセット間の交差の不足を比較します。

any構造のため、共通要素の最初の観測時に短絡し、各ペアを計算する必要がありません。

s = ['AB', 'CDE', 'AF'] 
>>> not any(set(list(s1)).intersection(set(list(s2))) 
      for n, s1 in enumerate(s) for s2 in s[n+1:]) 
False 

s = ['AB', 'CDE', 'FG'] 
>>> not any(set(list(s1)).intersection(set(list(s2))) 
      for n, s1 in enumerate(s) for s2 in s[n+1:]) 
True 
+0

'set( 'AB')'も '{'A'、 'B'}'を生成します。 'list'は必須ではありません。ネストされた 'for'ループで上の対角線をテストすることで賢明なアプローチ。 – pylang

0

私はこれらの回答を興味のある人に追加します。

アプローチ1:これはマルチセット(Counter)で行うことができます。

import itertools as it 
import collections as ct 


def all_disjoint(iterables): 
    return all(not v-1 for v in ct.Counter(it.chain.from_iterable(iterables)).values()) 

アプローチ2more_itertoolsライブラリから、more_itertools.unique_to_eachは、すべてのiterableからすべてのユニークなアイテムを生み出します。次のコードは、元のイテレート可能オブジェクトへの結果の長さを比較して:あなたは、各項目は、すべての他の項目と互いに素であることをテストしたいについて言ったことを考えると

import more_itertools as mit 

def all_disjoint(iterables): 
    return all(len(x) == len(y) for x, y in zip(iterables, mit.unique_to_each(*iterables))) 
1

は、私はこれが何をしたいんだと思う:

import itertools as it 

def all_disjoint(x): 
    return all((set(p0).isdisjoint(set(p1))) for p0, p1 in it.combinations(x, 2)) 

iterables = ['AB', 'CDE', 'AF'] 
print(all_disjoint(iterables)) # -> False 

iterables = ['AB', 'CDE', 'FG'] 
print(all_disjoint(iterables)) # -> True 

# your code gives different answer on this one 
# (because it doesn't check what you want) 
iterables = ['AB', 'CDE', 'AH', 'FG'] 
print(all_disjoint(iterables)) # -> False 
+0

ありがとうございます。なぜあなたは 'ペアワイズ 'を持っていますか?あなたはそれを使用していないようです。 – pylang

+0

pylang:良いポイントはもう消えました。それは、以前のいくつかの開発から残った単なるものでした。 – martineau

+0

'itertools'とこの簡単なアプローチを使用する場合+1。 ( 'AB'、 'C​​DE') 'と'(CDE '、' AB ')'のテストは等価です。しかし、このアプローチを非イテラブルに拡張する方が簡単です。 'lst = [" AB "、" CDE "、" AD "、123、" FG "]'。 – pylang

関連する問題