Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/294.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 求解三次多项式系统(寻找贝塞尔曲线的交点)_Python_Computational Geometry_Sympy_Polynomial Math_Groebner Basis - Fatal编程技术网

Python 求解三次多项式系统(寻找贝塞尔曲线的交点)

Python 求解三次多项式系统(寻找贝塞尔曲线的交点),python,computational-geometry,sympy,polynomial-math,groebner-basis,Python,Computational Geometry,Sympy,Polynomial Math,Groebner Basis,有人能提出一个修复方案或替代方案来找到这个系统的解决方案吗? 特别地,我只关心[0,1]x[0,1]中的解(s,t) 注意:我在这里寻找两条三次贝塞尔曲线的交点。我需要的方法,以确保找到所有的解决方案,并希望在合理的时间内(我的使用,这意味着每对曲线数秒) 我尝试使用Symphy,但solve()和solve_poly_system()都返回空列表 这是我的密码: from sympy.solvers import solve_poly_system, solve from sympy.abc

有人能提出一个修复方案或替代方案来找到这个系统的解决方案吗? 特别地,我只关心[0,1]x[0,1]中的解(s,t)

注意:我在这里寻找两条三次贝塞尔曲线的交点。我需要的方法,以确保找到所有的解决方案,并希望在合理的时间内(我的使用,这意味着每对曲线数秒)

我尝试使用Symphy,但solve()和solve_poly_system()都返回空列表

这是我的密码:

from sympy.solvers import solve_poly_system, solve
from sympy.abc import s,t

#here are two cubics.  I'm looking for their intersection in [0,1]x[0,1]:
cub1 = 600*s**3 - 1037*s**2 + 274*s + 1237*t**3 - 2177*t**2 + 642*t + 77
cub2 = -534*s**3 + 582*s**2 + 437*s + 740*t**3 - 1817*t**2 + 1414*t - 548

#I know such a solution exists (from plotting these curves) and fsolve finds an     approximation of it no problem:
from scipy.optimize import fsolve
fcub1 = lambda (s,t): 600*s**3 - 1037*s**2 + 274*s + 1237*t**3 - 2177*t**2 + 642*t + 77
fcub2 = lambda (s,t):-534*s**3 + 582*s**2 + 437*s + 740*t**3 - 1817*t**2 + 1414*t - 548
F = lambda x: [fcub1(x),fcub2(x)]
print 'fsolve gives (s,t) = ' + str(fsolve(F,(0.5,0.5)))
print 'F(s,t) = ' + str(F(fsolve(F,(0.5,0.5))))

#solve returns an empty list
print solve([cub1,cub2])

#solve_poly_system returns a DomainError: can't compute a Groebner basis over RR
print solve_poly_system([cub1,cub2])
这将产生:

fsolve gives (s,t) = [ 0.35114023  0.50444115]
F(s,t) = [4.5474735088646412e-13, 0.0]
[]
[]

谢谢你的阅读

简化系统怎么样

通过适当的线性组合,您可以消除
s^3
t^3
中的一个,并求解剩余的二次方程。通过将结果插入到另一个方程中,可以得到一个未知的方程

或求解由结果得到的代数方程:

36011610661302281 - 177140805507270756*s - 201454039857766711*s^2 + 1540826307929388607*s^3 + 257712262726095899*s^4 - 4599101672917940010*s^5 + 1114665205197856508*s^6 + 6093758014794453276*s^7 - 5443785088068396888*s^8 + 1347614193395309112*s^9 = 0

()

对于Béziers交叉口,有更好的方法。(,)

我推荐一个简单的解决方案:实现Bezier细分算法()。对于这两条曲线,计算控制点的边界框。如果它们重叠,则可以进行交叉,细分并用两半重复该过程(这次将进行四次比较)。递归地继续

你不必害怕指数爆炸(1、4、16、256……比较),因为很快许多框就会停止重叠


请注意,在理论上,您可以使用控制点的凸包,但在实践中,一个简单的边界框就足够了,而且更容易使用。

Yves的解决方案效果很好。以下是我的代码,以防对任何人有所帮助:

from math import sqrt
def cubicCurve(P,t):
    return P[0]*(1-t)**3 + 3*P[1]*t*(1-t)**2 + 3*P[2]*(1-t)*t**2 + P[3]*t**3
def cubicMinMax_x(points):
    local_extremizers = [0,1]
    a = [p.real for p in points]
    delta = a[1]**2 - (a[0] + a[1]*a[2] + a[2]**2 + (a[0] - a[1])*a[3])
    if delta>=0:
        sqdelta = sqrt(delta)/(a[0] - 3*a[1] + 3*a[2] - a[3])
        tau = a[0] - 2*a[1] + a[2]
        r1 = tau+sqdelta
        r2 = tau-sqdelta
        if 0<r1<1:
            local_extremizers.append(r1)
        if 0<r2<1:
            local_extremizers.append(r2)
    localExtrema = [cubicCurve(a,t) for t in local_extremizers]
    return min(localExtrema),max(localExtrema)
def cubicMinMax_y(points):
    return cubicMinMax_x([-1j*p for p in points])
def intervalIntersectionWidth(a,b,c,d): #returns width of the intersection of intervals [a,b] and [c,d]  (thinking of these as intervals on the real number line)
    return max(0, min(b, d) - max(a, c))
def cubicBoundingBoxesIntersect(cubs):#INPUT: 2-tuple of cubics (given bu control points) #OUTPUT: boolean
    x1min,x1max = cubicMinMax_x(cubs[0])
    y1min,y1max = cubicMinMax_y(cubs[0])
    x2min,x2max = cubicMinMax_x(cubs[1])
    y2min,y2max = cubicMinMax_y(cubs[1])
    if intervalIntersectionWidth(x1min,x1max,x2min,x2max) and intervalIntersectionWidth(y1min,y1max,y2min,y2max):
        return True
    else:
        return False
