给定平面上的两条线,如何找到最接近其交点的整数点?

时间:2010-04-25 14:50:28

标签: algorithm math

我无法解决它:

您将获得8个整数:

  • A,B,C表示平面上的直线,其中等式A x + B y = C
  • a,b,c代表另一条线
  • x,y表示平面上的点

两条线不平行,因此将平面分成4块。 点(x,y)位于这些部件的内部。

问题:
编写一个快速算法,找到与(x,y)相同的整数坐标的点,该点最接近两条给定线的交叉点。

注意:
这不是作业,这是旧的Euler类型的任务,我完全不知道如何处理。

更新 您可以假设输入上的8个数字是32位有符号整数。 但你不能假设解决方案是32位。

更新2: 困难的情况 - 线条几乎平行 - 是问题的核心

更新3: 该问题的作者声明解决方案是线性O(n)算法。其中n是输入的大小(以位为单位)。即:n = log(A)+ log(B)+ ... + log(y)
但我仍然无法解决它。

请说明已发布算法的复杂性(即使它们是指数级的)。

18 个答案:

答案 0 :(得分:9)

alt text http://imagebin.ca/img/yhFOHb.png

<强> Diagram

找到第L1:Ax+By=C行和L2:ax+by=c的交叉点后,即点A(x1,y1)

定义另外两行与y = ceil(y1)平行的y = floor(y1)X-axis,找到与L1L2的交点,即点B(x2,y2)和{ {1}}。

然后指出您需要的是C(x3,y3)D,更接近点E。类似的程序适用于飞机的其他部分。

A

答案 1 :(得分:7)

此问题属于整数凸优化类别。

这里介绍的是解决问题的数学方法。我不指望你真正使用它 - 需要很多复杂的技术,而其他算法方法(例如“搜索”适当的点)可能会很好。然而,人们对“真实”的解决方案表达了兴趣,所以在这里。

可以分三个阶段解决:

  1. 首先,确定答案所在的每一行的哪一侧,如TheMachineCharmer的回答所示。
  2. 一旦知道,问题就可以重写为凸优化问题(详见Wikipedia)。要优化的函数是最小化(x-x0)^ 2 +(y-y0)^ 2,其中x0和y0是两条线的交点的坐标。这两条线各自成为线性不等式,例如“x + y&gt; = 0”,一起形成凸区域,答案可以在中找到。我会注意到解决方案将是(x = x0,y = y0) - 你需要从这个阶段表达一种表达方式问题,与simplex method的可行表格相似。
  3. 第三,通过重复添加cuts来进一步约束可行区域,直到凸优化问题的解是积分的,可以找到整数解。在一般情况下,这个阶段可能需要进行大量的迭代,但是考虑到所呈现的问题,特别是它的2D性质,我相信它最多可以通过两次削减来解决。

答案 2 :(得分:5)

我在这里展示了如何解决这个问题的“困难”实例。我认为这种方法可以推广。我在原帖的评论中加入了另一个更简单的例子。

考虑两行:

10000019 * X - 10000015 * Y + 909093 >= 0    (L1)
-10000022 * X + 10000018 * Y + 1428574 >= 0  (L2)
A = 10000019, B = -10000015, C = -909093

交点是H:

Hx = -5844176948071/3, Hy = -5844179285738/3

对于点M(X,Y),平方距离HM ^ 2是:

HM^2 = (9*X^2+35065061688426*X
    +68308835724213587680825685
    +9*Y^2+35065075714428*Y)/9

g = gcd(A,B)= 1:L1 A*X+B*Y+909093的等式 可以取任何整数值。

Bezout系数,U = 2500004和V = 2500005验证:

A * U + B * V = 1

我们现在用以下定义的“双重”基础(K,T)重写问题:

X = T*U - K*B
Y = T*V + K*A

替换后,我们得到:

T+909093 >= 0
2*T+12*K+1428574 >= 0
minimize 112500405000369*T^2
   +900003150002790*T*K
   +1800006120005274*K^2
   +175325659092760325844*T
   +701302566240903900522*K
   +Constant

