配列が別の配列のサブセットであるかどうかを確認する


145

そのリストが別のリストのサブセットであるかどうかを確認する方法に関するアイデアはありますか?

具体的には、

List<double> t1 = new List<double> { 1, 3, 5 };
List<double> t2 = new List<double> { 1, 5 };

LINQを使用して、t2がt1のサブセットであることを確認するにはどうすればよいですか?


リストが(例のように)ソートされている場合、これはO(n + m)時間で可能です。
大佐パニック

回答:


255
bool isSubset = !t2.Except(t1).Any();

1
私は、拡張メソッドを作成しましたgeekswithblogs.net/mnf/archive/2011/05/13/...
マイケルFreidgeim

@Bul Ikanaこのコードの処理は単純で、拡張メソッドは、ジョブにIEqualityComparerが提供されていない場合、オーバーライドされたオブジェクトクラスメソッドのEqualsおよびGetHashCodeを内部的に呼び出します。
Mrinal Kamboj

2
リストが長さnおよびmの場合、このアルゴリズムの時間の複雑さはどのくらいですか?
大佐パニック

2
これが「ContainsAll」と呼ばれるlinqメソッドに要約されるとよいでしょう
Sebastian Patten

60

セットを使用する場合は、ListではなくHashSetを使用してください。次に、単純にIsSubsetOf()を使用できます

HashSet<double> t1 = new HashSet<double>{1,3,5};
HashSet<double> t2 = new HashSet<double>{1,5};

bool isSubset = t2.IsSubsetOf(t1);

LINQを使用していません。:-(

リストを使用する必要がある場合、@ Jaredのソリューションは、存在する繰り返し要素を削除する必要があるという警告とともに機能します。


3
丁度。集合演算が必要な場合は、それらのために設計されたクラスを使用してください。Cameronのソリューションは独創的ですが、HashSetほど明確で表現力がありません。
テクノフィール

2
質問では「LINQを使用する」と具体的に言っているので、私は同意しません。
JaredPar

9
@JaredPar:だから何?誰かに正しい方向を示すことは、彼らが望んでいる方法よりも良いことではありませんか?
ジョナサンアレン

リストは順序を維持しますが、セットは維持しません。順序が重要な場合、これは誤った結果をもたらします。
UuDdLrLrSs

11

あなたがいる場合は、ユニットテストをあなたにも利用できるCollectionAssert.IsSubsetOfの方法を:

CollectionAssert.IsSubsetOf(subset, superset);

上記の場合、これは次のことを意味します。

CollectionAssert.IsSubsetOf(t2, t1);

7

これは、ここに掲載されている他のソリューション、特にトップソリューションよりもはるかに効率的なソリューションです。

bool isSubset = t2.All(elem => t1.Contains(elem));

t1にないt2の単一の要素を見つけることができる場合、t2はt1のサブセットではないことがわかります。この方法の利点は、.Exceptまたは.Intersectを使用するソリューションとは異なり、追加のスペースを割り当てることなく、すべてがインプレースで実行されることです。さらに、このソリューションは、サブセット条件に違反する要素が1つ検出されるとすぐに中断し、他の要素は検索を続行します。以下は、最適な長い形式のソリューションです。これは、私のテストでは、上記の省略形のソリューションよりもわずかに高速です。

bool isSubset = true;
foreach (var element in t2) {
    if (!t1.Contains(element)) {
        isSubset = false;
        break;
    }
}

私はすべてのソリューションの基本的なパフォーマンス分析を行いましたが、結果は劇的です。これら2つのソリューションは、.Except()および.Intersect()ソリューションよりも約100倍高速で、追加のメモリを使用しません。


それがまさにその通りです!t2.Except(t1).Any()。Linqは前向きに取り組んでいます。少なくとも1つの要素がAny()あるIEnumerableかどうかを尋ねています。このシナリオでt2.Except(t1)は、t2にはない最初の要素のみが出力されt1ます。の最初の要素t2が含まれt1ていない場合、最も早く終了します。すべての要素t2が含まれt1ている場合、最も長く実行されます。
abto 2014年

ある種のベンチマークで遊んでいるときに、t1={1,2,3,...9999}およびを使用するとt2={9999,9998,99997...9000}、次の測定値が得られることがわかりました!t2.Except(t1).Any(): 1ms -> t2.All(e => t1.Contains(e)): 702ms。そして、それは範囲が大きくなるほど悪くなります。
abto 2014年

2
これはLinqの動作方法ではありません。t2.Except (t1)IEnumerablenotを返しCollectionます。あなたは、たとえば、その上に完全で繰り返す場合にのみ可能な項目のすべてを放出するToArray ()か、ToList ()または使用foreach中を壊すことなく。linq遅延実行を検索して、その概念の詳細を読んでください。
abto 2014年

1
Linqで遅延実行がどのように機能するかを十分に理解しています。実行は必要なだけ延期できますが、t2がt1のサブセットであるかどうかを判断する場合は、リスト全体を反復処理してそれを把握する必要があります。その事実を回避する方法はありません。
user2325458 2014年

2
コメントから例を見てみましょうt2={1,2,3,4,5,6,7,8} t1={2,4,6,8} t2.Except(t1)=> t2の最初の要素= 1 => 1とt1の差は1です({2,4,6,8}に対してチェックされます)=> Except()最初の要素1を発行します=> Any()要素を取得します=> Any()結果はtrue => t2の要素のチェックは行われません。
abto 2014年

6

拡張メソッドとしての@Cameronのソリューション:

public static bool IsSubsetOf<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
    return !a.Except(b).Any();
}

使用法:

bool isSubset = t2.IsSubsetOf(t1);

(これは似ていますが、@ Michaelのブログに投稿されたものと同じではありません)


0

@Cameronおよび@Neilからの回答に基づいて、Enumerableクラスと同じ用語を使用する拡張メソッドを作成しました。

/// <summary>
/// Determines whether a sequence contains the specified elements by using the default equality comparer.
/// </summary>
/// <typeparam name="TSource">The type of the elements of source.</typeparam>
/// <param name="source">A sequence in which to locate the values.</param>
/// <param name="values">The values to locate in the sequence.</param>
/// <returns>true if the source sequence contains elements that have the specified values; otherwise, false.</returns>
public static bool ContainsAll<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> values)
{
    return !values.Except(source).Any();
}

0

ここではt2、親リスト(つまりt1)に含まれていない要素が子リスト(つまり)に存在するかどうかを確認します。そのようなものが存在しない場合、リストは他のサブセットです。

例えば:

bool isSubset = !(t2.Any(x => !t1.Contains(x)));

-1

これを試して

static bool IsSubSet<A>(A[] set, A[] toCheck) {
  return set.Length == (toCheck.Intersect(set)).Count();
}

ここでの考え方は、Intersectは両方の配列にある値のみを返すということです。この時点で、結果セットの長さが元のセットと同じである場合、「セット」のすべての要素も「チェック」にあるため、「セット」は「toCheck」のサブセットになります。

注:「セット」に重複がある場合、私のソリューションは機能しません。他の人の票を盗みたくないので変更しません。

ヒント:キャメロンの回答に投票しました。


4
これは実際にセットである場合は機能しますが、2番目の「セット」が実際にはリストであるため、繰り返される要素が含まれている場合は機能しません。HashSet <double>を使用して、セマンティクスが設定されていることを確認できます。
tvanfosson 2008

両方の配列に要素があり、他の配列にはない要素がある場合、機能しません。
da_berni 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.