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

31
我无法解决它:
给你8个整数:
A,B,C表示平面上的一条线,其方程为Ax + By = 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)
但我仍然无法解决它。
请说明已发布算法的复杂度(即使它们是指数级的)。

5
这不是来自欧拉计划(Project Euler)的问题,而是类似欧拉问题(Euler-type problem)。 - Łukasz Lew
2
@Pavel:我的解决方案需要计算每条线的两个任意点和两个3x3行列式,这为什么会慢? - XenF
4
很遗憾没有好的答案。大多数编程问题会迅速得到许多答案,而且大多数都是正确的。我不知道为什么算法问题不能得到同样的待遇。至少如果人们不发帖,除非他们有一个真正的解决方案... - IVlad
2
以下是一个可能比较复杂的案例: 第一行 10000019 X - 10000015 Y + 909093 = 0。 第二行 -10000022 X + 10000018 Y + 1428574 = 0。 点 (x,y) 是坐标原点。 - Eric Bainville
3
在相同的精神上,这里有另一个更简单的例子:100003 X - 99999 Y + 9092 = 0 ; -100006 X + 100002 Y + 14286 = 0 ; (x,y) = (0,0)。在这个例子中,解似乎是 (-194800325,-194808117)。 - Eric Bainville
显示剩余16条评论
18个回答

9

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

图表

当你找到直线L1: Ax+By=CL2: ax+by=c的交点,即点A(x1,y1)之后。

定义两条平行于X轴的直线y = ceil(y1)y = floor(y1),并找到它们与L1L2的交点,即点B(x2,y2)C(x3,y3)

然后所需的点是DE,取决于哪个更靠近点A。类似的过程适用于平面的其他部分。

D ( ceil(x2), ceil(y1)  )
E ( ceil(x3), floor(y1) )

1
如果D和E都不在X所在的段中怎么办?如果在您提出的任何迭代的下一步中,这些点也不在段内怎么办? - P Shved
2
这些是最靠近交点的四个整数点。使这个问题具有挑战性的情况是当这四个点中没有一个在指定的区域内时。当两条线非常接近平行,以非常小的角度相遇时,就会发生这种情况。解决方案可能距离交点数百万个单位。 - Alan
1
@Pavel Shved,分段?我在图表中显示了分段,但是我的意思是线条。它们延伸到无限远。 - Pratik Deoghare
1
如果两条初始线几乎平行,并且不包含计算交点附近的任何整数点,该怎么办?想象一下图表上的“ax + by = c”线路只是在 D 上方。 - fgb
8
该方法“显而易见”的意思是这是首先想到的事情。但它很快被摒弃,因为它对于当两条线的斜率具有相同符号时没有提供任何解决方案。正是问题所在。否则,那就太简单了。 - AnT stands with Russia
显示剩余6条评论

7
这个问题属于整数凸优化范畴。
这里提供了一种数学方法来解决这个问题。我并不希望你真的使用它——需要很多复杂的技巧,而其他算法方法(例如“搜索”适当的点)可能也能很好地完成。然而,对“真正”的解决方案表达了兴趣,所以在这里给出了它。
它可以分为三个阶段解决:
  1. 首先,确定答案在每条线的哪一侧,正如TheMachineCharmer的回答所示。
  2. 一旦知道了这一点,问题可以被重写为一个凸优化问题(详见维基百科)。要优化的函数是最小化(x-x0)^2 + (y-y0)^2,其中x0和y0是两条直线相交的坐标。两条直线都成为一个线性不等式,例如“x+y>=0”,共同形成了答案所在的凸区域。我将注意到,解决方案将是(x=x0,y=y0)——你需要从这个阶段中得到表达问题的方式,类似于单纯形法的可行表格。
  3. 第三步,可以通过不断添加割线来进一步限制可行区域,直到凸优化问题的解变为整数解。在一般情况下,这个阶段可能需要很多次迭代,但是看看所提出的问题,特别是它的二维性质,我相信最多只需要两个割线就能解决。