def cubicBoundingBoxArea(cub_points):#INPUT: 2-tuple of cubics (given bu control points) #OUTPUT: boolean
    xmin,xmax = cubicMinMax_x(cub_points)
    ymin,ymax = cubicMinMax_y(cub_points)
    return (xmax-xmin)*(ymax-ymin)
def halveCubic(P):
    return ([P[0], (P[0]+P[1])/2, (P[0]+2*P[1]+P[2])/4, (P[0]+3*P[1]+3*P[2]+P[3])/8],[(P[0]+3*P[1]+3*P[2]+P[3])/8,(P[1]+2*P[2]+P[3])/4,(P[2]+P[3])/2,P[3]])
class Pair(object):
    def __init__(self,cub1,cub2,t1,t2):
        self.cub1 = cub1
        self.cub2 = cub2
        self.t1 = t1 #the t value to get the mid point of this curve from cub1
        self.t2 = t2 #the t value to get the mid point of this curve from cub2
def cubicXcubicIntersections(cubs):
#INPUT: a tuple cubs=([P0,P1,P2,P3], [Q0,Q1,Q2,Q3]) defining the two cubic to check for intersections between.  See cubicCurve fcn for definition of P0,...,P3
#OUTPUT: a list of tuples (t,s) in [0,1]x[0,1] such that cubicCurve(cubs[0],t) - cubicCurve(cubs[1],s) < Tol_deC
#Note: This will return exactly one such tuple for each intersection (assuming Tol_deC is small enough)
    Tol_deC = 1 ##### This should be set based on your accuracy needs.  Making it smaller will have relatively little effect on performance.  Mine is set to 1 because this is the area of a pixel in my setup and so the curve (drawn by hand/mouse) is only accurate up to a pixel at most. 
    maxIts =  100 ##### This should be something like maxIts = 1-log(Tol_deC/length)/log(2), where length is the length of the longer of the two cubics, but I'm not actually sure how close to being parameterized by arclength these curves are... so I guess I'll leave that as an exercise for the interested reader :)
    pair_list = [Pair(cubs[0],cubs[1],0.5,0.5)]
    intersection_list = []
    k=0
    while pair_list != []:
        newPairs = []
        delta = 0.5**(k+2)
        for pair in pair_list:
            if cubicBoundingBoxesIntersect((pair.cub1,pair.cub2)):
                if cubicBoundingBoxArea(pair.cub1)<Tol_deC and cubicBoundingBoxArea(pair.cub2)<Tol_deC:
                    intersection_list.append((pair.t1,pair.t2)) #this is the point in the middle of the pair
                    for otherPair in pair_list:
                        if pair.cub1==otherPair.cub1 or pair.cub2==otherPair.cub2 or pair.cub1==otherPair.cub2 or pair.cub2==otherPair.cub1:
                            pair_list.remove(otherPair) #this is just an ad-hoc fix to keep it from repeating intersection points
                else:
                    (c11,c12) = halveCubic(pair.cub1)
                    (t11,t12) = (pair.t1-delta,pair.t1+delta)
                    (c21,c22) = halveCubic(pair.cub2)
                    (t21,t22) = (pair.t2-delta,pair.t2+delta)
                    newPairs += [Pair(c11,c21,t11,t21), Pair(c11,c22,t11,t22), Pair(c12,c21,t12,t21), Pair(c12,c22,t12,t22)]
        pair_list = newPairs
        k += 1
        if k > maxIts:
            raise Exception ("cubicXcubicIntersections has reached maximum iterations without terminating... either there's a problem/bug or you can fix by raising the max iterations or lowering Tol_deC")
    return intersection_list
我希望这能帮助别人!
再次感谢你对伊夫的帮助

我认为这不能用辛菲来解决。例外情况是因为在euqations中使用浮点值。在
solve\u poly\u system
的源代码中有一行检查顺序:
if a@HYRY:Oh关于域错误你是对的。我本想删除所有的小数来检查,但我错过了一个!另外,你是说你认为solve_poly_系统只能用于每个多项式的阶数小于3的系统吗?我相信我不熟悉这个方法,我会检查一下。所以你认为这可以找到任意两条三次曲线(特别是贝塞尔曲线)的交点吗?是的,可以,但我没有注意到它是贝塞尔曲线的交点。有更合适的方法来解决这个问题,利用船体的特性。我认为这对我的目的来说会很好。我今晚会设法实施,看看结果如何。谢谢,真为你高兴。贝塞尔曲线具有很好的特性。
def splitBezier(points,t):
#returns 2 tuples of control points for the two resulting Bezier curves
    points_left=[]
    points_right=[]
    (points_left,points_right) =  splitBezier_deCasteljau_recursion((points_left,points_right),points,t)
    points_right.reverse()
    return (points_left,points_right)
def splitBezier_deCasteljau_recursion(cub_lr,points,t):
    (cub_left,cub_right)=cub_lr
    if len(points)==1:
        cub_left.append(points[0])
        cub_right.append(points[0])
    else:
        n = len(points)-1
        newPoints=[None]*n
        cub_left.append(points[0])
        cub_right.append(points[n])
        for i in range(n):
            newPoints[i] = (1-t)*points[i] + t*points[i+1]
        (cub_left, cub_right) = splitBezier_deCasteljau_recursion((cub_left,cub_right),newPoints,t)
    return (cub_left, cub_right)