最接近的排列

时间:2014-03-16 15:07:32

标签: c# algorithm permutation

我目前有两个4个3D点列表,让我们调用列表A和B.我想将A中的每个点连接到B中的一个(并且只有一个)点,以便A和B之间的总距离最小化。

例如,如果我有:

一个 1:(0,0,0) 2:(0,10,0) 3:(0,20,0) 4:(0,30,0)

乙 1:(0,35,10) 2:(0,25,10) 3:(0,15,10) 4:(0,5,10)

最佳解决方案是将A1与B4连接,A2与B3连接,A3与B2连接,A4与B1连接。

我如何以合理的方式计算这个?

2 个答案:

答案 0 :(得分:4)

当项目数量很小时,就像你的情况一样,你可以通过在三个嵌套循环中强制执行所有排列来实现这一点:

Point3D[] a = new Point3D[4];
Point3D[] b = new Point3D[4];
for (int i = 0 ; i != 4 ; i++) {
    for (int j = 0 ; j != 4 ; j++) {
        if (j == i) continue;
        for (int k = 0 ; k != 4 ; k++) {
            int m = 6 - i - j - k;
            if (k == i || k == j || m == i || m == j || m == k) continue;
            var d = a[0].Distance(b[i]) +a[1].Distance(b[j]) + a[2].Distance(b[k]) + a[3].Distance(b[m]);
            min = Math.Min(d, min);
        }
    }
}

这可以找到4的最小值! = 24次迭代。如果你有更多的点,比如说超过10个,那么可以使用更好的算法 - 你可以使用Hungerian algorithm在多项式时间 O中找到最小权重匹配(n 3 < / SUP>)

答案 1 :(得分:0)

    public static IEnumerable<Result> Connect(this IEnumerable<Point> setA, IEnumerable<Point> setB) {
        return setA
            .Select(a => setB.Select(b => new Result { A = a, B = b, D = a.DistanceTo(b) }))
            .SelectMany(s => s)
            .OrderBy(s => s.D)
            .Reduce(new Result[] { }, (a,b) => a.A != b.A && a.B != b.B);
    }

使用:

    public static IEnumerable<T> Concat<T>(this T h, IEnumerable<T> t) {
        yield return h;
        foreach (var r in t) {
            yield return r;
        }
    }

    static IEnumerable<T> Reduce<T>(this IEnumerable<T> items, IEnumerable<T> collected, Func<T,T,bool> predicate) {
        if (!items.Any())
            return new T[0];

        var t = items.First();
        var filtered = items.Where(s => predicate(s,t));
        return t.Concat(filtered.Reduce(t.Concat(collected), predicate));
    }

,其中

struct Result {
    public Point A;
    public Point B;
    public double D;//distance
}

struct Point {
    public int X;
    public int Y;
    public int Z;
}

double DistanceTo(this Point a, Point b) {
    return Math.Sqrt((a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y) + (a.Z - b.Z) * (a.Z - b.Z));
}