进一步翻译后(首先是T,然后是K,以最小化 在第二个等式中常数),T = T1-909093,K = K1 + 32468:

T1 >= 0
2*T1+4+12*K1 >= 0
minimize 112500405000369*T1^2
    +300001050000930*T1
    +900003150002790*T1*K1
    +1200004080003516*K1
    +1800006120005274*K1^2
    +Constant

我提出的算法是在T1上循环。实际上,我们不需要 这里循环,因为最好的结果是由T1 = K1 = 0给出,对应于:

X = -1948055649352, Y = -1948056428573

我在下面的帖子。

这是算法的另一个想法。它可能有用,但我没有实现它......

通过适当改变符号以匹配(x,y)的位置,可以写出问题:

A*X+B*Y>=C  (line D)
a*X+b*Y>=c  (line d)
minimize the distance between M(X,Y) and H, the intersection point
A*b != a*B (intersection is defined)
A,B,C,a,b,c,X,Y all integers

(A X + B Y)达到的所有值的集合是g = gcd(A,B)的所有倍数的集合,并且存在整数(u,v)等A u + B v = g(Bezout定理)。从具有整数坐标(X0,Y0)的点开始,具有整数坐标且A X + B Y的相同值的所有点都是(X0-K B / g,Y0 + K < / em> A / g),对于所有整数K。

为了解决这个问题,我们可以在距离H增加的距离上循环与D平行的线,并且包含具有整数坐标的点。

  1. 计算g,u,v和H(可能不需要H的坐标,我们只需要与距离对应的二次形式的系数)。

  2. T0 = ceil(C / g)

  3. 从T = T0循环

    一个。找到K最小(或最大,取决于 Bb A的符号)整数,验证a *(T uK B / g)+ b *(T v +ķ A / G)&GT; = C

    湾保持点(T u-K B / g,T v + K A / g)如果接近H

    ℃。当(T-T0)对应于D的距离大于目前为止的最佳结果时退出循环,否则继续T + = 1

答案 3 :(得分:4)

我过去一直在研究这个问题(因为它很有趣,因为我在我工作的地方碰到了相关的东西)。

据我所知,这个问题没有高效的(FPTIME)算法。

唯一已知的(对我而言)解决方案是基本上枚举整数坐标(从交叉点附近开始),直到找到所需的整数坐标。当两条线之间的角度非常小时,这当然不是有效的。你可以做一些修剪来提高效率,当坡度很小时,效率也不错。

已知这种(ILP - 整数线性规划)的推广是NP完全的。

答案 4 :(得分:4)

我越是想到这一点,它似乎变成了整数线性规划,它在一般情况下是NP完全的。 http://en.wikipedia.org/wiki/Linear_programming#Integer_unknowns

我的推理开始就像TheMachineCharmer的答案,直到我达到这一点。问题在于,只有当截面与垂直轴或水平轴通过交叉点对齐时,沿着交叉点的单元/底板检查线的方法才有效。更可能的是,薄部分将以一定角度远离轴倾斜,并且细胞/地板邻居不会在整数坐标上与该部分相交。

在那一点上,我们正在寻找一些自然单位向量的整数组合,它们满足定义我们所选部分的不等式,并最小化到交点的距离。对我来说,这似乎是一个整数线性规划问题。

有一些特殊情况的整数线性编程比NP-hard更容易,这个问题可能很容易成为其中之一,因为它似乎比一般线性编程情况更受约束。维基百科的文章链接到一些方法,但这超出了我的数学水平来应用。

答案 5 :(得分:1)

正如其他一些人所指出的,这是整数线性规划中的问题(又称线性丢番图不等式)。

查看此参考:ABS Algorithm For Solving a Class Of Linear Diophantine Inequalities and Integer LP Problems。作者声称能够解决像

这样的系统 对于Ax≤b,x∈Z n

max(c T x),其中c∈Z n ,b∈Z< sup> m ,A∈Z m,n ,m≤n。

特别是,设置m = 2,n = 2,我们得到了找到

的问题 对于Ax≤b,x∈Z 2

max(c T x),其中c∈Z 2 ,b∈Z< sup> 2 ,A∈Z 2,2

