Будь-яка ідея, як перевірити, чи є цей список підмножиною іншого?
Конкретно в мене є
List<double> t1 = new List<double> { 1, 3, 5 };
List<double> t2 = new List<double> { 1, 5 };
Як перевірити, що t2 є підмножиною t1, використовуючи LINQ?
Будь-яка ідея, як перевірити, чи є цей список підмножиною іншого?
Конкретно в мене є
List<double> t1 = new List<double> { 1, 3, 5 };
List<double> t2 = new List<double> { 1, 5 };
Як перевірити, що t2 є підмножиною t1, використовуючи LINQ?
Відповіді:
bool isSubset = !t2.Except(t1).Any();
Використовуйте HashSet замість List, якщо працюєте з наборами. Тоді ви можете просто використовувати IsSubsetOf ()
HashSet<double> t1 = new HashSet<double>{1,3,5};
HashSet<double> t2 = new HashSet<double>{1,5};
bool isSubset = t2.IsSubsetOf(t1);
Вибачте, що він не використовує LINQ. :-(
Якщо вам потрібно використовувати списки, тоді рішення Джареда працює з застереженням, що вам потрібно буде видалити всі повторювані елементи, які існують.
Якщо ви перевіряєте одиницю, ви також можете використовувати метод CollectionAssert.IsSubsetOf :
CollectionAssert.IsSubsetOf(subset, superset);
У наведеному вище випадку це означатиме:
CollectionAssert.IsSubsetOf(t2, t1);
Це значно ефективніше рішення, ніж інші, розміщені тут, особливо верхнє рішення:
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;
}
}
Я зробив кілька принципових аналіз ефективності всіх рішень, і результати є різкими. Ці два рішення приблизно в 100 разів швидше, ніж рішення .Except () та .Intersect (), і не використовують додаткової пам'яті.
!t2.Except(t1).Any()
робиться. Linq працює вперед. Any()
запитує, IEnumerable
чи є принаймні один елемент. У цьому сценарії t2.Except(t1)
випускається лише перший елемент, t2
якого немає t1
. Якщо перший елемент t2
не в t1
ньому, він закінчується найшвидше, якщо всі елементи t2
в t1
ньому знаходяться найдовше.
t1={1,2,3,...9999}
і t2={9999,9998,99997...9000}
, ви отримуєте наступні вимірювання: !t2.Except(t1).Any(): 1ms -> t2.All(e => t1.Contains(e)): 702ms
. І чим гірше, тим більший асортимент.
t2.Except (t1)
повертається IEnumerable
не a Collection
. Він випромінює тільки всі можливі пункти , якщо ви ітерацію повністю над ним, наприклад, ToArray ()
або ToList ()
чи використання , foreach
не порушуючи всередині. Шукайте відкладене виконання linq, щоб прочитати більше про цю концепцію.
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.
Спираючись на відповіді від @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();
}
Спробуйте це
static bool IsSubSet<A>(A[] set, A[] toCheck) {
return set.Length == (toCheck.Intersect(set)).Count();
}
Ідея тут полягає в тому, що Intersect поверне лише ті значення, які є в обох масивах. У цей момент, якщо довжина отриманого набору така сама, як і вихідний набір, то всі елементи в "set" також знаходяться в "check" і тому "set" є підмножиною "toCheck"
Примітка. Моє рішення не працює, якщо "set" має дублікати. Я цього не змінюю, бо не хочу красти чужі голоси.
Підказка: Я проголосував за відповідь Кемерона.