进行多对多数字匹配的更有效方法

时间:2018-12-04 10:31:24

标签: c# algorithm recursion

我有两组不同大小的数字,我需要运行一种算法来确定第二组中的每个元素是否可以由第一组中的唯一元素组成,而第一组中的元素只能使用一次。

示例。

设置一:1、3、5、6、8、14、11。 第二组:25、9、13、1。

要解决的第一个数字是25,您可以将集合1加在一起加上14 + 11。因此25是匹配项,您从第一组中删除了14和11。您只剩下1、3、5、6、8。

下一个要解决的数字是9。6 + 3 = 9,所以您从第一组中删除6和3并设置要匹配的9。现在您剩下1、5和8了。

下一个要解决的数字是13。5 + 8 =13。去掉5和8,剩下的就是1。

下一个要求解的数字是1。该数字与1匹配,因此最后一个数字也要求解。

我已经编写了一个递归函数来执行此操作,如下所示。我的问题是性能太慢-它需要最多在几分钟内作为系统测试运行,但是如果您说第一组中有130个元素和第二组中有60个元素,则解决所有路径所需的时间要长得多。

有人可以建议一种更高效,更高效的方法吗?

    foreach (var rhs in rightSideCandidates)
    {
        var matchingSet = GetMatchingSet(rhs, leftSideCandidates.ToArray(), 0, timer);
        if (matchingSet != null && matchingSet.Length > 0)
        {
            Console.WriteLine($"Matched ({string.Join(",", matchingSet.Select(s => s.Item1))}) to RHS candidate {rhs}");
            matchingSet.ForEach(t => leftSideCandidates.Remove(t));
        }
        else
        {
            Console.WriteLine($"Unable to find a matching set for rhs {rhs}");
        }
    }

private static (int, LabelledResult)[] GetMatchingSet(RightSideCandidate rhs, (int, LabelledResult)[] leftCandidates, decimal runningAggregation, Stopwatch runTimer)
{
    // Time out if we have been running for a long time as it could potentially go into a deep recursive loop
    if (runTimer.Elapsed.Minutes >= 3)
        Assert.Fail("Matching has been running over 3 minutes so aborting the path discovery and failing the test");

    // If we have no more potential candidates or we have exceeded the sought total, end this branch of the search tree as this is not a solution
    if (leftSideCandidates.Length == 0 || (rhs.Value < 0 && runningAggregation < rhs.Value) || (rhs.Value > 0 && runningAggregation > rhs.Value)) return new (int, LabelledResult)[0];

    foreach (var t in leftSideCandidates)
    {
        // If this node results in a solution, return from here to set off the cascading returns which will carry our solution to the top
        if (t.Item2.Value + runningAggregation == rhs.Value) return new[] { t };

        // Get the new candidate set to pass down to child nodes, without this item in it
        var tmpSet = leftSideCandidates.Where(tc => tc.Item1 != t.Item1).ToArray();
        var pathToResult = GetMatchingSet(rhs, tmpSet, runningAggregation + t.Item2.Value, runTimer);

        // If we have found a path to a solution, then return that child path, adding ourself to it. Otherwise continue with the next possible path
        if (pathToResult.Length > 0)
        {
            return pathToResult.Union(new[] { t }).ToArray();
        }
    }

    // We have run out of potential paths on this route, so return nothing to end discovery of this path
    return new (int, LabelledResult)[0];
}

0 个答案:

没有答案