#coding=utf-8
导入sympy;
import sympy
sympy是一个第三方库,请先打开你的cmd(命令行command)窗口运行:
pip install sympy
再导入Python自带的库math
from math import *
具体的原因:
sympy是一个很“任性”的库,经常返回一个公式。比如:sqrt(5)*7
那么我们画图是需要一个数值的,所以我们用math库里的函数计算。
我们首先来明确一下实现的过程:
我们让电脑读懂方程似乎有一些困难,所以我们可以进行一个暴力模拟。把一个数丢进去不断推演,最后得知n个答案,把所有答案的坐标点连起来, 实现一个模型。
需要的变量:
x=[]
y=[]
z=[]
t=[]
这些列表用于储存坐标点。
x:横轴
y:纵轴
z:前后
t:模拟一个时间的变化。
比如:例式x=y=z=t得出的结果应该是一个移动向斜上的点。
原因:x轴、y轴、z轴都相等,随着时间(自增量t)的变大,xyz也会变大,坐标点会不断向斜上移动。
然后定义一个函数,用于计算具体值
def write(formula, min_=-10, max_=10, nums=100,printout=True):
formula是公式;
min_是模拟最小值;
max_是模拟最大值;
nums是模拟数量;
printout是是否打印进度条的布尔参数。
注:
有人问过我为什么不用numpy(numpy.linespace可以生成一个等差数列),原因其实也很简单:
因为有一些运算参数numpy的linespace无法进行运算,所以就没有使用。
(而且,numpy库太大了,打包成exe时不方便【手动狗头Doge】)
接下来,为了方便起见,函数不设置返回值,而是直接写入到前面定义的x,y,z,t四个列表里。
def write ...#省略参数部分
global x,y,z,t
接下来定义一些变量:
I=1j
oo=inf
step=(max_-min_)/nums
a=min_-step
solves=[]
解释:
1.
I=1j
sympy自带的虚数。对于方程x^2+1=0的解,sympy给出的是1*I;但是,如果对其进行运算的话,sympy在虚(复)数的运算上似乎有些确实。于是,Python的complex类型(复数类型)就起了作用。
sqrt(-1)=I=1j。所以,定义I=1j,在sympy返回的字符串表达式中使用eval函数进行再次计算取值。
2.
oo=inf
sympy里的无限就是oo。将math库里的inf(infinity,正无穷)赋给它。
3.
定义累加器a,每次加的值step。
step相当于每循环一次所加的值。
a是从min_开始的,但是因为第一次循环还会先加上一个step,所以定义a=min_-step。
def write(formula, min_=-10, max_=10, nums=100,printout=True):
global x, y, z, t
I=1j
oo=inf
step=(max_-min_)/nums
a=min_-step
r"""
lf,un = legalSet(toLeg(formula))
print(un)
print(lf,toLeg(formula),sep="\n\n",end="\n\n")
"""
solves=[]
(目前代码)
接下来需要新定义一个函数,叫做legalSet。
原因:
通过对t一次次的赋值得到的结果会形成一个新的方程。例,x=y=z=t就可以改为x=y=z=(具体数值),所以实际上formula就变成了一(N)个方程。所以,使用sympy的solve函数求解。
solve函数有它的特殊要求:
1)除号只有“/”,即Python法定除号。(比号“:”、除号“÷”都不行)
2)乘号只有“*”且不可省略。(其他的写法例如“x”、“×”不行,而且a乘b必须写成a*b,不能写成ab)
3)方程的结果必须等于0,省略等号。所以拖等式是无法计算的。
…………
但是,我们写的方程自然不会写成这样(麻烦死了)所以我们需要用程序进行修改。
第一个函数叫做Legalset,将以换行符分割的方程转换为一个符合要求的方程。
def legalSet(fx):
...
第一步,因为可能有许多方程,所以首先将它们以换行符("\n"是转义字符,就表示按一下<enter>后的换行符)分隔成列表。
def legalSet(fx):
v=str(fx).split("\n")
定义一个临时变量nw对v进行初步处理再赋值给v。因为nw在赋值完之后已经没有用了,所以删掉。
nw=[]
for l in v:
tmpstr=""
if l=='':continue #滤除空行
k=l.split("=")
tmpstr+="(("+k[0]+")-("+k[1]+"))"#等号两边值相等,左边减右边就是结果为0的值。
nw.append(tmpstr)
v=nw
del nw
然后转换字符:
index=-1
for i in v:
index+=1
if ":" in i: v[index] = i.replace(":", "/")
if "^" in i: v[index] = i.replace("^", "**")
if "÷" in i: v[index] = i.replace("÷", "/")
if "×" in i: v[index] = i.replace("×", "*")
if "[" in i: v[index] = i.replace("[", "(")
if "]" in i: v[index] = i.replace("]", ")")
if "【" in i: v[index] = i.replace("【", "(")
if "】" in i: v[index] = i.replace("】", ")")
if "{" in i: v[index] = i.replace("{", "(")
if "}" in i: v[index] = i.replace("}", ")")
if "(" in i: v[index] = i.replace("(", "(")
if ")" in i: v[index] = i.replace(")", ")")
接下来的代码是添加省略的乘号并获取每一行方程的所有未知数。
newlt=[]
lt=[]
for j in v:
new=""
pos=-1
lt__=[]
funcs=[]
for i in j:
pos+=1
tmpos=pos+0
string=""
while (j[tmpos].isalpha()):
string+=j[tmpos]
tmpos=tmpos+1
if string in sympy.__all__ :
funcs+=[i for i in range(pos,tmpos+1)]
if ((i.isalpha() or i == "(") and (j[pos-1].isnumeric() or j[pos-1].isalpha())
and pos!=0 and pos not in funcs):
new+="*"+i
else:
new+=i
if i.isalpha() and pos not in funcs:lt__.append(i)
lt__=list(set(lt__))
lt.append(lt__)
newlt.append(str(new))
return newlt,lt
返回值是一个tuple类型,包含:
(合法公式列表,每行公式对应的未知数)
(修改内容|此处是修改部分)
在试验方程时,发现部分方程可能在求解时遇到问题,legalSet函数的代码应当“全面”一点:
def legalSet(fx):
v=str(fx).split("\n")
nw=[]
for l in v:
tmpstr=""
tmpstr2=""
if l=='':continue
k=l.split("=")
tmpstr+="(("+k[0]+")-("+k[1]+"))"
tmpstr2 += "((" + k[1] + ")-(" + k[0] + "))"
tmpstrs=[tmpstr,tmpstr2]
nw.append(tmpstrs)
v=nw
del nw
index=-1
for i in v:
index+=1
for j in range(2):
if ":" in i: v[index][j] = i[j].replace(":", "/")
if "^" in i: v[index][j] = i[j].replace("^", "**")
if "÷" in i: v[index][j] = i[j].replace("÷", "/")
if "×" in i: v[index][j] = i[j].replace("×", "*")
if "[" in i: v[index][j] = i[j].replace("[", "(")
if "]" in i: v[index][j] = i[j].replace("]", ")")
if "【" in i: v[index][j] = i[j].replace("【", "(")
if "】" in i: v[index][j] = i[j].replace("】", ")")
if "{" in i: v[index][j] = i[j].replace("{", "(")
if "}" in i: v[index][j] = i[j].replace("}", ")")
if "(" in i: v[index][j] = i[j].replace("(", "(")
if ")" in i: v[index][j] = i[j].replace(")", ")")
newlt=[]
lt=[]
for j in v:
lt__=[]
funcs=[]
news = []
for i_ in j:
new = ""
pos = -1
for i in i_:
pos+=1
tmpos=pos+0
string=""
while (i_[tmpos].isalpha()):
string+=i_[tmpos]
tmpos=tmpos+1
if string in sympy.__all__ :
funcs+=[i for i in range(pos,tmpos+1)]
if ((i.isalpha() or i == "(") and (i_[pos-1].isnumeric() or i_[pos-1].isalpha())
and pos!=0 and pos not in funcs):
new+="*"+i
else:
new+=i
if i.isalpha() and pos not in funcs:lt__.append(i)
news.append(new)
lt__=list(set(lt__))
lt.append(lt__)
newlt.append(news)
return newlt,lt
主要改动:将单方程“a-b”改为列表性双方程“a-b”,“b-a”,思路大体一致,讲解略。
接下来是拖等式转多行单等式,上代码:
def toLeg(fx):
v=str(fx).split("\n")
val=[]
for i in v:
tv=i.split("=")
length=len(tv)
last=None
while length!=0:
if length==2:
val.append('='.join(tv))
break
if length==1:
val.append('='.join([tv[0],str(last)]))
break
val.append('='.join([tv[-2],tv[-1]]))
length=length-1
last=tv[-1]
tv.pop()
return '\n'.join(val)
这其实就是一个普通的Python代码。
通过计算等号的项数再两两搭配。
接下来,基本的转换函数就写好了。
关于它的调试部分在前面write函数的前部分有(用三引号标为注释了,运行时去掉就好)
今天再写上最后一个函数。
把最前面的import语句升级一下:
import sympy
from tkinter.messagebox import *
from tkinter import *
from math import *
接下来要编写的是当方程没有解析解的时候所显示的错误:
def no_ans():
Tk().geometry("9x9+990000+999999")
showerror("警告", "方程无解析解")
注:
1)showerror是tkinter.messagebox里面的函数,参见:
Python Tkinter:messagebox_Unconquerable&Llxy的博客-优快云博客tkinter messagebox,消息显示窗口,快捷方便https://blog.youkuaiyun.com/html_finder/article/details/1248294932) Tk().geometry("9x9+990000+999999")是让附带的窗口消失。
下一篇参见:
-------------------------------------完------------------------------------------------------------------------------
这里是Unconquerable&Llxy,原名html_finder