这里,A是2x2矩阵,b,c和x是2x1列向量。

OP所述的问题可以这种方式重申(如果被问到,我会尝试更详细地说明这一点)。

本文中提出的矩阵算法看起来可能看起来没毛,但矩阵算法就是这样。并不是说我一行一行地了解它,或者理解它,但与我见过的一些东西相比它看起来相当温和。

这似乎是ABS methods的一般类中的某些内容,它似乎在几个问题域中获得了关注。

本文第2部分的最后一句也提到了另一种解决方法。

@Alan指出,虽然一般的ILP问题是NP-Hard,但这里所述的问题可能不是。我不确定为什么会这样,但可能是因为矩阵A是2x2(而不是nx2),因为约束可以表示为整数。

Edit1 :算法的复杂性似乎是O(1)(看起来是O(d),其中d是点阵的维数。在这种情况下,d = 2 )。我对此感到惊讶的是O(!!)并且理解和实现它仍然是O(??),尽管我现在经历了几次并且它看起来比我想象的更直接。

答案 6 :(得分:0)

实际上,可以通过修改Bresenham的线条绘制算法来解决这个问题。 它通常用于扫描线的转换,如果你知道线的终点,只需要在循环内增加一些步骤。

一旦找出了该点所在的扇区,将原点移动到交叉点,记录非整数误差。计算从交点到底线的线的斜率,然后在整数x值(如果斜率很小)或从y的法线(斜率是高)处垂直到水平线并找到它与另一个轴相交一个整数点。

您应该能够检查一个轴上的每个整数步长,以确定您正在测试的点是在两条线之上还是两条线之间(从交叉点向该点创建一个新向量并确定斜率)。如果该点高于增量您的整数步长。因为你正在测试它应该是O(n)的一条线的最小梯度差异。在Bresenhams算法中,它们是8个扇区而不仅仅是4个。

答案 7 :(得分:0)

这是一个可能有助于获得完整解决方案的部分想法。想象一下,这两条线非常非常接近。那么它们之间的任何整体解决方案也将是非常接近每条线的整体点。让我们尝试找到行ax+by=c的紧密积分点。自y = (c - ax)/b起,我们需要y非常接近整数,因此b大约将c-ax除以。换句话说,c-ax+D == 0 mod b表示一个小整数D

我们可以为c-ax+D == 0求解b mod xx = a^-1(c+D) mod b(如果a和b相对为素数,则存在^ -1不确定这是否是这种情况。)

因此算法是为{= 1}} mod x = a^-1(c+D)评估D = 0,+ 1,-1,+ 2,-2,...并尝试生成的x以查看它们是否有效。如果两条线的交点有紧密的积分点,它们应该在这次迭代的早期出现。当然,在最坏的情况下你可能必须达到b ......

答案 8 :(得分:0)

你们是错过了这一点!哈哈,对不起,无法抗拒。

嘿,让我们想象一个稍微简单的情况。

你有一条线从原点发出,与x轴形成一个小于90度的角度。找到最近的整数点。

直到你点击我们想要的象限中的格子点时才搜索格点的问题是,人们不知道要搜索多远。在非常非常锐角的情况下,在我们击中我们地区的一个之前,我们可以考虑多个点。

解决方案:

解决:(Slope of Line) * Delta(x) = 1

即。 Delta(x) = 1/(Slope of Line),是我们开始搜索的地方。受约束Delta(x) > 1

约束

换句话说,我们走得足够远,以至于x和y坐标之间必须存在至少一个整数差异。

在我们的问题中,我们必须适当地转换并调整数字以给出适当的误差范围。 Delta(x) >= 2Delta(x) = 2/(Slope of Line)我认为它会脱离我的头脑,但我没有铅笔。

没有

答案 9 :(得分:-1)

嗯,这取决于被认为足够快的东西。

让我们将点命名为[x,y] P.此外,我将使用整数坐标'整数点'来调用点。

