识别分段链的算法

时间:2017-05-19 17:05:16

标签: c# arrays algorithm

我有一系列细分:

ISegment[] segments

由界面定义:

public interface ISegment
{
    Point3D A { get; } // Segment start point
    Point3D B { get; } // Segment end point
}

可以包含以下实例:

public class Line : ISegment
{
    public Point3D A { get; } // Line start point
    public Point3D B { get; } // Line end point
    /* ... Other line properties & methods ... */
}

或实例:

public class Arc : ISegment
{
    public Point3D A { get; } // Arc start point
    public Point3D B { get; } // Arc end point
    /* ... Other arc properties & methods ... */
}

可以用这张图片表示: Segments representation

我正在寻找一种优雅的算法来将它们识别为细分链:

ISegment[][] segmentChains

结果如下:

[[s1, s2, s3, s4], [s5, s6], [s7], [s8, s9, s10], [s11, s12]]

没有任何订单考虑。

注意:

  • 输入1D数组segments可以有任何顺序
  • 输出2D数组segmentChains可以有任何顺序
  • 弧段可以具有相同的起点和终点

欢迎提供一些帮助!

2 个答案:

答案 0 :(得分:2)

我有一个应该有效的迭代方法。但不确定它有多优雅。我更喜欢使用Lists而不是Arrays,因为它们可以动态扩展(初始化时没有大小要求)。

方法签名如下:

public static List<List<ISegment>>GetSegmentChains(List<ISegment> segments)

其中segments是段列表,返回值是“链”列表,其中每个链都是段列表。

