查找具有元组项目的最小累积总和的列表

时间:2014-09-16 12:42:20

标签: c# linq

我有一个List<List<Tuple<int, int, double>>类型的列表。

从语义上讲,它的工作原理如下:

  • 每个元组描述一个坐标(i,j)和一个测量值(double)。
  • 每个元组列表都描述了一个轨迹。它的Sum(t => t.Item3)描述了轨迹上的能量(离散值的积分)。

现在我想找到哪个子列表能量最少。我尝试了下面的代码,但这给了我最低的,而我希望 list 本身具有最低的值。

var minimum_path = all_paths.Min(c => c.Sum(p => p.Item3));

 ^___ "var" is of type double, should be List<Tuple<...>>

4 个答案:

答案 0 :(得分:3)

您必须按总和订购:

List<Tuple<int, int, double>> minList = all_paths
   .OrderBy(l => l.Sum(t => t.Item3))
   .First();

如果您想获得总体上包含最小Item3的列表:

List<Tuple<int, int, double>> minList = all_paths
   .OrderBy(l => l.Min(t => t.Item3))
   .First();

您也可以按此分组以查找具有此值的其他列表:

List<List<Tuple<int, int, double>>> allMinLists = all_paths
      .GroupBy(l => l.Sum(t => t.Item3))
      .OrderBy(g => g.Key)
      .First()
      .ToList();

答案 1 :(得分:2)

这里有一个O(n)解决方案(列表数量)只迭代父列表一次:

var minList = all_paths
    .Aggregate((x, y) => x.Sum(i => i.Item3) < y.Sum(i => i.Item3) ? x : y);

答案 2 :(得分:1)

Orderby有O(n ^ 2)的复杂度,所以我猜这个变体应该更好,因为3 {n次传递和Sum()函数的单次评估而不是OrderBy处理中每个比较的计算:

var pathsWithEnergy = all_paths.Select(c => new {Value = c, Energy = c.Sum(p => p.Item3)}).ToList();
var minimum_energy = pathsWithEnergy.Min(x => x.Energy);
var minimum_path = pathsWithEnergy.Find(x => x.Energy == minimum_energy);

如果列表很大,那么更好的方法是结合我的灵魂和@Rawling的

var minList = all_paths.Select(c => new {Value = c, Energy = c.Sum(p => p.Item3)})
                       .Aggregate((x, y) => x.Energy < y.Energy ? x : y).Value;
在我的工作台上,这个解决方案比使用&#34; brute-aggregate&#34;的原始解决方案快2倍。

class Program
{
    static void Main()
    {
        var all_paths = new List<List<Tuple<int, int, double>>>();
        const int n = 4000;
        for (int i = 0; i < n; i++)
        {
            var tuples = new List<Tuple<int, int, double>>();
            for (int j = 0; j < n; j++)
            {
                tuples.Add(new Tuple<int, int, double>(i, j, (n + i)*j));
            }
            all_paths.Add(tuples);
        }

        var sw = Stopwatch.StartNew();
        var minList = MinList1(all_paths);
        sw.Stop();
        Console.WriteLine("{0} - {1}", minList[0], sw.Elapsed);
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        var sw2 = Stopwatch.StartNew();
        var minList2 = MinList2(all_paths);
        sw2.Stop();
        Console.WriteLine("{0} - {1}", minList2[0], sw2.Elapsed);
    }


    private static List<Tuple<int, int, double>> MinList1(List<List<Tuple<int, int, double>>> all_paths)
    {
        return all_paths.Select(c => new {Value = c, Energy = c.Sum(p => p.Item3)})
                        .Aggregate((x, y) => x.Energy < y.Energy ? x : y).Value;
    }

    private static List<Tuple<int, int, double>> MinList2(List<List<Tuple<int, int, double>>> all_paths)
    {
        return all_paths.Aggregate((x, y) => x.Sum(i => i.Item3) < y.Sum(i => i.Item3) ? x : y);
    }
}

enter image description here

答案 3 :(得分:1)

如果您的应用对性能敏感,我建议您忘记LINQ(Sum :)除外。

private static List<Tuple<int, int, double>> GetMinimumPath(List<List<Tuple<int, int, double>>> all_paths)
{
    double minSum = double.MaxValue;
    List<Tuple<int, int, double>> minSumPath = null;

    foreach (var path in all_paths)
    {
        double curSum = path.Sum(t => t.Item3);
        if (curSum < minSum)
        {
            minSum = curSum;
            minSumPath = path;
        }
    }

    return minSumPath;
}

或者,您可以实现通用MinBy(morelinq source)并将其用作:

var minimum_path = all_paths.MinBy(path => path.Sum(t => t.Item3));