C# 递归检索字典的所有可能组合

C# 递归检索字典的所有可能组合,c#,algorithm,combinations,combinatorics,C#,Algorithm,Combinations,Combinatorics,我看到了很多类似的问题,但是没有找到我需要的。谢谢你的帮助 我有一组键(K[1..M]),每个键K[I]可以映射到这个特定键K[I]:V[I,1..Ni的一组可用值中的任何值 K[1]: V[1,1] | V[1,2] ... | V[1,N1] K[2]: V[2,1] | V[2,2] ... | V[1,N2] ... K[M]: V[M,1] | V[M,2] ... | V[1,NM] 我需要实现递归函数,返回K-V映射的所有可能组合的可枚举性 例如: 对于给定集合: K1: 1 |

我看到了很多类似的问题,但是没有找到我需要的。谢谢你的帮助

我有一组键(K[1..M]),每个键K[I]可以映射到这个特定键K[I]:V[I,1..Ni的一组可用值中的任何值

K[1]: V[1,1] | V[1,2] ... | V[1,N1]
K[2]: V[2,1] | V[2,2] ... | V[1,N2]
...
K[M]: V[M,1] | V[M,2] ... | V[1,NM]
我需要实现递归函数,返回K-V映射的所有可能组合的可枚举性

例如: 对于给定集合:

K1: 1 | 2 | 3
K2: 4 | 1
组合如下所示:

(K1:1, K2:4)
(K1:2, K2:4)
(K1:3, K2:4)
(K1:1, K2:1)
(K1:2, K2:1)
(K1:3, K2:1)
理想情况下,函数应如下所示:

IEnumerable<Dictionary<TKey, TValue>> EnumerateAllPossibleCombinations(IEnumerable<TKey> keys, Func<TKey, IEnumerable<TValue>> getAvailableValuesForKey)
{
    ...
    yield return ...;
}
IEnumerable EnumerateAllPossibleCombinations(IEnumerable键,Func getAvailableValuesForKey)
{
...
收益率。。。;
}
功能的使用(代码草案):

var-allcombines=EnumerateAllPossibleCombinations(新[]{“K1”,“K2”},k=>{
开关k
{
案例“K1”:返回新的[]{1,2,3};
案例“K2”:返回新[]{4,1};
}
ThroweException(“未知密钥”);
});
例如,上面的结果应该是6个字典,每个字典中有2个键值对


我试图避免使用笛卡尔积,因为我需要一个接一个地接收字典(allCombinations.ElementAt(1),allCombinations.ElementAt(2)),而笛卡尔积必须在返回第一个字典之前完全针对所有组合执行。

您的解决方案基本上采用(伪代码)的形式:

如果需要传入谓词以约束值,则可以插入该谓词:

foreach (var key in dictionary)
    foreach (var value in getAvailableValuesForKey)
        yield return new KeyValuePair(key, value)
实际代码将更接近于此(未测试):


我相信这就是你要找的

public static IEnumerable<IDictionary<TKey, TSource>> EnumerateAllPossibleCombinations<TKey, TSource>(
    IEnumerable<TKey> keys,
    Func<TKey, IEnumerable<TSource>> getAvailableValuesForKey)
{
    if (keys == null)
    {
        throw new ArgumentNullException("keys");
    }

    if (getAvailableValuesForKey == null)
    {
        throw new ArgumentNullException("getAvailableValuesForKey");
    }

    return keys.Any() ? 
           EnumerateAllPossibleCombinationsImp(keys.Distinct(), getAvailableValuesForKey) :
           Enumerable.Empty<IDictionary<TKey, TSource>>();
}

private static IEnumerable<IDictionary<TKey, TSource>> EnumerateAllPossibleCombinationsImp<TKey, TSource>(
    IEnumerable<TKey> keys,
    Func<TKey, IEnumerable<TSource>> getAvailableValuesForKey)
{
    if (!keys.Any())
    {
        yield return new Dictionary<TKey, TSource>();
        yield break;
    }

    var firstKey = keys.First();
    var values = getAvailableValuesForKey(firstKey) ?? Enumerable.Empty<TSource>();
    bool hasValues = values.Any();

    foreach (var value in values.DefaultIfEmpty())
    {
        foreach (var dictionary in EnumerateAllPossibleCombinationsImp(keys.Skip(1), getAvailableValuesForKey))
        {
            if (hasValues)
            {
                dictionary.Add(firstKey, value);
            }

            yield return dictionary;
        }
    }
}
公共静态IEnumerable EnumerateAllPossibleCombination(
i数不清的键,
Func GetAvailableValuesWorkey)
{
如果(键==null)
{
抛出新的异常(“键”);
}
if(getAvailableValuesForKey==null)
{
抛出新ArgumentNullException(“getAvailableValuesForKey”);
}
返回键。有()吗?
EnumerateAllPossibleCombinationsImp(key.Distinct(),GetAvailableValuesOfferKey):
Enumerable.Empty();
}
私有静态IEnumerable EnumerateAllPossibleCombinationsImp(
i数不清的键,
Func GetAvailableValuesWorkey)
{
如果(!keys.Any())
{
返回新字典();
屈服断裂;
}
var firstKey=keys.First();
var values=GetAvailableValuesMarkey(firstKey)??Enumerable.Empty();
bool hasValues=values.Any();
foreach(values.DefaultIfEmpty()中的var值)
{
foreach(EnumerateAllPossibleCombinationsImp(keys.Skip(1),getAvailableValuesForKey)中的var字典)
{
if(hasValues)
{
dictionary.Add(firstKey,value);
}
收益返回字典;
}
}
}
首先,使用这两种方法的原因是,在调用方法时,而不是在迭代生成的
IEnumerable
时,将抛出
GetAvailableValuesMarkey
ArgumentNullException
s。接下来,我们快速检查
keys
是否为空,如果为空,我们只返回一个空的
IEnumerable
。如果它不是空的,那么我们调用包含主实现的第二个方法


第二种方法是递归的,因此首先我们设置默认情况,即
keys
为空,在这种情况下我们生成一个空字典(这就是为什么我们在第一种方法中执行空检查)。然后,我们通过从
中获取第一项来分解问题,并检索该键的
。如果
getAvailableValuesForKey
为该键返回
null
,我们将视其为返回空集(如果需要,可以将其视为例外情况)。然后检查是否有
,并对其进行迭代,但如果没有,则使用
DefaultIfEmpty
插入一个默认值。这样我们就可以对一组空值进行一次迭代。然后我们使用
Skip(1)
执行递归调用,传入其余的键,对于每个返回的字典,我们检查是否有键的值,如果有,则将该键和值对添加到字典中,并以任何方式生成字典。其思想是,递归调用最终会遇到没有键的情况,只返回一个空字典,然后在递归调用展开时将条目添加到字典中。

您需要Func做什么?这不就是主函数中的另一个循环吗?或者你打算传递一个谓词吗?那么,对于你的例子,你希望它返回6个字典,每个字典有两个条目?我根据你的评论更新了问题文本,并进行了额外的澄清。为什么人们会对这个问题投反对票?它有什么问题?为什么不直接迭代
属性呢?谢谢你的回答,不过这本质上是笛卡尔积的解决方案,我在这里试图避免。我在问题上加上了那张便条。
foreach (var key in dictionary)
    foreach (var value in getAvailableValuesForKey)
        yield return new KeyValuePair(key, value)
foreach (var x in dictionary)
    foreach (var y in dictionary)
        yield return new KeyValuePair (x.key, y.value);
public static IEnumerable<IDictionary<TKey, TSource>> EnumerateAllPossibleCombinations<TKey, TSource>(
    IEnumerable<TKey> keys,
    Func<TKey, IEnumerable<TSource>> getAvailableValuesForKey)
{
    if (keys == null)
    {
        throw new ArgumentNullException("keys");
    }

    if (getAvailableValuesForKey == null)
    {
        throw new ArgumentNullException("getAvailableValuesForKey");
    }

    return keys.Any() ? 
           EnumerateAllPossibleCombinationsImp(keys.Distinct(), getAvailableValuesForKey) :
           Enumerable.Empty<IDictionary<TKey, TSource>>();
}

private static IEnumerable<IDictionary<TKey, TSource>> EnumerateAllPossibleCombinationsImp<TKey, TSource>(
    IEnumerable<TKey> keys,
    Func<TKey, IEnumerable<TSource>> getAvailableValuesForKey)
{
    if (!keys.Any())
    {
        yield return new Dictionary<TKey, TSource>();
        yield break;
    }

    var firstKey = keys.First();
    var values = getAvailableValuesForKey(firstKey) ?? Enumerable.Empty<TSource>();
    bool hasValues = values.Any();

    foreach (var value in values.DefaultIfEmpty())
    {
        foreach (var dictionary in EnumerateAllPossibleCombinationsImp(keys.Skip(1), getAvailableValuesForKey))
        {
            if (hasValues)
            {
                dictionary.Add(firstKey, value);
            }

            yield return dictionary;
        }
    }
}