我在2D中有一些点(蓝色)。
我希望得到这三个点,它们以这种方式形成一个三角形,点 D (红色)位于这个三角形内。 如果没有这样的三角形,则可以抛出异常。
所以对于上面的图片我想得到黑点:
到目前为止我做了什么: 我以为我可以通过他们到 D 的距离来排序点,而不是从排序列表中取出前三个点。但问题是,可能是这三个最近的点形成一个三角形,不包括点 D 。如下图所示:
除了得到错误的点之外,我无法确定天气 D 位于找到的点的凸包中,因此如果有一个包含点 D的三角形。 这就是我被卡住的地方。
答案 0 :(得分:2)
正如TaW的评论中正确指出的那样,基本形式的以下算法并不总能找到最佳解决方案或解决方案,因为它会贪婪地开始使用两个壁橱点。
但是这可以通过重复算法来解决:如果它找不到三角形,你可以重复它忽略第一个最近的点。
如果您没有多少分,那么无论如何都可以针对不同的起点重复算法,以确保您找到最佳解决方案。
1)找到最接近 D 的点,我们称之为 A
2)找到第二个最接近 D 的点,我们称之为 B
3)找到通过 D 和 A 的线的等式,我们称之为 L1 (缺少的点必须位于 L1 的另一侧,而不是 B )
4)找到通过 D 和 B 的线的等式,我们称之为 L2 (缺失点必须位于 L2 的另一侧,而不是 A )
5)过滤其余点:只留下位于 L1 另一侧的点而不是 B ,并且位于的另一侧L2 而不是 A
6)如果没有这样的点,抛出异常(或重复不同的起点)
7)否则找到最近的一个,我们称之为 C
8)结果是三角形 ABC
附加说明:
如果这个等式给出了它们的坐标给出了不同的符号( X , Y , Z 线方程系数,通常使用 A , B , C ,但我不想将它们与上面的点标签混淆):
使用以下公式可以找到通过坐标(V1x,V1y)和(V2x,V2y)的两个点的线的公式:
其中为线方程系数提供了以下公式:
答案 1 :(得分:2)
关键是寻找有效的解决方案,同时专注于最佳解决方案:
对于每一点: - 存储到目标点的距离 - 相对于目标点的存储位置。
我会使用以下枚举来存储位置:
enum RelativePosition
{
ll,
le,
lg,
eg,
gg,
ge,
gl,
el,
ee
}
其中第一个字母表示相对于目标的x坐标的点的x坐标,第二个字母表示相对于目标的y坐标的点的y坐标。
l less,g greater,e equal
按距离(升序)到目标点订购点数 从最近点开始,并根据相对位置获得将在目标周围形成三角形的候选列表。此外,最接近那些候选人的目标,并以相同的方式继续第三点。
我现在在移动设备上,很难提供代码,但我可以在一两个小时内编写代码。
编辑:
抱歉延误。这是一些代码。
你会看到在ValidPositions
方法中我硬编码了相对于第一和第二点位置的所有有效位置。我知道它们之间存在数学关系,它们可以生成但我们可以说我将其作为一种练习。 :)
即使使用此方法,也有可能无法确定目标点是否在三角形区域内(请参阅UncertainSolution
方法)。但是,TriangleContainsPoint
的测试次数会减少。
编辑2:修正了方法TriangleContainsPoint
中的错误class Point2D
{
public double X { get; set; }
public double Y { get; set; }
}
enum RelPos2D
{
ll = 1,
le = 2,
lg = 3,
eg = 4,
gg = 5,
ge = 6,
gl = 7,
el = 8,
ee = 0
}
static class Tools2D
{
public static double Distance(Point2D Point1, Point2D Point2)
{
return Math.Sqrt(Math.Pow(Point1.X - Point2.X, 2) + Math.Pow(Point1.Y - Point2.Y, 2));
}
public static RelPos2D RelativePosition(Point2D Of, Point2D To)
{
int xRel = Of.X < To.X ? -1 : Of.X > To.X ? 1 : 0;
int yRel = Of.Y < To.Y ? -1 : Of.Y > To.Y ? 1 : 0;
switch (xRel)
{
case -1:
switch (yRel)
{
case -1: return RelPos2D.ll;
case 0: return RelPos2D.le;
case 1: return RelPos2D.lg;
}
break;
case 0:
switch (yRel)
{
case -1: return RelPos2D.el;
case 0: return RelPos2D.ee;
case 1: return RelPos2D.eg;
}
break;
case 1:
switch (yRel)
{
case -1: return RelPos2D.gl;
case 0: return RelPos2D.ge;
case 1: return RelPos2D.gg;
}
break;
}
return RelPos2D.ee; // never reached
}
public static double TriangleArea(Point2D Point1, Point2D Point2, Point2D Point3)
{
return 1 / 2d *
(
(Point1.X - Point3.X) * (Point2.Y - Point1.Y) -
(Point1.X - Point2.X) * (Point3.Y - Point1.Y)
);
}
public static bool TriangleContainsPoint(Point2D Point1, Point2D Point2, Point2D Point3, Point2D Target)
{
var s = Point1.Y * Point3.X - Point1.X * Point3.Y + (Point3.Y - Point1.Y) * Target.X + (Point1.X - Point3.X) * Target.Y;
var t = Point1.X * Point2.Y - Point1.Y * Point2.X + (Point1.Y - Point2.Y) * Target.X + (Point2.X - Point1.X) * Target.Y;
if ((s < 0) != (t < 0))
return false;
var area = TriangleArea(Point1, Point2, Point3);
var sign = area < 0 ? -1 : 1;
s *= sign;
t *= sign;
area *= sign;
return s > 0 && t > 0 && (s + t) < 2 * area;
}
}
class ProblemSolver
{
private static RelPos2D[] AllPositions = new RelPos2D[]
{
RelPos2D.ee,
RelPos2D.eg,
RelPos2D.el,
RelPos2D.ge,
RelPos2D.gg,
RelPos2D.gl,
RelPos2D.le,
RelPos2D.lg,
RelPos2D.ll,
};
private static RelPos2D[] NoPositions = new RelPos2D[0];
private static RelPos2D[] ValidPositions(RelPos2D Pos1, RelPos2D Pos2)
{
if (Pos1 == RelPos2D.ee || Pos2 == RelPos2D.ee)
return AllPositions;
switch (Pos1)
{
case RelPos2D.ll:
switch (Pos2)
{
case RelPos2D.ll:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.gg };
case RelPos2D.le:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.gg, RelPos2D.ge };
case RelPos2D.lg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.gg, RelPos2D.ge, RelPos2D.gl };
case RelPos2D.eg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.gg, RelPos2D.ge, RelPos2D.gl, RelPos2D.el };
case RelPos2D.gg:
return AllPositions;
case RelPos2D.ge:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.le, RelPos2D.lg, RelPos2D.eg, RelPos2D.gg };
case RelPos2D.gl:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.lg, RelPos2D.eg, RelPos2D.gg };
case RelPos2D.el:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.eg, RelPos2D.gg };
}
break;
case RelPos2D.le:
switch (Pos2)
{
case RelPos2D.ll:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.gg, RelPos2D.ge };
case RelPos2D.le:
return NoPositions;
case RelPos2D.lg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.ge, RelPos2D.gl };
case RelPos2D.eg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.ge, RelPos2D.gl, RelPos2D.el };
case RelPos2D.gg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.ge, RelPos2D.gl, RelPos2D.el, RelPos2D.ll };
case RelPos2D.ge:
return AllPositions.Except(new RelPos2D[] { Pos1, Pos2 }).ToArray();
case RelPos2D.gl:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.lg, RelPos2D.eg, RelPos2D.gg };
case RelPos2D.el:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.eg, RelPos2D.gg };
}
break;
case RelPos2D.lg:
switch (Pos2)
{
case RelPos2D.ll:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.gg, RelPos2D.ge, RelPos2D.gl };
case RelPos2D.le:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.ge, RelPos2D.gl };
case RelPos2D.lg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.gl};
case RelPos2D.eg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.gl, RelPos2D.el };
case RelPos2D.gg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.gl, RelPos2D.el, RelPos2D.ll };
case RelPos2D.ge:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.gl, RelPos2D.el, RelPos2D.ll, RelPos2D.le };
case RelPos2D.gl:
return AllPositions;
case RelPos2D.el:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.eg, RelPos2D.gg, RelPos2D.ge, RelPos2D.gl };
}
break;
case RelPos2D.eg:
switch (Pos2)
{
case RelPos2D.ll:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.gg, RelPos2D.ge, RelPos2D.gl, RelPos2D.el };
case RelPos2D.le:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.ge, RelPos2D.gl, RelPos2D.el };
case RelPos2D.lg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.gl, RelPos2D.el };
case RelPos2D.eg:
return NoPositions;
case RelPos2D.gg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.el, RelPos2D.ll };
case RelPos2D.ge:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.el, RelPos2D.ll, RelPos2D.le };
case RelPos2D.gl:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.el, RelPos2D.ll, RelPos2D.le, RelPos2D.lg };
case RelPos2D.el:
return AllPositions.Except(new RelPos2D[] { Pos1, Pos2}).ToArray();
}
break;
case RelPos2D.gg:
switch (Pos2)
{
case RelPos2D.ll:
return AllPositions;
case RelPos2D.le:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.ge, RelPos2D.gl, RelPos2D.el, RelPos2D.ll };
case RelPos2D.lg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.gl, RelPos2D.el, RelPos2D.ll };
case RelPos2D.eg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.el, RelPos2D.ll };
case RelPos2D.gg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.ll };
case RelPos2D.ge:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.ll, RelPos2D.le};
case RelPos2D.gl:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.ll, RelPos2D.le, RelPos2D.lg };
case RelPos2D.el:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.ll, RelPos2D.le, RelPos2D.lg, RelPos2D.eg };
}
break;
case RelPos2D.ge:
switch (Pos2)
{
case RelPos2D.ll:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.le, RelPos2D.lg, RelPos2D.eg, RelPos2D.gg };
case RelPos2D.le:
return AllPositions.Except(new RelPos2D[] { Pos1, Pos2 }).ToArray();
case RelPos2D.lg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.gl, RelPos2D.el, RelPos2D.ll, RelPos2D.le };
case RelPos2D.eg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.el, RelPos2D.ll, RelPos2D.le };
case RelPos2D.gg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.ll, RelPos2D.le };
case RelPos2D.ge:
return NoPositions;
case RelPos2D.gl:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.le, RelPos2D.lg };
case RelPos2D.el:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.le, RelPos2D.lg, RelPos2D.eg };
}
break;
case RelPos2D.gl:
switch (Pos2)
{
case RelPos2D.ll:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.lg, RelPos2D.eg, RelPos2D.gg };
case RelPos2D.le:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.lg, RelPos2D.eg, RelPos2D.gg, RelPos2D.ge };
case RelPos2D.lg:
return AllPositions;
case RelPos2D.eg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.el, RelPos2D.ll, RelPos2D.le, RelPos2D.lg };
case RelPos2D.gg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.ll, RelPos2D.le, RelPos2D.lg };
case RelPos2D.ge:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.le, RelPos2D.lg};
case RelPos2D.gl:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.lg };
case RelPos2D.el:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.lg, RelPos2D.eg };
}
break;
case RelPos2D.el:
switch (Pos2)
{
case RelPos2D.ll:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.eg, RelPos2D.gg };
case RelPos2D.le:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.eg, RelPos2D.gg, RelPos2D.ge };
case RelPos2D.lg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.eg, RelPos2D.gg, RelPos2D.ge, RelPos2D.gl };
case RelPos2D.eg:
return AllPositions.Except(new RelPos2D[] { Pos1, Pos2 }).ToArray();
case RelPos2D.gg:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.ll, RelPos2D.le, RelPos2D.lg, RelPos2D.eg };
case RelPos2D.ge:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.le, RelPos2D.lg, RelPos2D.eg };
case RelPos2D.gl:
return new RelPos2D[] { RelPos2D.ee, RelPos2D.lg, RelPos2D.eg };
case RelPos2D.el:
return NoPositions;
}
break;
}
return NoPositions;
}
private static bool UncertainSolution(RelPos2D Pos1, RelPos2D Pos2, RelPos2D Pos3)
{
RelPos2D[] array = new RelPos2D[] { Pos1, Pos2, Pos3 };
return
(array.Contains(RelPos2D.ll) && array.Contains(RelPos2D.gg)) ||
(array.Contains(RelPos2D.lg) && array.Contains(RelPos2D.gl));
}
public Tuple<Point2D, Point2D, Point2D> Solve(Point2D Target, params Point2D[] Points)
{
Dictionary<Point2D, double> distanceToTarget = new Dictionary<Point2D, double>();
Dictionary<Point2D, RelPos2D> relativePosition = new Dictionary<Point2D,RelPos2D>();
List<int> visited = new List<int>();
Dictionary<RelPos2D, int> countPerPosition = new Dictionary<RelPos2D, int>()
{
{RelPos2D.ee,0},
{RelPos2D.eg,0},
{RelPos2D.el,0},
{RelPos2D.ge,0},
{RelPos2D.gg,0},
{RelPos2D.gl,0},
{RelPos2D.le,0},
{RelPos2D.lg,0},
{RelPos2D.ll,0}
};
foreach (var point in Points)
{
distanceToTarget.Add(point, Tools2D.Distance(point, Target));
RelPos2D position = Tools2D.RelativePosition(point, Target);
relativePosition.Add(point, position);
countPerPosition[position]++;
}
//check countPerPosition to see if there are solutions
int pointsCount = Points.Length;
bool noSolutions = false;
foreach (var key in countPerPosition.Keys)
{
if (countPerPosition[key] == pointsCount)
{
noSolutions = true;
break;
}
}
noSolutions = noSolutions ||
countPerPosition[RelPos2D.ll] + countPerPosition[RelPos2D.le] + countPerPosition[RelPos2D.lg] == pointsCount ||
countPerPosition[RelPos2D.lg] + countPerPosition[RelPos2D.eg] + countPerPosition[RelPos2D.gg] == pointsCount ||
countPerPosition[RelPos2D.gg] + countPerPosition[RelPos2D.ge] + countPerPosition[RelPos2D.gl] == pointsCount ||
countPerPosition[RelPos2D.ll] + countPerPosition[RelPos2D.el] + countPerPosition[RelPos2D.gl] == pointsCount;
if (noSolutions)
throw new Exception("No solutions.");
var orderedPoints = Points.OrderBy(point => distanceToTarget[point]);
bool found = false;
Point2D
Point1 = null,
Point2 = null,
Point3 = null;
RelPos2D PosPoint1,
PosPoint2,
PosPoint3;
foreach (var point1 in orderedPoints)
{
Point1 = point1;
PosPoint1 = relativePosition[Point1];
var point2Candidates = orderedPoints.Where(p => p != Point1)
.OrderBy(p => distanceToTarget[p]);
//this should not happen because we know that we have at least one solution
if (point2Candidates.Count() == 0)
continue;
foreach (var point2 in point2Candidates)
{
Point2 = point2;
PosPoint2 = relativePosition[Point2];
var point3ValidPositions = ValidPositions(PosPoint1, PosPoint2);
var point3Candidates = orderedPoints.Where(p => p != Point1 && p != Point2 && point3ValidPositions.Contains(relativePosition[p]))
.OrderBy(p => distanceToTarget[p]);
if (point3Candidates.Count() == 0)
continue;
foreach (var point3 in point3Candidates)
{
Point3 = point3;
PosPoint3 = relativePosition[Point3];
//check if already visited
//hash subject to conflicts
var hash = Point1.GetHashCode() *
Point2.GetHashCode() *
Point3.GetHashCode();
if (visited.Contains(hash))
continue;
if (UncertainSolution(PosPoint1, PosPoint2, PosPoint3))
{
found = Tools2D.TriangleContainsPoint(Point1, Point2, Point3, Target);
}
else
{
found = true;
}
if (found)
break;
visited.Add(hash);
}
if (found)
break;
}
if (found)
break;
}
if (found)
return new Tuple<Point2D, Point2D, Point2D>(Point1, Point2, Point3);
throw new Exception("No solutions.");
}
}
class Program
{
static void Main(string[] args)
{
ProblemSolver ps = new ProblemSolver();
Random r = new Random();
List<Point2D> points = new List<Point2D>();
Point2D target = new Point2D()
{
//X = r.NextDouble() * 10,
//Y = r.NextDouble() * 10
X = r.Next(11),
Y = r.Next(11)
};
for (int i = 0; i < 10; i++)
points.Add(new Point2D()
{
//X = r.NextDouble() * 10,
//Y = r.NextDouble() * 10
X = r.Next(11),
Y = r.Next(11)
});
Console.WriteLine("Target: {0}X: {1}{0}Y: {2}{0}", Environment.NewLine, target.X, target.Y);
Stopwatch sw = new Stopwatch();
sw.Start();
try
{
var solution = ps.Solve(target, points.ToArray());
Console.WriteLine("Solution: {0}X1: {1}{0}Y1: {2}{0}X2: {3}{0}Y2: {4}{0}X3: {5}{0}Y3: {6}{0}",
Environment.NewLine,
solution.Item1.X,
solution.Item1.Y,
solution.Item2.X,
solution.Item2.Y,
solution.Item3.X,
solution.Item3.Y
);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
sw.Stop();
Console.WriteLine("Solved in {0} ms", sw.ElapsedMilliseconds);
Console.ReadLine();
}
答案 2 :(得分:0)
您可以使用bowyer-watson算法并对其进行修改以查找红点。