算法我建议:

  1. 找到这两条线相交的点Q. (Q = [x_q,y_q])

  2. 获取Q和P之间的直线函数,y = f(x)或反x = g(y);

  3. 根据角度确定QP是垂直还是水平。让我们说它是垂直的,以简化以下解决方案(如果它是水平的,轴将简单地反转,我写x的位置是y,反之亦然)。

  4. 取第一个整数坐标y_1,我们沿着从Q到P的线。

  5. 计算该点的第二个坐标:x_1 = f(y_1)。这一点在我们的细分中。

  6. 查找坐标为[floor(x_1); y_1]和[floor(x_1 + 1); y1]的周围整数点是否在我们感兴趣的细分中。

  7. 6.1如果是,那么我们遍历水平线x_3 = f(y_1)以找到仍然在我们的片段中的整数点并且具有(x_3-x_q) - &gt;分钟。这一点是我们的答案。

    6.2如果不是,则将y_1增加1并从步骤5开始重复。

答案 10 :(得分:-1)

我怀疑这是一个数学优化问题,可以用拉格朗日乘数来解决......

答案 11 :(得分:-1)

这是一个线性时间(即O(A,B,C等的#位),假设位符合O(1)字的存储器)解决方案使用线路侧测试和二分搜索:< / p>

假设w.l.o.g. B!= 0(否则我们将A与a交换,B与b交换,C与c交换)。执行线侧测试以查看该点所在的线(A,B,C)的哪一侧。假设w.l.o.g.该点在线下方(或在线上)。

注意,对于任意x坐标x',我们可以计算最小y',使得(x',y')在O(1)时间内通过y'在线(A,B,C)之上=(C - A * x')/ B.

现在,假设w.l.o.g.输入点(x,y)位于(a,b,c)的右侧,或者在水平线的情况下位于下方。然后,我们可以相对于线(a,b,c)执行(x',y')的线侧测试,并确定是否需要增加x'或减少x'以找到最小x',使得(x) ',y')落在(a,b,c)的正确一边。

二进制搜索此点最多需要O(w)时间,其中w是A,B等中的位数。请注意,因为输入坐标x和y各自都是整数,所以返回值也是如此。即使x和y不一定在这些界限内并且线几乎平行,在O(w)时间内也会找到合适的x,因为斜率的差异是A / B-a / b =(Ab-aB)/ Bb <= 1/2 ^(2w),因此答案的x坐标将适合存储器的O(1)个字。我们仍然需要找到答案的y坐标,也可以通过二分搜索找到答案。

答案 12 :(得分:-1)

检查点是否是数学锥的一部分的问题相当简单。给定2个向量, v w ,由( v w )定义的圆锥中的任何点都将是在表格上:z = a *** v ** + b *** w **,其中a,b> = 0.注意,为此,您必须将Origo移动到2的交叉点线。由于我们不能假设交叉点的有限精度,您将不得不进行浮点数学运算并确定某些东西是否足够接近您想要的东西。

  1. 找到定义4个圆锥的矢量(其中有无限多个,每个圆锥需要2个),由2条线定义。
  2. 找出哪个圆锥包含我们的点,将该圆锥称为 C
  3. 取2个定义 C 的矢量,找到中间矢量(在2个相同的锥体中分割 C 的矢量),称之为 即可。
  4. 现在是时候启动循环了。为简单起见,我将假设我们将自己限制为x和y轴上的n位。请注意,对于 m 的长度,您需要一个大于n位的整数。现在沿着 m 的长度进行二元搜索,每次检查2个环(我怀疑1个环就足够了)。当您找到包含点 C 的最小长度时,请检查哪些点最接近。
  5. 最坏的情况是O(log(sqrt(2 * n ^ 2)),其中n是我们用来表示x和y轴的长度。

    如果您不知道 * m 的长度,可以进行“反向二分搜索”。只需保持你出去的长度加倍,直到你在 C 中找到分数。然后你知道 m 上有2个点,你可以在它们之间进行二元搜索。

    所有这些的主要问题是精确度,所以请记住这一点。另一种追求的方法可能包括组成锥形的2个半平面。上面的每个圆锥都是由2个半平面的交点定义的,检查点是否是半平面的成员可能很简单,我不确定。

    编辑:对于半平面来说确实更容易,2条线将R ^ 2分成2个半平面,这给出4个组合,即4个圆锥。所以每次你想要检查一个点是否是锥体的一个成员时,你必须检查它是否是构成该特定锥体的2个半平面的成员。这里解释了如何做到这一点:

    http://www.mathsteacher.com.au/year9/ch04_linear_graphs/07_half/planes.htm

    并且比移动Origo更容易并且精确地摆弄。取代检查成员资格的方法并保持其他所有内容相同,你就会达到同样的增长。

答案 13 :(得分:-1)

当我必须找到标记多边形的点时,我正在做类似的事情。

最终结果是在Autocad中的奔腾3上在5秒内有70000个多边形。如果您排除Autocad,那么大约需要3秒。

首先,您需要找到一个交叉点。 接下来你必须找到你的点(x,y)所在的位置,并在其中绘制一条水平线或垂直线,以便你的2条线(A,B,C)和(a,b,c)和一个新的水平/ verical线形成一个三角形。 如何查找它是垂直还是水平线: 通过(x,y)点绘制水平和垂直线,然后检查: - 水平:      - 如果线A,B,C和你的水平线和线a,b,c的交点使这个方程起作用(与A,B,C的交点).x&lt; x&lt; (与a,b,c交叉).x,然后你知道你的内心。 (你可以切换A,B,C和a,b,c,就像x在里面一样长。      - 类似于y,只检查y而不是x。

所以现在你有一个三角形,你知道它在哪里(左,右,上,下)。 例如,如果它是一个直角三角形(如上图所示)。你取交点的x,你就把它给它(如果它在左边你就是它的底部)。如果你有上/下三角形,你的y坐标类似。 然后你通过它绘制一条扫描线,它通过你的(x,y)点到你的扫描线上并且检查你是否在交叉点内有一个点(类似于x

当你找到一个点时,它可能不是最接近的点。因此,您必须在最后一个不好的点(当您增加步骤时)和最后一个点(您找到一个整数)之间进行二分法。

答案 14 :(得分:-1)

我认为这有3件。

  1. 计算两条线的交点,并保持该点的X和Y坐标

  2. 找到给定点所在的部分。这应该很容易,因为你有2条线的斜率,以及由给定点和交点创建的线的斜率。称他们为m_line1m_line2m_intersect。如果m_intersect有一个公式可以找出使用这些值的部分以及给定点的位置。

  3. 找到最接近的整数。一旦你知道上面#1的值和#2的斜率,你就可以直接计算出这个值。你可以蛮力,但有一个优雅的数学解决方案。
  4. 这是我采取的步骤,至少。

    已更新以添加更多内容

    好的,我将开始讨论#2。

    如果计算给定点和交点的斜率,则到达m_intersection。这是穿过交叉点的线的斜率。假设m_line1是2个斜率中的较大者,因此当交叉点之后x增加时,line1在line2上方。它可以更容易地考虑节名称。我们将第1行和第2行之间的条子给出的部分称为 A ,其中x大于交叉点坐标x,然后我们将顺时针命名其他3个部分,以便 A C 彼此相对。

    如果m_intersection介于m_line1m_lin2之间,那么它必须位于以下两个部分之一 A C 。哪个部分是对交叉点x坐标的x坐标值的简单测试。我们将 A 定义为具有更高价值的部分。如果斜率在m_line1m_line2之外,则可以进行类似的计算。

    这将为您提供您的观点所在的部分。您所做的只是计算1个交点(5次乘法,2次除法和少量减法,如果按传统方式进行),3个斜率,然后进行几个整数比较

    编辑#3 - 返回(非)热门需求!

    所以这就是我如何计算#3,即到交点的最近整数点。它可能不是最好的,但它使用二分搜索,因此它是O(log n),其中n与线斜率差的倒数相关。它们越接近,n越大。

    首先,取两条线的斜率之间的差异。说它是1/8。这意味着从交点开始,在确保两条线之间的y轴上存在整数(它可能位于其中一条线上)之前,必须沿x轴出8个单位。现在,如果交叉点本身不在整数x坐标上,那么你需要进一步走出去保证你的起点在整数x坐标上,但它是有界的。如果交点在x = 1.2,那么在上面的例子中,最坏的情况是从x = 41开始,然后沿y轴向下移动约5个单位。选择你得到的y值的ceil或floor。这不是非常关键。

    现在您有一个起点,最近的点可以通过二分搜索来近似。您的新线段位于交点和起点之间,您的移动单位是该线段斜率的倍数。计算线段的中点,看它是否位于两条线之间。如果它不是直接命中,则向其中加1或减1,如果其中任何一个命中,则将剩余距离减半并再次执行。否则搜索该段的另一半。

    如果您没有斜率差异&lt; 1,我认为问题可能更简单(强行交叉周围的空间)。但这只是上面搜索的一个特例,你不需要走出那么远就能找到一个起点。

答案 15 :(得分:-2)

我的建议是这样的。假设包含我们的目标点的平面的截面完全在左下象限中,从两条线的交叉点看(其他象限是类似的,并且当平面的截面跨越多于一个象限时将稍后考虑)。

让两个给定的行为l 1 且l 2 (l 1 比l 2 <更浅'< /子>)

  1. 找到X =(a,b),l 1 和l 2 的交叉点。

  2. 让k = 0

  3. 让v k 为垂直线,x坐标x k = floor(a-k)

  4. 找到v k 的交叉点,其中l 1 且l 2 (点V 1 =(x 1 ,y 1 ),V 2 =(x 2 ,y 2 < /子>))。

  5. 如果楼层(y 1 )!=楼层(y 2 ),目标点为(x 1 ,楼层( y 1 ))END。

  6. 如果楼层(y 1 )== floor(y 2 ),则递增k并转到步骤3.

  7. 由于l 1 且l 2 不平行,因此abs(y 1 - y 2 )必须与k一起成长。当abs(y 1 - y 2 )大于1时,算法肯定会停止(尽管可能会提前停止)。

    现在让我们考虑一下(简单)情况,当我们的平面截面跨越多个象限时,从两条线的交叉点看(它可能跨越两个或三个象限)。

    1. 找到X =(a,b),l 1 和l 2 的交叉点。

    2. 找到A,这是一组四个最靠近X的点,它们具有整数坐标

    3. 找到B,A中的一组点,位于飞机的目标部分。

    4. 从B点开始最接近l 1 的交叉点,l 2 是目标点

    5. (这种情况在恒定时间内运行。)

答案 16 :(得分:-2)

第1行定义为y1 = m1 * x1 + b1。 第2行定义为y2 = m2 * x2 + b2。

m1,m2,b1,b2都是已知值[常数]。

确保m1&lt;&gt;平方米。

找到交点,即y1 == y2和x1 == x2,定义为(X,Y)。

Y =((m1 * b2)/ m2)/(m1 / m2-1)

X =(Y-b1)/ m1

可以通过将X和Y四舍五入到最接近的整数来找到最近的点。你决定如何处理.5

答案 17 :(得分:-2)

在这四个平面中,一个位于两条线的左侧,一条位于两条线的右侧,一条位于另一条线的右侧,另一条线位于另一条线的左侧,最后一条是在另一条线的左侧和右侧。你是否更容易看出它。

点的相对位置取决于该行列式的结果:

[1 p1x p1y; 1 p2x p2y; 1 p3x p3y],其中p1和p2是线中的两个任意点,p3是给定点。

如果它等于零,则该点在该行中,如果它大于零,则它在一侧,侧面取决于行中p1和p2的相对位置以及您认为左右的点飞机。

问题是在两行中选择两个遵循相同标准的点,因此结果是一致的,也许p1总是具有比p2更低的x坐标值(如果线是垂直的,则为y坐标)。

如果每条线都有两个点,那么计算两个决定因素就完成了。

修改

Ups,这部分地解决了这个问题。无论如何,您可以计算XY点所在的边,计算交点,然后计算所有有效点的相对位置(floor(x),floor(y)),(floor(x),ciel(y)) ),...