1
说整数凸优化问题是广义问题就相当于说它是搜索问题,这太宽泛了。 - Łukasz Lew
如果有人能够指出更具体的类别,我将很高兴地进行编辑。它肯定不是像许多人声称的那样的整数线性规划(ILP),所以我想要清楚明确。此外,请注意,凸优化问题要求目标函数是凸的(即对于给定的z,使得f(x,y) <= z的点构成一个凸集),而不仅仅是可行域,因此它可能没有你想象的那么广泛。 - Eric Burnett
“添加一些切口”这部分真的非常模糊。当然,也许你可以通过某种方式添加两个切口来找到解决方案,但是,如果是这样,找到这些切口的规格就变成了与原问题等价的问题,而你并没有真正说出任何有意义的东西。 - Don Hatch

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),其到点H的距离的平方是:

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 可以取任何整数值。

贝祖定理系数 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

(AX+BY)所达到的所有值的集合是所有g=gcd(A,B)的倍数集合,并且存在整数(u,v),使得Au+Bv=g(贝祖定理)。从具有整数坐标(X0,Y0)的点开始,所有具有相同值的AX+BY的整数坐标点为(X0-KB/g,Y0+KA/g),其中K为所有整数。

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

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

  2. T0 = ceil(C/g)

  3. 从T = T0开始循环

    a. 找到最小(或最大,取决于aB-bA的符号)的整数K,满足a*(Tu-KB/g)+b*(Tv+KA/g)>=c

    b. 如果更靠近H,请保留点(Tu-KB/g,Tv+KA/g)

    c. 当(T-T0)对应于距离D的最佳结果时退出循环,否则继续使用T+=1


"Cst" 是指常量吗?如果是的话,最好直接写成 "constant"。 - brainjam
太棒了。你能解释一下“双”基础技巧吗?你实现了什么?这是获得K1和K2的最终方程,使用相当温顺的系数吗?这是一个众所周知的技术,在哪个领域中使用?在一般情况下,你是否仍然认为需要循环?哦,你用什么工具来处理你的方程和大数字? - brainjam
“双重”公式通常将未知数表示为约束条件的线性组合。在这里,即将(X,Y)表示为(A,B,C)和(a,b,c)的组合。由于它们是整数,因此最好使用这种“伪双重”。请注意,对于Z基和格子的常规用户来说,所有这些东西可能都是微不足道的(我不知道)。 - Eric Bainville
关于循环的必要性,我不知道。消除二次形式中的K*T项可能是有用的。在所有情况下,当迭代T和K时,我们只需要更新形式的值,这比完全计算它更快(考虑圆形光栅化)。 - Eric Bainville

4

我过去曾经研究过这个问题(因为它很有趣,而且在我工作的某个地方也遇到了相关的问题)。

据我所知,目前没有高效的(FPTIME)算法可以解决这个问题。

唯一已知的(对我来说)解决方法是基本上枚举整数坐标(从交点附近开始),直到找到你想要的那个。当两条线之间的角度非常小的时候,这显然不是很有效率。你可以进行一些修剪以提高效率,并且当斜率较小时,效率还不错。

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


正如我在艾伦的回答中所指出的,这个问题并不是线性的(最小化 x^2 + y^2),因此它不是严格的 ILP,而是整数凸优化。但同样它也是 NP-完全的,因为 ILP 是一个特殊情况。 - Eric Burnett
5
泛化问题是NP完全的事实并不意味着原始问题也是NP完全的。 - Łukasz Lew
@Eric,你说得对,关于非线性的问题(但也许可以做些什么来适应这个问题并仍然获得ILP)。 - user51568
这里有答案(特别是@Eric Bainville的答案),表明问题根本不是NP完全问题。甚至没有人排除O(1)的可能性。 - brainjam

