通过c#生成器设置最大独立子集

时间:2013-03-09 02:46:44

标签: c# iterator set

我想找到给定集合的所有子集,这些子集是互斥的,并且包含尽可能多的超集元素。用户定义排他性的含义:

bool exclusion<T>(T a, T b)

至少exclusion(a, b) == exclusion(b, a)成立。

exclusion(a, b) == true

保证a.Equals(b) == true

我的代码如下所示:

public static HashSet<HashSet<T>> MutuallyExclusive<T>(this IEnumerable<T> available, Func<T, T, bool> exclusion) {
    HashSet<HashSet<T>> finished = new HashSet<HashSet<T>>(new HashSetEquality<T>());
    Recursion<T>(available, new HashSet<T>(), finished, exclusion);
    return finished;
}

private static void Recursion<T>(IEnumerable<T> available, HashSet<T> accepted, HashSet<HashSet<T>> finished, Func<T, T, bool> exclusion) {
    if (!available.Any())
        finished.Add(accepted);
    else
        foreach (T a in available)
            Recursion<T>(available.Where(b => !exclusion(a, b)), new HashSet<T>(accepted) { a }, finished, exclusion);
}

private class HashSetEquality<T> : IEqualityComparer<HashSet<T>> {
    public bool Equals(HashSet<T> x, HashSet<T> y) {
        if (x.Count != y.Count)
            return false;
        return x.All(t => y.Contains(t));
    }

    public int GetHashCode(HashSet<T> obj) {
        return obj.Aggregate(0, (i, t) => i ^ t.GetHashCode());
    }
}

有没有办法将此代码转换为逐个移动可接受值的迭代器?

修改

在我的问题中,我似乎有点不高兴,对不起。我实际上正在寻找一个生成器来进行自定义执行。因此,每次调用它时,只计算下一个接受的集合

2 个答案:

答案 0 :(得分:2)

基本上,您要做的是每次拨打finished.Add()时都会产生一个新集,并返回true

但由于递归,您还需要生成递归调用返回的所有值。你可以通过在循环中产生所有这些值来做到这一点:

public static IEnumerable<HashSet<T>> MutuallyExclusive<T>(
    this IEnumerable<T> available, Func<T, T, bool> exclusion)
{
    var finished = new HashSet<HashSet<T>>(new HashSetEquality<T>());
    return Recursion<T>(available, new HashSet<T>(), finished, exclusion);
}

private static IEnumerable<HashSet<T>> Recursion<T>(
    IEnumerable<T> available, HashSet<T> accepted, HashSet<HashSet<T>> finished,
    Func<T, T, bool> exclusion)
{
    if (!available.Any())
    {
        if (finished.Add(accepted))
            yield return finished;
    }
    else
        foreach (T a in available)
        {
            var results = Recursion<T>(
                available.Where(b => !exclusion(a, b)),
                new HashSet<T>(accepted) { a }, finished, exclusion);

            foreach (var set in results)
                yield return set;
        }
}

这可能不是最有效的解决方案,但它可以完成工作。

此外,您可能需要考虑仅浏览每个子集一次。这样,您就不需要finished集,您可以直接获得您找到的所有结果。

答案 1 :(得分:-1)

而不是return finished;你可以使用

foreach (HashSet<T> set in finished)
    yield return set;

既然你正在创建一个生成器(我认为这就是他们所谓的那个?),你需要将MutuallyExclusive的签名从HashSet<HashSet<T>>更改为IEnumerable<HashSet<T>>。所以基本上MutuallyExclusive看起来像是:

public static IEnumerable<HashSet<T>> MutuallyExclusive<T>(this IEnumerable<T> available, Func<T, T, bool> exclusion)
{
    HashSet<HashSet<T>> finished = new HashSet<HashSet<T>>(new HashSetEquality<T>());
    Recursion<T>(available, new HashSet<T>(), finished, exclusion);
    foreach (HashSet<T> set in finished)
        yield return set;
}