强基初中数学&学Python——第五十二课 坐标应用之三:两点之间的距离

    上节我们知道方向和距离可以确定点的坐标,反过来,两点的坐标也可以确定它们的距离。容易想到,这实际是知道直角三角形的直角边求斜边。用勾股定理:

直角边a和b,斜边c,
a²+b²=c²,

进行计算。

    例题:已知A(-2,6)和B(6,-4),计算它们的距离(结果用最简根式表示)。用海龟画图标出A和B,并编写程序计算它们的距离核对你的结果。
分析:其中的直角三角形两直角边是x,y坐标的分别差,根据勾股定理,它们的平方和是两点距离的平方,开方化最简分数。编程的步骤也一样,只不过需要用到已经做好的二次根式计算模块和根式书写模块。
解:[6-(-2)]²+(-4-6)²=8²+10²=164,164开方=2根号41。

Python源程序代码(图片):

Python源程序代码(源码):
见附录1。

绘制出的精美图片:

结论:计算值与程序的结果一致。

练习题:
    三角形的三个顶点的坐标分别是A(-6,-8)、B(-3,2)、C(4,-4),求三角形的三边长度。仿上面的例题,画出这个三角形,标注顶点坐标,计算边长的实数值并打印在图上。

附录1:
#从两点的坐标计算两点的实数距离
import sys 
sys.path.append("/5xstar/pyfiles")  #预制自定义模块(py文件)存放根目录
import turtle as t  #导入海龟画图
t.setup(500,500)
t.screensize(420,420)  
t.shape("circle")  
t.shapesize(0.25,0.25) #印章时是四分之一大小
from mymath.rcs import * #导入函数轨迹模块
from mymath.quadraticradical import QR #导入单项根式
from mymath.rooting import writeSqrt #导入根式书写函数
xUnt = (1,20)  #数值与像素数值比例1:20,实际坐标是像素数的二十分之一
yUnt = (1,20)
build(t,xUnt,yUnt) #画平面直角坐标系
t.up()
workFont=("宋体", 12, "bold") #标位置字体:12号,宋体,黑体
#标定位置
def indication(name,pos,dx=2,dy=2,algn=None):
    '''
    @name 位置名称
    @pos 位置坐标
    @dx 写字位置x增加值
    @dy 写字位置y增加值
    @align 写字方向:center,left,right
    '''
    t.setpos(pos)
    t.stamp()
    t.setpos(pos[0]+dx,pos[1]+dy)
    if algn == None:
        t.write("%s(%d,%d)" % (name, pos[0]//20, pos[1]//20), font=workFont)  #实际坐标是像素数的二十分之一
    else:
        t.write("%s(%d,%d)" % (name, pos[0]//20, pos[1]//20), align=algn, font=workFont)
ax, ay = -2, 6  #A点
t.setpos(ax*20,ay*20)  
t.stamp()
indication("A",t.pos())  #打名称坐标
t.down()
bx, by = 6, -4  #B点
t.setpos(bx*20, by*20) 
t.stamp()
indication("B",t.pos())  #打名称坐标
t.up()
t.sety((by-2)*20)  #打印结果
qr=QR(1, (bx-ax)**2+(by-ay)**2)
t.write("AB距离是%s" % str(qr.coefficient), move=True, align="center",  font=workFont) #打印根号前系数
if qr.bottom != 1:
   writeSqrt(t, qr.bottom, font=workFont)
t.ht()
t.mainloop()

    

附录2:
'''初中平面直角坐标系中单值函数轨迹、画直线和打点



@作者  码老师

@微信公众号  学思营 ID:xuesying

@公司  深圳五行星软件有限公司

@日期  2020-12-19



@使用方法

1、导入海龟画图并初始化窗口和画布,例如:

    import turtle as t

    t.setup(600,600)

    t.screensize(400,400)

    t.shape("turtle")

    

2、导入本模块:

    from rcs import *



3、用下面函数:

    build(aTurtle, xUnt = None, yUnt = None, wc = None):

    @aTurtle 一个海龟

    @xUnt  x轴数值与像素对应元组

    @yUnt  y轴数值与像素对应元组

    @wc 自定义坐标元组(llx, lly, urx, ury)

        @llx -- 一个数值, 画布左下角的 x-坐标(像素)

        @lly -- 一个数值, 画布左下角的 y-坐标

        @urx -- 一个数值, 画面右上角的 x-坐标

        @ury -- 一个数值, 画布右上角的 y-坐标

    画平面直角坐标系,例如:

    build(t) #, worldcoordinates=(0,0,400,400))



4、使用模块进行画单调函数轨迹、直线和打点  

(1)画函数轨迹

第一步:定义一个单调函数例如:

        def fun(x):

            return 5*x + 3

第二步:获取x,y的取值范围:

        minx, maxx, miny, maxy = getRange()

第三步(可选):根据x,y的取值范围和函数的实际要求,确定自变量取值范围:

       x1,x2

第四步:调用下面函数:

        trace(aTurtle, x1, x2, fun):

        @aTurtle 一个海龟

        @x1 自变量一端点值

        @x2 自变量另一端点值

        @fun 单调函数

        画函数轨迹,例如:

    #一元一次函数

    def fun(x):

        return 2*x + 1

    trace(t, 50, -50, fun)        



(2)画直线段

第一步:获取x,y的取值范围:

        minx, maxx, miny, maxy = getRange()

第二步:调用下面函数:

        line(aTurtle, x1, y1, x2, y2):

        @aTurtle 一个海龟

        @x1,y1 始点

        @x2,y2 终点

        画直线段,例如画对角线:

        line(t, minx, miny, maxx, maxy)



(3)打圆点

第一步:获取x,y的取值范围:

        minx, maxx, miny, maxy = getRange()

第二步:调用下面函数:

        circularDot(aTurtle, x, y, size=None, *color):

        @aTurtle 一个海龟

        @x,y 坐标

        @size 圆点大小

        @color 圆点颜色

        打圆点,例如下降对角线打点:

    x, y = minx, maxy

    size = 1

    while True:

        if x > maxx or y < miny:

            break

        circularDot(t, x, y, size, "red")

        x += 1

        y -= 1

        size += 1



'''



#导入分数

from fractions import Fraction



#坐标轴线宽

axisWidth = 1

#坐标轴和坐标点颜色

axisColor = "black"

#坐标箭头

axisTurtle = "classic"

#箭头大小 原来四分之一 轮廓线1

axisShapesize = (0.8, 0.8, 1)

#数轴单位(整数)及其对应的像素大小(整数) 1对应10像素

xUnit = [1, 20]

yUnit = [1, 20]

#方格线宽

squareWidth = 1

#方格线色 浅蓝

squareColor = "#EFEFFF"

#数点线长

markLen = 5

#数字字体

markFont=("Arial", 8, "normal")

#坐标系,调用build后才有值

worldcoordinates = None





#检测数轴单位

def __checkUnit(unt):

    if unt != None and len(unt) == 2:

        return isinstance(unt[0],int) and isinstance(unt[0],int)

    return False







#建立直角坐标系

def build(aTurtle, xUnt = None, yUnt = None, wc = None):

    '''按初中课本使用的直角坐标系绘图

    @aTurtle 一个海龟

    @xUnt  x轴数值与像素对应元组

    @yUnt  y轴数值与像素对应元组

    @wc 自定义坐标元组(llx, lly, urx, ury)

        @llx -- 一个数值, 画布左下角的 x-坐标(像素)

        @lly -- 一个数值, 画布左下角的 y-坐标

        @urx -- 一个数值, 画面右上角的 x-坐标

        @ury -- 一个数值, 画布右上角的 y-坐标'''

    #保持海龟原来的参数后设置参数

    speed = aTurtle.speed()

    aTurtle.speed(0)

    width = aTurtle.pensize()

    pencolor = aTurtle.pencolor()

    shape = aTurtle.shape()

    shapesize = aTurtle.shapesize()

    isdown = aTurtle.isdown()

    pos = aTurtle.pos()

    heading =aTurtle.heading()



    #数量与像素的对应关系

    if __checkUnit(xUnt):

        xUnit[0] = xUnt[0]

        xUnit[1] = xUnt[1]

    if __checkUnit(yUnt):

        yUnit[0] = yUnt[0]

        yUnit[1] = yUnt[1]



    global worldcoordinates



    #自定义坐标

    if wc != None and len(wc) == 4:

        worldcoordinates = wc

        aTurtle.setworldcoordinates(*worldcoordinates) #自定义坐标系

    else:

        temp = aTurtle.screensize()

        worldcoordinates = (-temp[0]/2,-temp[1]/2,temp[0]/2,temp[1]/2)



    #定坐标方向

    xh = worldcoordinates[0] < worldcoordinates[2]

    if xh:  #True从左到右

        x1,x2 = int(worldcoordinates[0]), int(worldcoordinates[2])

    else:

        x1,x2 = int(worldcoordinates[2]), int(worldcoordinates[0])

    yh = worldcoordinates[1] < worldcoordinates[3]

    if yh:  #True从下到上

        y1,y2 = int(worldcoordinates[1]), int(worldcoordinates[3])

    else:

        y1,y2 = int(worldcoordinates[3]), int(worldcoordinates[1])





    #画方格线

    #画与y轴平行的线

    if isdown:

        aTurtle.up()

    for i in range(-xUnit[1], x1, -xUnit[1]):

        aTurtle.pencolor(squareColor)

        aTurtle.pensize(squareWidth)

        aTurtle.setpos(i,y1)

        aTurtle.down()

        aTurtle.sety(y2)

        aTurtle.up()

        aTurtle.sety(0)

        aTurtle.down()        

        aTurtle.pencolor(axisColor)

        aTurtle.pensize(axisWidth)

        aTurtle.sety(markLen)

        aTurtle.up()

        if yh:   #标x数字

            aTurtle.sety(-markLen-markFont[1])  #还要下降字体高度

        else:

            aTurtle.sety(-markLen//2)           #无需下降字体高度

        aTurtle.write(i//xUnit[1]*xUnit[0], align="center", font = markFont)        

    for i in range(xUnit[1], x2, xUnit[1]):

        aTurtle.pencolor(squareColor)

        aTurtle.pensize(squareWidth)

        aTurtle.setpos(i,y1)

        aTurtle.down()

        aTurtle.sety(y2)

        aTurtle.up()

        aTurtle.sety(0)

        aTurtle.down()        

        aTurtle.pencolor(axisColor)

        aTurtle.pensize(axisWidth)

        aTurtle.sety(markLen)

        aTurtle.up()

        if yh:

            aTurtle.sety(-markLen-markFont[1])

        else:

            aTurtle.sety(-markLen//2)

        aTurtle.write(i//xUnit[1]*xUnit[0], align="center", font = markFont)

    #画与x轴平行的线

    for i in range(-yUnit[1], y1, -yUnit[1]):

        aTurtle.pencolor(squareColor)

        aTurtle.pensize(squareWidth)

        aTurtle.setpos(x1, i)

        aTurtle.down()

        aTurtle.setx(x2)

        aTurtle.up()

        aTurtle.setx(0)

        aTurtle.down()        

        aTurtle.pencolor(axisColor)

        aTurtle.pensize(axisWidth)

        aTurtle.setx(markLen)

        aTurtle.up()

        if yh:  #标y数字

            aTurtle.setpos(-markLen//2, i-markFont[1]//2) #下降字体高度一半

        else:

            aTurtle.setpos(-markLen//2, i+markFont[1]//2)

        if xh:  

            aTurtle.write(i//yUnit[1]*yUnit[0], align="right", font = markFont)

        else:

            aTurtle.write(i//yUnit[1]*yUnit[0], font = markFont)            

    for i in range(yUnit[1], y2, yUnit[1]):

        aTurtle.pencolor(squareColor)

        aTurtle.pensize(squareWidth)

        aTurtle.setpos(x1, i)

        aTurtle.down()

        aTurtle.setx(x2)

        aTurtle.up()

        aTurtle.setx(0)

        aTurtle.down()        

        aTurtle.pencolor(axisColor)

        aTurtle.pensize(axisWidth)

        aTurtle.setx(markLen)

        aTurtle.up()

        if yh:

            aTurtle.setpos(-markLen//2, i-markFont[1]//2)

        else:

            aTurtle.setpos(-markLen//2, i+markFont[1]//2)

        if xh:

            aTurtle.write(i//yUnit[1]*yUnit[0], align="right", font = markFont)

        else:

            aTurtle.write(i//yUnit[1]*yUnit[0], font = markFont)



    #画坐标

    aTurtle.pencolor(axisColor)

    aTurtle.pensize(axisWidth)

    aTurtle.shape(axisTurtle)

    aTurtle.shapesize(*axisShapesize)

    aTurtle.up()

    aTurtle.setpos(x1, 0)

    aTurtle.down()

    aTurtle.setx(x2)

    if xh:

        aTurtle.seth(0)

    else:

        aTurtle.seth(180)

    aTurtle.stamp()

    aTurtle.up()

    if yh:

        aTurtle.sety(-markLen-markFont[1])

    else:

        aTurtle.sety(-markLen//2)

    aTurtle.write('x', align="center", font = markFont)

    aTurtle.setpos(0, y1)

    aTurtle.down()

    aTurtle.sety(y2)

    if yh:

        if xh:

            aTurtle.seth(90)

        else:

            aTurtle.seth(-90)

    else:

        if xh:

            aTurtle.seth(90)

        else:

            aTurtle.seth(-90)

    aTurtle.stamp()

    aTurtle.up()

    if yh:

        aTurtle.setpos(-markLen//2, y2-markFont[1])

    else:

        aTurtle.setx(-markLen//2)

    if xh:

        aTurtle.write('y', align="right", font = markFont)

    else:

        aTurtle.write('y', font = markFont)



    #标注原点

    if yh:

        aTurtle.setpos(-markLen//2, -markLen-markFont[1])

    else:

        aTurtle.setpos(-markLen//2, -markLen//2)

    if xh:

        aTurtle.write(0, align="right", font = markFont)

    else:

        aTurtle.write(0, font = markFont)

        



    #恢复海龟原来的设置参数

    aTurtle.speed(speed)

    aTurtle.shape(shape)

    aTurtle.pensize(width)

    aTurtle.pencolor(pencolor)

    aTurtle.shapesize(*shapesize)

    aTurtle.setpos(*pos)

    aTurtle.seth(heading)

    if isdown:

        aTurtle.down()



#检查是否已经初始化

def __checkBuild():

    if worldcoordinates == None:

        print("还没有画坐标,请调用build(aTurtle, xUnt = None, yUnt = None, worldcoordinates = None)函数画坐标")

        return False

    else:

        return True



#屏幕上进1像素实际的值之间的转换

def __pixchange():

    #屏幕上进1像素实际的值之间的转换

    dx, udx, dy, udy = Fraction(*xUnit), Fraction(xUnit[1], xUnit[0]), Fraction(*yUnit), Fraction(yUnit[1], yUnit[0])

    #x坐标作图范围

    if worldcoordinates[0] < worldcoordinates[2]:

        minx, maxx = dx * worldcoordinates[0], dx * worldcoordinates[2]

    else:

        minx, maxx = dx * worldcoordinates[2], dx * worldcoordinates[0]

    #y坐标作图范围

    if worldcoordinates[1] < worldcoordinates[3]:

        miny, maxy = dy * worldcoordinates[1], dy * worldcoordinates[3]

    else:

        miny, maxy = dy * worldcoordinates[3], dy * worldcoordinates[1]

    return dx, udx, dy, udy, minx, maxx, miny, maxy



#函数轨迹

def trace(aTurtle, x1, x2, fun):

    '''函数轨迹

    @aTurtle 一个海龟

    @x1 自变量一端点值

    @x2 自变量另一端点值

    @fun 单调函数'''



    #自变量检查

    if x1 == x2:

        print("自变量不可以相等")

        return False        



    #检查是否已经画坐标

    if not __checkBuild():        

        return False

    

    #保持海龟落笔状态

    isdown = aTurtle.isdown()

    speed = aTurtle.speed()

    aTurtle.speed(0)

    pos = aTurtle.pos()

    if isdown:

        aTurtle.up()

    

    if x1 > x2:    #交换自变量

        temp = x1

        x1 = x2

        x2 = temp



    #屏幕上进1像素实际的值之间的转换

    dx, udx, dy, udy, minx, maxx, miny, maxy  =  __pixchange()





    if x2 <= minx or x1 >= maxx:

        print("自变量不在%s到%s范围内!" % (str(minx), str(maxx)))

        return False

    if x1 < minx:

        x1 = minx

    if x2 > maxx:

        x2 = maxx

        



    #起点

    x = x1

    y = fun(x1)

    if y >= miny and y<= maxy:

        aTurtle.setpos(udx*x, udy*y)

        aTurtle.down()

        



    #描图

    while True:

        x += dx

        if x > x2: #过了终点

            break

        y = fun(x)

        if y >= miny and y<= maxy:

            aTurtle.setpos(udx*x, udy*y)

            if not aTurtle.isdown():

                aTurtle.down()

        else:

            if aTurtle.isdown():

               aTurtle.setpos(udx*x, udy*y) 

            aTurtle.up()



    if aTurtle.isdown():

        aTurtle.up()

    aTurtle.setpos(*pos)

    aTurtle.speed(speed)

    #恢复下笔状态 

    if isdown:

        aTurtle.down()



    return True



#作图范围

def getRange():

    '''作图范围

    @return x坐标最小值,x坐标最大值,y坐标最小值,y坐标最大值'''

    

    #检查是否已经画坐标

    if not __checkBuild():        

        return None    



    #屏幕上进1像素实际的值之间的转换

    dx, udx, dy, udy, minx, maxx, miny, maxy  =  __pixchange()

    return minx, maxx, miny, maxy



#作图范围字符串

def __rangestr(minx, maxx, miny, maxy):

    return "%s≤x≤%s,%s≤y≤%s" % (str(minx),str(maxx),str(miny),str(maxy))

    

#画线段

def line(aTurtle, x1, y1, x2, y2):

    '''画线段

    @aTurtle 一个海龟

    @x1,y1 始点

    @x2,y2 终点'''



    #检查是否已经画坐标

    if not __checkBuild():        

        return False    



    #屏幕上进1像素实际的值之间的转换

    dx, udx, dy, udy, minx, maxx, miny, maxy  =  __pixchange()



    if min(x1, x2) < minx or max(x1, x2) > maxx or min(y1, y2) < miny or max(y1, y2) > maxy:

        print("有端点不在作图范围(%s)内!" % __rangestr(minx, maxx, miny, maxy))

        return False   



    #保持海龟落笔状态

    isdown = aTurtle.isdown()

    pos = aTurtle.pos()

    speed = aTurtle.speed()

    aTurtle.speed(0)

    if isdown:

        aTurtle.up()



    aTurtle.setpos(udx*x1, udy*y1)

    aTurtle.down()

    aTurtle.setpos(udx*x2, udy*y2)

    aTurtle.up()

    aTurtle.setpos(*pos)

    aTurtle.speed(speed)

    if isdown:

        aTurtle.down()

    return True



#打圆点

def circularDot(aTurtle, x, y, size=None, *color):

    '''打点

    @aTurtle 一个海龟

    @x,y  坐标

    @size 圆点大小

    @color 圆点颜色'''



    #检查是否已经画坐标

    if not __checkBuild():        

        return False    



    #屏幕上进1像素实际的值之间的转换

    dx, udx, dy, udy, minx, maxx, miny, maxy  =  __pixchange()



    if x< minx or x > maxx or y < miny or y > maxy:

        print("该点点不在作图范围(%s)内!" % __rangestr(minx, maxx, miny, maxy))

        return False

    

    isdown = aTurtle.isdown()

    pos = aTurtle.pos()

    speed = aTurtle.speed()

    aTurtle.speed(0)

    if isdown:

        aTurtle.up()

    aTurtle.setpos(udx*x, udy*y)

    if color == None:

        aTurtle.dot(size)

    else:

        aTurtle.dot(size, *color)



    aTurtle.setpos(*pos)

    aTurtle.speed(speed)

    if isdown:

        aTurtle.down()

    return True    

    



if __name__ == "__main__":

    import turtle as t

    t.setup(600,600)

    t.screensize(400,400)

    t.shape("turtle")

    build(t) #, worldcoordinates=(0,0,400,400))

    #一元一次函数

    def fun1(x):

        return 2*x + 1

    trace(t, 50, -50, fun1)

    #一元二次函数

    def fun2(x):

        return x**2 + 2*x + 2

    trace(t, 50, -50, fun2)

    #画圆

    def fun3(x):

        return (5**2 - x**2)**0.5

    trace(t, 5, -5, fun3)

    def fun4(x):

        return -(5**2 - x**2)**0.5

    trace(t, 5, -5, fun4)

    #画对角线

    minx, maxx, miny, maxy = getRange()

    line(t, minx, miny, maxx, maxy)

    #打点对角线

    x, y = minx, maxy

    size = 1

    while True:

        if x > maxx or y < miny:

            break

        circularDot(t, x, y, size, "red")

        x += 1

        y -= 1

        size += 1

        

    t.mainloop()

    

    

    

附录3:
'''二次根式加、减、乘、除(除数项数不超过2)和乘方运算


@作者  码老师

@微信公众号  学思营 ID:xuesying

@公司  深圳五行星软件有限公司

@日期  2020-11-01


@使用方法

1、导入模块

>>>from quadraticradical import *


2、单项二次根


空单项二次根式(与0相等)例如:

>>>QR()


整数单项二次根式例如:

>>>QR(3)


分数单项二次根式例1:

>>>QR("1/2")


分数单项二次根式例2:

>>>from fractions import Fraction

>>>QR(Fraction(1, 2))


小数单项二次根式(字符串输入,系统自动转为分数):

>>>QR("3.14")


如果单项二次根式有根号数,底数在常数之后,例如

>>>QR("3.14", 10)



3、多项二次根式


空多项二次根式(与0相等):

>>>pr = PR()

>>>pr.pt()


可以有列表化单项二次根式,单项二次根式和多项二次根式参数,例如:

>>>q1 = QR("3.14", 2)

>>>p1 = PR([12, 98])

>>>_test = PR(q1, p1, QR("2/3", 10), PR(q1, [9, 8]), ["5/3", 20])

>>>_test.pt()


4、加多项二次根式:

>>>_test.add(["2.5", 3], PR(["7.5", 18])).pt()


5、减多项二次根式:

>>>_test.sub(PR(["3/2",32], ["3/4",20])).pt()


6、乘多项二次根式:

>>>_test.mul([1,30],[1]).pt()


7、除以常数:

>>>_test.div("-1/2").pt()


8、除以单项二次根式:

>>>_test.div(["1/2","1/10"]).pt()


9、除以二项二次根式:

>>>_test.div(["1/2","1/10"], ["0.125", "3.75"]).pt()


10、乘方:    

>>>PR([1,3],[1,2]).pow(5).pt()



'''


import math

from fractions import Fraction



#二次方根数类

class QR:

    '''quadratic radical'''


    coefficient = 0  #系数 有理数

    

    bottom = 1  #方根的底——非负有理数

    

    #相反数

    def opp(self):

        if self.coefficient != None:

           self.coefficient = -self.coefficient

        return self


    #倒数

    def rec(self):

        self.coefficient = Fraction(1, self.coefficient * self.bottom)

        return self


    #克隆

    def clone(self):

        return QR(self.coefficient, self.bottom)

    

    #打印

    def print(self, isFirst):

        if self.coefficient == None or self.coefficient == 0 or self.bottom == None or self.bottom == 0:

            return ""

        else:

            if self.coefficient == 1:

                s = ""

            elif self.coefficient == -1:

                s = "-"

            else:

                s = str(self.coefficient)

            if self.coefficient > 0:

                if not isFirst:

                    s = "+" + s

            if self.bottom != 1: 

                s += "sqrt(%d)" % self.bottom

            return s


    #比较大小:底降序

    #第二个要在前正值;合并0;其它负值

    def cmp(pr, otherPr):

        return otherPr.bottom - pr.bottom;



    #初始化方法

    def __init__(self, coefficient=0, bottom=1):

        #print("coefficient=",coefficient,"bottom=",bottom)

        if not isinstance(coefficient, (int, Fraction, str)) or not isinstance(bottom, (int, Fraction, str)):

            raise ValueError("输入的不是整数、分数或字符串(小数、分数)!")

        if isinstance(coefficient, (str)):

            coefficient = Fraction(coefficient)

        if isinstance(bottom, (str)):

            bottom = Fraction(bottom)

        if bottom < 0:

            raise ValueError("底数不可以小于0!")

        self.coefficient = coefficient

        self.bottom = bottom

        self.__simplestQuadraticRadical() #化简


    #平方方法

    def square(self):

        return self.coefficient**2 * self.bottom 


    #描述自己

    def __str__(self):

        if self.coefficient == 0 or self.bottom == 0:

            return "0"

        if self.bottom == 1:

            return str(self.coefficient)

        return "%ssqrt(%s)" % (str(self.coefficient), str(self.bottom))


    #化成最简二次根式

    def __simplestQuadraticRadical(self):

        #如果底是分数,就化为整数

        if isinstance(self.bottom, (Fraction)):

            self.coefficient = Fraction(self.coefficient, self.bottom.denominator)

            self.bottom = self.bottom.numerator * self.bottom.denominator

        return self.__firstStep()


    #第一步:检测自己是否是平方数,如果是就变成整数

    def __firstStep(self):

        #print("第一步:检测自己是否是平方数")

        tmp = round(math.sqrt(self.bottom))   #开方的最近整数

        if tmp ** 2 == self.bottom:

            self.coefficient *= tmp

            self.bottom = 1

            return self

        return self.__secondStep()


    #第二步:如果不是平方数,计算最大的可能因数

    def __secondStep(self):

        #print("第二步:如果不是平方数,计算最大的可能因数")

        maxNum = self.bottom // 2

        return self.__thirdStep(maxNum)


    #第三步:寻找不大于最大的可能因数的最近平方数

    def __thirdStep(self, maxNum):

        #print("第三步:寻找不小于最大的可能因数的最近平方数")

        end = math.ceil(math.sqrt(maxNum))

        return self.__fourthStep(end)


    #第四步:分解平方数的因数

    def __fourthStep(self, end):

        #print("第四步:分解平方数的因数")

        factors = []  #平方数是self.intNum的因数的数

        startNum = self.bottom  #开始数

        for i in range(end, 1, -1):

            i2 = i ** 2

            endNum = startNum // i2

            if i2 * endNum == startNum:

                factors.append(i)

                startNum = endNum

        return self.__fifthStep(factors, startNum)           



    #第五步:平方数因数开方成倍数,根式化成最简式

    def __fifthStep(self, factors, startNum):

        #print("第五步:平方数因数开方成倍数,根式化成最简式")

        if len(factors) == 0:

            return self     #根式已经是最简根式

        else:

            self.coefficient *= self.__getCoefficient(factors)

            self.bottom = startNum

            return self


    #根据因数的二次方根,生成最简根式的系数

    def __getCoefficient(self, factors):

        rst = 1

        for f in factors:

            rst *= f

        return rst


    #乘二次根式

    def mul(self, otherQr):

        self.coefficient *= otherQr.coefficient

        self.bottom *= otherQr.bottom

        return self.__simplestQuadraticRadical()


#多项二次根式

class PR:

    '''多项quadratic radical'''


    #项quadratic radical列表

    terms = None


    #克隆

    def __clone(self):

        p = PR()

        if self.terms == None or len(self.terms) == 0:

            return p

        lth = len(self.terms)

        t = []

        for i in range(lth):

            t.append(self.terms[i].clone())

        p.terms = t

        return p



    #排序合并同类项,按底数从大到小

    def __sort1(terms, idx, num):

        term = terms[num]  #需检查的元素

        for i in range(idx+1):  #把元素插入或合并到结果中

            m = terms[i]

            t = m.cmp(term)     #比较

            if t == 0:                              #合并同类项

                m.coefficient += term.coefficient                 

                return idx

            elif t > 0:                              #新的高阶项,高阶项插入当前位置

                idx += 1

                for p in range(idx, i, -1):         #后推其它项

                    terms[p] = terms[p-1]

                terms[i] = term                     #插入该项

                return idx                          #返回结果数

        idx += 1

        terms[idx] = term                           #把新项插在结果最后

        return idx

            


    #合并同类项与排序

    def __sort(terms):

        idx = 0   #结果下标

        lth = len(terms)

        for num in range(1, lth):

            idx = PR.__sort1(terms, idx, num)  #排序和合并1项

            #print("idx=%d" % idx)

        #消除0系数项

        temp = []

        for num in range(idx +1):

            term = terms[num]

            if term.coefficient != 0:

                temp.append(term)

        nlth = len(temp)

        if nlth == 0:

            del(terms[0:])

            return terms

        else:

            for num in range(nlth):

                terms[num] = temp[num]

            if nlth < lth:

                del(terms[nlth:])

            return terms;



    #打印自己

    def __str__(self):

        s = "原式="

        if self.terms == None or len(self.terms) == 0:

            s += "0"

        else:

            s += self.terms[0].print(True)

            for i in range(1, len(self.terms)):

                s += self.terms[i].print(False)

        return s

    def pt(self):

        print(self)


    #加

    def __add(self, isSub, *terms):

        if terms != None and len(terms) > 0:

            if self.terms == None:

                self.terms = []

            for term in terms:

                if isinstance(term, PR):

                    if term.terms != None and len(term.terms) > 0:

                        for t in term.terms:

                            if isSub:

                                self.terms.append(t.clone().opp())  #克隆,保证原式不受影响

                            else:

                                self.terms.append(t.clone())  

                else:

                    if isinstance(term, QR):

                        if isSub:

                            self.terms.append(term.clone().opp())     #克隆防止影响原对象

                        else:

                            self.terms.append(term.clone())     

                    else:

                        if isSub:

                            self.terms.append(QR(*term).opp())  #解包list添加1个单项式,无需克隆

                        else:

                            self.terms.append(QR(*term))  

            PR.__sort(self.terms) #排序

            

    def add(self, *terms):

        s = self.__clone()  #克隆防止改变原对象

        s.__add(False, *terms)

        return s


    #减            

    def sub(self, *terms):

        s = self.__clone()  #克隆防止改变原对象

        s.__add(True, *terms)

        return s


        

    #乘除常数,私有方法不克隆

    def __muldiv(self, fraction, isdiv):

        if self.terms != None and len(self.terms) >0:

                if isinstance(fraction,  (int, Fraction)):

                    p = fraction

                else:

                    p = Fraction(fraction)

                for term in self.terms:

                    if isdiv:

                        term.coefficient /= p

                    else:

                        term.coefficient *= p

        return self



    #多项乘单项,私有方法都不可以克隆

    def __mul(pr, qr):

        for term in pr.terms:

            term.mul(qr)

        PR.__sort(pr.terms) #排序

        return pr


    #乘

    def mul(self, *terms):

        if terms == None or len(terms) == 0:  #空调用返回自己

            return self

        if len(terms) == 1 and isinstance(terms[0],(int, Fraction, str)): #乘一个有理数

            return self.__clone().__muldiv(terms[0], False)      #需要克隆对象

        p = PR(*terms)  #建立要乘的多项式 如果里面有Mn或Pn,则它们会被克隆

        if self.terms == None or len(self.terms) == 0: #self是空多项式可忽略

            return p

        elif p.terms != None and len(p.terms) >0:

            lth = len(p.terms)

            s = self.__clone().__mul(p.terms[0])  #乘单项式

            if lth == 1:                             

                return s

            for i in range(lth-1):  #克隆对象乘

                s.__add(False, self.__clone().__mul(p.terms[i+1]))

            return s

        else:                       #p为空多项式

            return self;


            

    #乘方

    def pow(self, num):

        if not isinstance(num, (int)) or num < 2:

            print("请输入大于1的整数")

            return self

        if self.terms == None or len(self.terms) == 0:

            return self

        s = self.__clone()

        for i in range(num-1):

            s = s.mul(self)  # 由于mul会克隆,这里不用克隆

        return s


    #2项式倒数,使用平方差公式

    def __rec2(self):

        d = self.terms[0].square() - self.terms[1].square()  #分母

        self.terms[1].opp()                                  #第二项取相反数

        return self.__muldiv(d, True)


    #除

    def div(self, *terms):

        if terms == None or len(terms) == 0:  #空调用返回自己

            return self

        if len(terms) == 1 and isinstance(terms[0],(int, Fraction, str)):

            return self.__clone().__muldiv(terms[0], True)

        pq = PR(*terms)

        if pq.terms == None or len(pq.terms) == 0:

            return self

        elif len(pq.terms) == 1:

            return self.__clone().__mul(pq.terms[0].rec())

        elif len(pq.terms) == 2:                       #只处理2项式

            return self.mul(pq.__rec2())     #2项式倒数

        else:

            raise ValueError("除数多项二次根式超过了2项,超出本模块处理范围!")            

        


    #构造

    def __init__(self, *terms):

        if terms == None or len(terms) > 0:

            self.terms = []

            for term in terms:

                if isinstance(term, (QR)):

                    self.terms.append(term.clone())

                elif isinstance(term, (PR)):

                    if term.terms != None and len(term.terms) > 0:

                        for t in term.terms:

                            self.terms.append(t.clone())

                else:

                    self.terms.append(QR(*term)) #解包参数

                    

            PR.__sort(self.terms)  #排序合并同类项,按底数从大到小

    

   



if __name__ == "__main__":


    print("\n二次根式测试:")

    print("\n导入二次根式模块:")

    print('>>>from quadraticradical import *')

    print("\n构造函数测试:")

    print('>>>_qr = QR("3/2", 32)')

    print('>>>_pr = PR(["3/5", 8])')

    print('>>>_test = PR(["-3.14", 128], _qr, _pr, [4])')

    print('>>>_test.pt()')

    _qr = QR("3/2", 32)

    _pr = PR(["3/5", 8])

    _test = PR(["-3.14", 128], _qr, _pr, [4])

    _test.pt()

    print("对比")

    print('>>>_test = PR(["-3.14", 128], ["3/2", 32], ["3/5", 8], [4])')

    print('>>>_test.pt()')

    _test = PR(["-3.14", 128], ["3/2", 32], ["3/5", 8], [4])

    _test.pt()

    print("\n减多项二次根式测试:")

    print('>>>_test.sub(PR(["3/2",162], ["3/4",27])).pt()')

    _test.sub(PR(["3/2",162], ["3/4",27])).pt()

    print("\n加多项二次根式测试:")

    print('>>>_test.add(["2.5", 125], PR(["7.5", 45])).pt()')

    _test.add(["2.5", 125], PR(["7.5", 45])).pt()

    print("\n乘多项二次根式测试:")

    print('>>>_test.mul([1,2],[3, 5]).pt()')

    _test.mul([1,2],[3, 5]).pt()    

    print("\n多项二次根式除以常数测试:")

    print('>>>_test.div("-1/2").pt() ')    

    _test.div("-1/2").pt()

    print("\n多项二次根式乘方测试:")    

    print('>>>PR([1,2],[1,3]).pow(5).pt()')

    PR([1,2],[1,3]).pow(5).pt()

    print("\n多项二次根式除以一项二次根式测试:")

    print('>>>_test.div(["-1/2", 8]).pt() ')    

    _test.div(["-1/2", 8]).pt()     

    print("\n多项二次根式除以二项二次根式测试:")

    print('>>>_test.div(["-1/2", 8],[1]).pt() ')  

    _test.div(["-1/2", 8],[1]).pt()



附录4:
#开方符合书写,只能从左到右书写
def writeSqrt(aTurtle, base, index=2, pensize=2, font=("Arial",17,"normal")):
    '''
    @aTurtle 一个海龟
    @base 底数
    @index 指数
    @pensize 画根号的笔大小
    @font 字体
    '''
    #保持海龟原来的参数后设置参数
    speed = aTurtle.speed()
    aTurtle.speed(0)
    isdown = aTurtle.isdown()
    pos = aTurtle.pos()
    old_pensize=aTurtle.pensize()

    if isdown:
        aTurtle.up()

    #右移1字符写底数
    aTurtle.setx(pos[0]+font[1])
    aTurtle.write(base,  move=True, font=font)
    pos2=aTurtle.pos()

    #写指数
    if index != 2:
        aTurtle.setpos(pos[0]+font[1]*0.75,pos[1]+font[1]*0.5)
        aTurtle.write(index, align="right", font=("Arial",font[1]//2,"normal"))
        
    #原位右移1/4格画根号
    aTurtle.pensize(pensize)
    aTurtle.setpos(pos[0]+font[1]*0.25,pos[1]+font[1]*0.25)
    aTurtle.down()
    aTurtle.setpos(pos[0]+font[1]*0.5,pos[1]+font[1]*0.5)
    aTurtle.setpos(pos[0]+font[1]*0.75,pos[1]+font[1]*0.25)
    aTurtle.setpos(pos[0]+font[1],pos[1]+font[1]*1.4)
    aTurtle.setpos(pos2[0],pos2[1]+font[1]*1.4)
    aTurtle.up()


    #恢复
    aTurtle.speed(speed)
    aTurtle.setpos(pos2)
    aTurtle.pensize(old_pensize)
    if isdown:
        aTurtle.down()  

if __name__ == "__main__":

    import turtle as t

    t.setup(200,200)
    t.screensize(180,180)

    t.shape("turtle")
    t.up()
    
    writeSqrt(t,3)
    t.sety(50)
    writeSqrt(t,-56,-3,font=("Arial",20,"normal"))
    t.ht()
    t.mainloop()    
    

粤ICP备11092478号