4
我越想,就越觉得这变成了整数线性规划问题,一般情况下是NP完全的。http://en.wikipedia.org/wiki/Linear_programming#Integer_unknowns 我的思路开始和TheMachineCharmer的答案一样,直到那个点。问题在于,如果该部分与交点的垂直或水平轴对齐,则沿着交点上方/下方的线的方法才有效。更可能的是,细部分将倾斜于某个角度,远离轴,天花板/地板相邻点不会在整数坐标上与该部分相交。
此时,我们正在寻找一些自然单位向量的整数组合,满足定义所选部分的不等式,并最小化到交点的距离。对我来说,这似乎是一个整数线性规划问题。
有一些整数线性规划的特殊情况比NP难度低,这个问题很容易成为其中之一,因为它似乎比一般的线性规划情况更受限制。维基百科文章链接了一些方法,但那超出了我的数学水平。

这并不完全是整数线性规划(ILP);寻找最近点需要将 x^2 + y^2 最小化,而这不是一个线性方程。但它是凸的,因此属于凸优化的一般类别。我没有仔细研究过,但我敢打赌,通过添加约束来解决 ILP 的技术也可以推广到解决整数凸优化问题。 - Eric Burnett
关于问题的非线性,确实如此。我曾经认为曼哈顿距离是足够的近似,但是再想一想,显然不对。 - Alan
无论如何,它会为您提供x和y距离的上限。 - Eric Burnett
生成线性目标函数很容易。我们可以轻松地找到一候选点,使得这些线之间的距离为1,因此任何更接近的整数点必须具有不同的x值(假设我们的候选区域是水平的)。因此,x是一个很好的目标函数。(对于垂直的区域,请使用y代替。) - Keith Randall

1

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

请查看此参考文献:ABS Algorithm For Solving a Class Of Linear Diophantine Inequalities and Integer LP Problems。作者声称能够解决以下系统:

max(cTx) for Ax≤b, x∈Zn, where c∈Zn, b∈Zm, A∈Zm,n, m≤n。

特别地,当m=2,n=2时,我们得到了寻找以下问题的答案:

max(cTx) for Ax ≤ b, x∈Z2, where c∈Z2, b∈Z2, A∈Z2,2

在这里,A是一个2x2的矩阵,而b、c和x是2x1的列向量。

如果需要,可以这样重新陈述OP提出的问题(我会尽力详细解释)。

论文中提出的矩阵算法对于未接触过矩阵算法的人来说可能看起来很复杂,但是矩阵算法就是这样。虽然我没有逐行查看或理解它,但与我见过的一些东西相比,它看起来相当温和。

这似乎是ABS方法的一般类别,该方法在几个问题领域中似乎越来越受欢迎。

论文第2节的最后一句话还提到了另一种解决方法。

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

编辑1:该算法的复杂度似乎为O(1)(它似乎是O(d),其中d是晶格的维数。在这种情况下,d=2)。我对此感到非常惊讶,理解和实现仍然是O(??),尽管我已经多次研究过它,但现在看起来比我想象的要简单。


问题陈述(尽管在你编写答案时可能没有)n 是输入中的位数。一个正确的算法通常必须读取所有位,因此它至少需要O(n),对吗? - Don Hatch
@Don,我不是O(?)理论的专家,但我认为读取输入在分类中并不重要。 - brainjam
无论您认为O(n)是读取输入的成本,还是在某个稍后时间点查看每个位的成本,都不重要;事实上,它确实需要读取所有位,即所有位都很重要,这意味着其中有一个O(n)。这与已知排序列表中的二进制搜索不同,在这种情况下,并非所有位都很重要,从而允许使用次线性时间算法。 - Don Hatch

0
你们都没抓住重点!哈哈,抱歉,忍不住要说一句。

嘿,我们来想象一个稍微简单一点的情景。

从原点开始有一条线与x轴形成小于90度的夹角。找到最近的整数点。

仅仅搜索格点直到找到符合我们要求的象限内的点存在的问题在于,我们不知道该搜索到多远。对于一个非常锐角的情况,可能需要搜索无数个点才能找到一个在我们区域内的点。

解决方法:

求解:(线的斜率) * Δ(x) = 1.

Δ(x) = 1/(线的斜率),这是我们开始搜索的地方。满足约束条件 Δ(x) > 1

换句话说,我们只需走出足够远的距离,确保x和y坐标之间至少有一个整数差异。

