배열이 다른 배열의 부분 집합인지 확인


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();


@Bul Ikana이 코드의 작업은 간단하며 확장 메소드는 내부 작업에 IEqualityComparer가 제공되지 않은 경우 재정의 된 객체 클래스 메소드의 Equals 및 GetHashCode를 내부적으로 호출합니다.
Mrinal Kamboj

2
리스트가 길이 n과 m이라면,이 알고리즘의 시간 복잡성은 무엇입니까?
대령 패닉

2
이 containsAll 나라는 LINQ 방법까지 삶은 경우 좋겠지
세바스찬 패튼

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만큼 명확하지는 않습니다.
technophile

2
음, 질문에 "LINQ 사용"이 구체적으로 나와 있기 때문에 동의하지 않습니다.
JaredPar

9
@JaredPar : 그래서 무엇? 누군가가 가고 싶은 길보다 올바른 길을 보여주는 것이 낫지 않습니까?
Jonathan Allen

목록은 순서를 유지하지만 집합은 유지하지 않습니다. 순서가 중요한 경우 잘못된 결과가 나타납니다.
UuDdLrLrSs

11

당신이 경우 단위 테스트 당신은 또한 활용할 수 CollectionAssert.IsSubsetOf의 방법 :

CollectionAssert.IsSubsetOf(subset, superset);

위의 경우 이는 다음을 의미합니다.

CollectionAssert.IsSubsetOf(t2, t1);

7

이것은 여기에 게시 된 다른 솔루션, 특히 최고의 솔루션보다 훨씬 효율적인 솔루션입니다.

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

t2에서 t1에없는 단일 요소를 찾을 수 있으면 t2가 t1의 하위 집합이 아님을 알 수 있습니다. 이 방법의 장점은 .Except 또는 .Intersect를 사용하는 솔루션과 달리 추가 공간을 할당하지 않고 모든 위치에서 수행된다는 것입니다. 또한이 솔루션은 하위 집합 조건을 위반하는 단일 요소를 찾은 후 다른 요소는 계속 검색하면서 중단 될 수 있습니다. 아래는 최적의 긴 형태의 솔루션이며 위의 속기 솔루션보다 테스트에서 약간 빠릅니다.

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

모든 솔루션에 대한 기초적인 성능 분석을 수행했으며 그 결과는 극적입니다. 이 두 솔루션은 .Except () 및 .Intersect () 솔루션보다 약 100 배 빠르며 추가 메모리를 사용하지 않습니다.


바로 그 !t2.Except(t1).Any()일을하는 것입니다. Linq는 계속해서 노력하고 있습니다. 하나 이상의 요소가 있는지 Any()묻습니다 IEnumerable. 이 시나리오에서는 에없는 t2.Except(t1)첫 번째 요소 만 방출합니다 . 의 첫 번째 요소는 경우 에없는 의 모든 요소가있는 경우는, 가장 빠른 완료 에 가 가장 긴 실행됩니다. t2t1t2t1t2t1
abto

일종의 벤치 마크를 가지고 놀면서, 당신이 취할 때 , t1={1,2,3,...9999}그리고 t2={9999,9998,99997...9000}당신은 다음과 같은 측정을 얻는다는 것을 알았습니다 !t2.Except(t1).Any(): 1ms -> t2.All(e => t1.Contains(e)): 702ms. 그리고 범위가 클수록 악화됩니다.
abto

2
이것은 Linq가 작동하는 방식이 아닙니다. t2.Except (t1)를 반환 IEnumerable하지 않습니다 Collection. 예를 들어 ToArray ()또는 내부를 침입하지 않고 ToList ()사용 하거나 완전히 반복하는 경우 가능한 모든 항목을 방출합니다 foreach. linq 지연된 실행 을 검색하여 해당 개념에 대해 자세히 알아보십시오.
abto

1
Linq에서 지연된 실행이 어떻게 작동하는지 완전히 알고 있습니다. 원하는대로 실행을 연기 할 수 있지만 t2가 t1의 하위 집합인지 확인하려면 전체 목록을 반복하여 파악해야합니다. 그 사실을 피할 수는 없습니다.
user2325458

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()t2에서 요소를 더 이상 검사하지 않는 true => 결과.
abto

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는 부모 목록 (ie t1)에 포함되지 않은 자식 목록 (ie ) 에 요소가 존재하는지 확인합니다 .

예 :

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가 두 배열에있는 값만 반환한다는 것입니다. 이 시점에서 결과 집합의 길이가 원래 집합과 동일하면 "set"의 모든 요소도 "check"에 있고 "set"은 "toCheck"의 하위 집합입니다.

참고 : "set"에 중복이 있으면 내 솔루션이 작동하지 않습니다. 다른 사람들의 투표를 훔치고 싶지 않기 때문에 변경하지 않습니다.

힌트 : Cameron의 답변에 투표했습니다.


4
이것은 실제로 세트 인 경우에만 작동하지만, 두 번째 "세트"에 실제로는 목록이므로 반복 된 요소가 포함 된 경우에는 작동하지 않습니다. 의미를 설정하기 위해 HashSet <double>을 사용할 수 있습니다.
tvanfosson

두 Array에 다른 Array에없는 요소가 있으면 작동하지 않습니다.
da_berni
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.