元组列表的所有组合

时间:2016-05-03 21:27:26

标签: c# optimization tuples

我正在练习一些优化问题而且我被卡住了。

我有一个元组列表,我正在做以下事情:

private static int CalculateMinimumTotalCost(List<Tuple<int, int>> tuples)
{
    int minimumCost = 0;

    for(int i=0;i<tuples.Count()-1;i++)
    {
        minimumCost += Math.Max(Math.Abs(tuples[i].Item1 - tuples[i + 1].Item1), Math.Abs(tuples[i].Item2 - tuples[i + 1].Item2));
    }
    return minimumCost;
}

这个想法是,给定一个元组列表和这个数学方程式,我需要找到最低成本。问题在于元组的顺序可以重新排列。我的工作是找到最低成本的元组排列。

所以我想做的是循环所有可能的元组组合并以最低成本返回组合。

例如:

(1,2)(1,1)(1,3)= 3

(1,1)(1,2)(1,3)= 2

所以在这种情况下,我会返回2,因为这种安排成本较低。

据我所知,当有N个元组时,组合的数量是N!。

如何获得元组列表的所有组合?

谢谢!

2 个答案:

答案 0 :(得分:2)

正如其他人所建议你应该创建Point类:

public partial class Point
{
    public int X { get; set; }
    public int Y { get; set; }

    public Point(int x, int y)
    {
        this.X = x;
        this.Y = y;
    }
}

并且,让我们封装函数来计算距离和总成本:

public partial class Point
{
    public static int CalculateDistance(Point p0, Point p1)
    {
        return Math.Max(
            Math.Abs(p0.X - p1.X),
            Math.Abs(p0.Y - p1.Y)
            );
    }
}

public static class PointExtensions
{
    public static int GetTotalCost(this IEnumerable<Point> source)
    {
        return source
            .Zip(source.Skip(1), Point.CalculateDistance)
            .Sum();
    }
}

最后,您需要另一种扩展方法来创建“所有可能的组合”:

public static class PermutationExtensions
{
    public static IEnumerable<IEnumerable<T>> GetPermutations<T>(this IEnumerable<T> source)
    {
        if (source == null || !source.Any())
            throw new ArgumentNullException("source");

        var array = source.ToArray();

        return Permute(array, 0, array.Length - 1);
    }
    private static IEnumerable<IEnumerable<T>> Permute<T>(T[] array, int i, int n)
    {
        if (i == n)
            yield return array.ToArray();
        else
        {
            for (int j = i; j <= n; j++)
            {
                array.Swap(i, j);
                foreach (var permutation in Permute(array, i + 1, n))
                    yield return permutation.ToArray();
                array.Swap(i, j); //backtrack
            }
        }
    }
    private static void Swap<T>(this T[] array, int i, int j)
    {
        T temp = array[i];

        array[i] = array[j];
        array[j] = temp;
    }
}
来自Listing all permutations of a string/integer

来源更适合LINQ友好

用法:

void Main()
{
    var list = new List<Point>
    {
        new Point(1, 2),
        new Point(1, 1),
        new Point(1, 3),
    };

    // result: Point[] (3 items) : (1, 1), (1, 2), (1,3)
    list.GetPermutations()
        .OrderBy(x => x.GetTotalCost())
        .First();
}

编辑:正如@EricLippert所指出的,source.OrderBy(selector).First()有一些额外费用。以下扩展方法处理此问题:

public static class EnumerableExtensions
{
    public static T MinBy<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector, IComparer<TKey> comparer = null)
    {
        IEnumerator<T> etor = null;

        if (source == null || !(etor = source.GetEnumerator()).MoveNext())
            throw new ArgumentNullException("source");

        if (keySelector == null)
            throw new ArgumentNullException("keySelector");

        var min = etor.Current;
        var minKey = keySelector(min);

        comparer = comparer ?? Comparer<TKey>.Default;
        while (etor.MoveNext())
        {
            var key = keySelector(etor.Current);
            if (comparer.Compare(key, minKey) < 0)
            {
                min = etor.Current;
                minKey = key;
            }
        }

        return min;
    }
}

并且,我们可以将上述解决方案重写为:

list.GetPermutations().MinBy(x => x.GetTotalCost())

答案 1 :(得分:-4)

您可以将for循环更改为Foreach以使其更具可读性,而不是使用索引来获取值。

def mod(x,modulus):
    numer, denom = x.as_numer_denom()
    return numer*mod_inverse(denom,modulus) % modulus

pprint(B_rref[0].applyfunc(lambda x: mod(x,5)))

#returns
#[1  0  1  0  4]
#[             ]
#[0  1  3  0  2]
#[             ]
#[0  0  0  1  0]
#[             ]
#[0  0  0  0  0]