该方法的基本思想是:

  1. 从细分列表(候选人)中删除候选细分,并将其存储在临时列表中。
  2. 将其A和B点存储在另一个列表中
  3. 对于每个剩余的段,其端点与列表中的一个点匹配:
    • 将该段的其他(非匹配)点放入列表
    • 将细分与第一个一起存储
    • 从我们的候选人中删除该段
  4. 如果没有其他候选人,请在以下情况下将我们的临时列表添加到返回值列表中:
    • 列表中有多个项目(有效链)-OR -
    • 列表中有一个项目,但它的端点是相同的(单个段圆圈)
  5. 这是一种可以做到的方式:

    public static List<List<ISegment>>GetSegmentChains(List<ISegment> segments)
    {
        // Some quick 'fail fast' validation
        var segmentChains = new List<List<ISegment>>();
        if (segments == null) return segmentChains;
        if (segments.Count == 0) return segmentChains;
        if (segments.Count == 1)
        {
            if (IsSingleSegmentChain(segments[0])) segmentChains.Add(segments);
            return segmentChains;
        }
    
        // Get a copy of our segments
        var candidateSegments = segments.ToList();
    
        // Process each one
        while (candidateSegments.Any())
        {
            // Remove the first one from the candidate list and add it to a temporary chain
            // list, and add it's endPoints to a list for comparision with other candidates
            var candidateSegment = candidateSegments.First();
            candidateSegments.Remove(candidateSegment);
            var candidateChain = new List<ISegment> { candidateSegment };
            var endPoints = new List<Point3D> {candidateSegment.A, candidateSegment.B};
    
            // Go through the points list, finding any candidates with a match
            while (endPoints.Any())
            {
                foreach (var endPoint in endPoints.ToList())
                {
                    // Add the 'other' point to our points list from each 
                    // candidate that has a match with this point
                    foreach (var candidate in candidateSegments
                        .Where(c => ContainsPoint(c, endPoint)).ToList())
                    {
                        endPoints.Add(GetNonMatchingPoint(candidate, endPoint));
                        candidateSegments.Remove(candidate);
                        candidateChain.Add(candidate);
                    }
    
                    // Remove this point since it's been fully processed
                    endPoints.Remove(endPoint);
                }
            }
    
            // See if we have a chain, and if so, add it to our return list
            if (candidateChain.Count == 1 && IsSingleSegmentChain(candidateChain[0]) ||
                candidateChain.Count > 1)
            {
                segmentChains.Add(candidateChain);
            }
        }
    
        return segmentChains;
    }
    

    类修改以包含构造函数和ToString()覆盖

    public interface ISegment
    {
        string Name { get; }
        Point3D A { get; }
        Point3D B { get; }
    }
    
    public class Line : ISegment
    {
        public string Name { get; }
        public Point3D A { get; }
        public Point3D B { get; }
    
        public Line(string name, Point3D a, Point3D b)
        {
            Name = name;
            A = a;
            B = b;
        }
    
        public override string ToString()
        {
            return Name;
        }
    }
    
    public class Arc : ISegment
    {
        public string Name { get; }
        public Point3D A { get; }
        public Point3D B { get; }
    
        public Arc(string name, Point3D singlePoint) : this(name, singlePoint, singlePoint)
        {
        }
    
        public Arc(string name, Point3D a, Point3D b)
        {
            Name = name;
            A = a;
            B = b;
        }
    
        public override string ToString()
        {
            return Name;
        }
    }
    

    帮助方法生成示例中的细分列表,以确定细分是否为单段链,以确定细分是否包含某个点,并获得不匹配从细分中指出:

    public static List<ISegment> GenerateSegmentList()
    {
        // Generate the points in the diagram
        var A = new Point3D(0, 0, 0);
        var B = new Point3D(0, 0, 1);
        var C = new Point3D(0, 0, 2);
        var D = new Point3D(0, 0, 3);
        var E = new Point3D(0, 0, 4);
        var F = new Point3D(0, 0, 5);
        var G = new Point3D(0, 0, 6);
        var H = new Point3D(0, 0, 7);
        var I = new Point3D(0, 0, 8);
        var J = new Point3D(0, 0, 9);
        var K = new Point3D(0, 1, 0);
        var L = new Point3D(0, 1, 1);
        var M = new Point3D(0, 1, 2);
        var N = new Point3D(0, 1, 3);
    
        // Generate the segments in the diagram
        return new List<ISegment>
        {
            new Line("s1", A, B),
            new Line("s2", B, C),
            new Line("s3", C, D),
            new Line("s4", D, E),
            new Line("s5", F, G),
            new Line("s6", G, H),
            new Arc("s7", I),
            new Line("s8", J, K),
            new Line("s9", K, L),
            new Line("s10", L, J),
            new Line("s11", M, N),
            new Arc("s12", N, M)
        };
    }
    
    public static bool IsSingleSegmentChain(ISegment segment)
    {
        return segment != null && segment.A == segment.B;
    }
    
    public static bool ContainsPoint(ISegment segment, Point3D pointToMatch)
    {
        return segment != null && (segment.A == pointToMatch || segment.B == pointToMatch);
    }
    
    public static Point3D GetNonMatchingPoint(ISegment segment, Point3D pointToMatch)
    {
        return segment == null
            ? default(Point3D)
            : (segment.A == pointToMatch)
                ? segment.B
                : segment.A;
    }
    

    使用示例

    private static void Main()
    {
        List<ISegment> segments = GenerateSegmentList();
        List<List<ISegment>> segmentChains = GetSegmentChains(segments);
    
        for(int i = 0; i < segmentChains.Count; i++)
        {
            Console.WriteLine($"Segment Chain #{i + 1}: {string.Join(" => ", segmentChains[i])}");
        }
    
        Console.WriteLine("\nDone!\nPress any key to exit...");
        Console.ReadKey();
    }
    

    <强>输出

    enter image description here

答案 1 :(得分:1)

我认为,以下内容应该有效:

  1. 将它们全部放入F,其中起点是一个键,此时开始的Dictionary段是一个值。
  2. 将所有起点和终点放入字典中,其中值是等于此点的可用终点数。
  3. 从第2步中获取字典中0的任何起始点。它不能添加到任何链中。如果没有这样的观点,请选择任何一个(因为有一个循环)。
  4. 对于临界点,如果存在则从中开始任何段。减少词典中可用点的值。
  5. 如果没有合适的细分,请从第3步继续。