在我们的问题中,我们需要适当地进行转换并调整数字以给出适当的误差范围。Delta(x) >= 2Delta(x) = 2/(线斜率)。我认为这将从我的脑海中完成,但我没有铅笔。
不是吗?

我不确定你的意思,但我认为你的算法会在正确的象限找到一个整数点,但不一定是最接近的点。 - Keith Randall
同时,您不能以这种形式表达问题。一行不必是水平或垂直的。 - Alexandre C.
我对这个问题感到矛盾。虽然这不是一个真正的答案,但我想点赞“无数点”讨论,因为你已经清楚地解释了为什么天真的实现需要指数时间(在输入数字的位数上,即在数字的数量级上线性时间)。如果你在评论中说出这句话,我会点赞这条评论 :-) - Don Hatch

0
这里有一个部分想法,可能对得到完整解决方案有用。想象一下两条线非常非常接近。那么它们之间的任何积分解也将是非常靠近每条线的积分点。让我们尝试找到靠近线ax+by=c的整数点。由于y=(c-ax)/b,因此我们需要使y非常接近整数,因此b大致可以整除c-ax。换句话说,对于小整数D,c-ax+D==0 mod b。
我们可以解出x:x=a^-1(c+D) mod b (如果a和b互质,则a ^ -1将存在,不确定是否在这种情况下成立)。

因此,该算法是评估 x = a^-1(c+D) mod b,其中 D=0,+1,-1,+2,-2,... 并尝试结果 x 是否有效。如果两条线的交点附近有接近整数的点,则它们应该在此迭代中早期出现。当然,在最坏情况下,您可能需要达到 D=b-1...


0

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

一旦确定了点所在的扇区,将原点移动到交点,并注意非整数误差。从交点到底部线条计算出线的斜率,然后在整数x值处进行水平正常(如果斜率较小)或从y进行正常(如果斜率较高),并找到它与另一个轴相交的整数点。

您应该能够检查每个轴上的整数步骤,以确定您正在测试的点是否在两条线之上或之间(从交点到该点创建一个新向量并确定斜率)。如果该点在上方,则增加您的整数步骤。因为您正在从一条线的最小梯度差异进行测试,所以应该是O(n)。在Bresenhams算法中,有8个扇区而不仅仅是4个。


-1
检查一个点是否属于数学圆锥的问题相当简单。给定两个向量v和w,由(v,w)定义的圆锥中的任何点都将具有以下形式:z = a***v** + b***w**,其中a,b >= 0。请注意,为了使此方法有效,您必须将原点移动到两条线的交点处。由于我们无法假设交点具有有限精度,因此您必须进行浮点运算并决定某些内容是否足够接近您想要的内容。
找到定义四个圆锥体的向量(有无限多个,我们只需要两个),这些向量由两条线定义。
找出包含我们点的圆锥体,将其称为C。
取定义C的两个向量,并找到中间向量(将C分割成两个相同的圆锥体),称之为m。
现在是时候开始循环了。为了简单起见,我假设我们将自己限制在x轴和y轴上的n位数。请注意,你需要一个比n位数更大的整数作为m的长度。现在沿着m的长度进行二进制搜索,并每次检查周围的两个环(我猜想一个环就足够了)。当你找到最小长度的圆锥体包含点C时,检查哪些点是最接近的。
最坏情况下的增长率为O(log(sqrt(2*n^2)),其中n是我们用于表示x和y轴的长度。

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

所有这些方法的主要问题是精度,所以请记住这一点。其他追求的替代方法可能包括构成锥体的两个半平面。每个锥体都由两个半平面的交集定义,检查一个点是否是半平面的成员可能足够简单,我不确定。

编辑:使用半平面确实更容易,两条线将R^2分为2个半平面,这给出了4个组合,即4个锥体。因此,每次您想要检查一个点是否是锥体的成员时,必须检查它是否是构成该特定锥体的2个半平面的成员。如何执行解释在此处:

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

这样做比移动Origo并调整精度要容易。替换检查成员资格的方法并保持其他所有内容不变,您将获得相同的增长。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接