飞诗(fsxm)
发表于 2007-7-23 22:02:00
本帖最后由 作者 于 2007-7-23 22:04:58 编辑 <br /><br /> <p>呵呵~我只是写了一个处理有优化级的函数一个例子~</p><p>sin之类的没有优化级别更好处理~</p><p>只要提前将sin(XXX)用字符处理成(sin XX)就行了~用不到表处理`!</p><p>字符替代 "sin(" -> "(sin"</p>
狂刀无痕
发表于 2007-7-24 15:07:00
本帖最后由 作者 于 2007-7-24 20:20:14 编辑 <br /><br /> <p>;| xcal = 计算字符串表达式------- by lxx.2007.7.22<br/>格式: (xcal 运算过程式 返回值表达式)<br/>返回: 实数 (可用rtos提取)<br/>命令: xxcal<br/>实例:<br/>(xcal "" "1e+2*5^3*exp(5.31)" )<br/>-> 2.52938e+006<br/>(xcal "a=1e+2*5^3*exp(5.31):" "a" )<br/>->2.52938e+006<br/>(rtos (xcal "a=1e+2*5^3*exp(5.31):" "a" ) 2 10)<br/>-> "2529377.854851844"<br/>(xcal "a=3 : for i = 1 to 100000 : a=a+i+5^3*exp(5.31) : b=a^2*sin(a) : c=sqr(a)*b : next :" "c" )<br/>-> 1.59322e+021 ;;返回 c值,10万次计算,1~2秒<br/>版本:<br/>v1.0 2007.7.22<br/>|;</p><p> 命令行方式演示,重复运算100万次, <1秒得结果.</p><p></p><p>其实计算不是lisp的强项。用c系列语言,甚至vb都快得多。上面的方法是在lisp中调用vb计算。C我不懂,肯定能更快。大家继续。。。</p><p> </p>
飞诗(fsxm)
发表于 2007-7-24 20:46:00
本帖最后由 作者 于 2007-7-24 20:46:58 编辑 <br /><br /> <p></p><p>;;;分离出变量与函数<br/>(defun format1 (str / char funs lastfun lst tmp lastchar)<br/> (setq funs '("+" "-" "*" "/" "^" "%" "(" ")" " "))<br/> (setq tmp "")<br/> (while (/= str "")<br/> (setq char (substr str 1 1))<br/> (setq str (substr str 2))<br/> (if (and (member char funs)<br/> ;;负号特别处理<br/> (not (and lastfun (/= lastfun ")") (= char "-")))<br/> ;;"e"科学计数法特别处理<br/> (not (and lastchar (or (= char "-") (= char "+"))))<br/> )<br/> (setq lst (vl-list* char tmp lst)<br/> tmp ""<br/> lastfun char<br/> lastchar nil<br/> )<br/> (setq tmp (strcat tmp char)<br/> lastfun nil<br/> lastchar (if (= (strcase char) "E") t)<br/> )<br/> )<br/> )<br/> (vl-remove "" (vl-remove " " (reverse (cons tmp lst))))<br/>)<br/>;;;处理简单无优先级别函数运算<br/>(defun format1_1 (lst funs / fun lasta nlst tmp)<br/> (foreach a lst<br/> (cond<br/> ((setq tmp (assoc (strcase a) funs))<br/> (setq fun (cadr tmp))<br/> )<br/> ((and (= a "(") fun)<br/> (setq nlst (vl-list* fun "(" nlst))<br/> (setq fun nil)<br/> )<br/> ((and (= a "(")<br/> (not (member lasta '(nil "+" "-" "*" "/" "^" "%" "(" ")")))<br/> )<br/> (setq nlst (vl-list* lasta "(" (cdr nlst)))<br/> )<br/> (t (setq nlst (cons a nlst)))<br/> )<br/> (setq lasta a)<br/> )<br/> (reverse nlst)<br/>)<br/>;;;带return的apply<br/>(defun Fsxm-Apply ($Sym $Lst / $$ return $rt)<br/> (defun Return (var) (setq Return nil) (setq $$ var) (exit))<br/> (setq $rt (vl-catch-all-apply $Sym $Lst))<br/> (if Return $rt $$)<br/>)<br/>;;递归处理括号<br/>(defun format2 (lst / a i lst2 nlst tmp var)<br/> (setq i 0)<br/> (while lst<br/> (setq a (car lst))<br/> (setq lst (cdr lst))<br/> (setq i (1+ i))<br/> (cond ((= a "(")<br/> (setq var (fsxm-apply 'format2 (list lst)))<br/> (repeat (car var) (setq lst (cdr lst)))<br/> (setq i (+ i (car var)))<br/> (setq nlst (cons (cadr var) nlst))<br/> (setq tmp (cons (cadr var) tmp))<br/> )<br/> ((= a ")")<br/> (return (list i (reverse tmp)))<br/> )<br/> (t<br/> (setq tmp (cons a tmp))<br/> (setq nlst (cons a nlst))<br/> )<br/> )<br/> )<br/> (reverse nlst)<br/>)<br/>;;递归转化计算式格式<br/>(defun format3 (lst funs / lasta nlst tmp fun)<br/> (foreach a lst<br/> (cond ((setq fun (assoc a funs))<br/> (setq tmp (list lasta (cadr fun)))<br/> )<br/> (t<br/> (if (listp a)<br/> (setq a (format3 a funs))<br/> )<br/> (if tmp<br/> (setq lasta (reverse (cons a tmp))<br/> nlst (cons lasta (cdr nlst))<br/> tmp nil<br/> )<br/> (setq lasta a<br/> nlst (cons lasta nlst)<br/> )<br/> )<br/> )<br/> )<br/> )<br/> (reverse nlst)<br/>)<br/>;;递归处理掉多余的括号,<br/>;;常量str->浮点数real 变量str->符号sym<br/>(defun format4 (lst)<br/> (mapcar '(lambda (a / x)<br/> (cond ((listp a)<br/> (if (listp (car a))<br/> (format4 (car a))<br/> (format4 a)<br/> )<br/> )<br/> ((= (type a) 'str)<br/> (or (setq x (distof a))<br/> (setq x (read a))<br/> )<br/> x<br/> )<br/> (t a)<br/> )<br/> )<br/> lst<br/> )<br/>)<br/>(defun trans_format (str / lst)<br/> ;;预处理 去空字符&转括号<br/> (setq str (vl-string-translate "{[]}\t\n," "(()) " str))<br/> ;;分离出变量与函数<br/> (setq lst (format1 str))<br/> ;;处理无优先级别函数运算<br/> (setq lst (format1_1 lst '(("COS" cos2) ("SIN" sin2) ("TAN" tan2))))<br/> ;;递归处理括号<br/> (setq lst (format2 lst))<br/> ;;优先计算 开方<br/> (setq lst (format3 lst '(("^" expt))))<br/> ;;再次计算 乘 除 取模<br/> (setq lst (format3 lst '(("*" *) ("/" /) ("%" rem))))<br/> ;;最后计算 加减<br/> (setq lst (format3 lst '(("+" +) ("-" -))))<br/> ;;后处理<br/> (car (format4 lst))<br/>)</p><p>(defun sin2 (d)<br/> (sin (* d (/ pi 180)))<br/>)<br/>(defun cos2 (d)<br/> (cos (* d (/ pi 180)))<br/>)<br/>(defun tan2 (d)<br/> (setq d (* d (/ pi 180)))<br/> (/ (sin d) (cos d))<br/>)<br/>;;====================功能测试1:====================<br/>(setq str1 (strcat "(1/(cos(-2)*-3)+"<br/> "min(22,abs(-5),0.5,8)"<br/> "*(2-5))/3^(sin(pi/5)+2)-1e+2*5"<br/> )<br/>)<br/>(eval (trans_format str1)) ;-> -500.201<br/>(eval (trans_format "min(22 , abs(-5) , 0.5 , 8)")) ;-> 0.5<br/>;;因min(22,abs(-5),0.5,8) -> 0.5 现在用cal验证结果<br/>(setq str2 "(1/(cos(-2)*-3)+0.5*(2-5))/3^(sin(pi/5)+2)-1e+2*5")<br/>(c:cal str2) ;-> -500.201</p><p>;;功能测试通过</p><p><br/>;;====================效率测试====================<br/>;;计时子函数<br/>(defun time0 () (setq t0 (getvar "TDUSRTIMER")))<br/>(defun time1 ()<br/> (princ "用时:")<br/> (princ (* (- (getvar "TDUSRTIMER") t0) 86400))<br/> (princ "(S)")<br/> (princ)<br/>)<br/>(setq str "(1/(cos(-2)*-3)+0.5*(2-5))/3^(sin(pi/5)+2)-1e+2*5")<br/>(defun c:t1 (/ t0) ;用CAL对比<br/> (time0)<br/> (repeat 5000 (cal str))<br/> (time1)<br/>)<br/>(defun c:t2 (/ t0) ;多次eval+多次trans_format(比cal慢)<br/> (time0)<br/> (repeat 5000 (eval (trans_format str)))<br/> (time1)<br/>)<br/>(defun c:t3 (/ t0) ;多次eval+1次trans_format(与cal差不多)<br/> (time0)<br/> (setq trans_lst (trans_format str))<br/> (repeat 5000 (eval trans_lst))<br/> (time1)<br/>)<br/>(defun c:t4 (/ t0 test) ;1次eval+1次trans_format(比cal快)<br/> (time0)<br/> (eval (list 'defun 'test nil (trans_format str)))<br/> (repeat 5000 (test))<br/> (time1)<br/>)</p><p>后语:<br/>事实上基于字符解释与表处理的trans_format<br/><font color="#3809f7">它的单次运算效率是不如cal的,约比cal慢20倍</font><br/>但是因为它的最终解释结果为lisp表达式,<br/><font color="#0909f7">在很多次运算"解释结果"时,运算效率将比cal快10倍以上</font><br/><font color="#ff0033">它开放式的程式构架,赋予了很多cal函数所没有功能.比如:<br/>trans_format可以运算"自定义函数"也可以运算"变量",还可以自定义"运算子"的优先级别</font><br/>-----fsxm2007.07.23~2007.07.24</p><p>希望以上程序代码能对各位有点帮助!</p>
highflybir
发表于 2007-7-25 09:33:00
本帖最后由 作者 于 2007-7-25 10:51:34 编辑
看来飞诗已经研究出来了,佩服佩服!
先下载下来学习学习。
建议,函数不应局限于lisp中的有限几个,可以扩展一些。
感觉速度上应该还可以提高。
关于为什么要转换,请大家看下面的例子:就是平方运算:
;;;平方表达式的lisp运算和cal运算。
(defun C:test(/ cal-express lisp-express)
(arxload "Geomcal.arx")
(setq cal-express "x^2")
;;假设我已经准确地翻译"X^2"成如下函数
(setq lisp-express (read "(* x x)"))
;;则
(eval
(list 'defun 'lisp-sqr (list 'x)
lisp-express
)
)
(defun cal-sqr (x)
(cal cal-express)
)
;;lisp
(time0)
(setq i 0)
(repeat 10000
(lisp-sqr i)
(setq i (1+ i))
)
(time1)
;;cal
(time0)
(setq i 0)
(repeat 10000
(cal-sqr i)
(setq i (1+ i))
)
(time1)
)
实际结果发现对于是一个变量的表达式子,如果经lisp翻译后(假设这种翻译是完全准确而且不冗余的)的运算速度可能要比cal快100倍以上。这还是没有经过编译的。
;;计时子函数
(defun time0 () (setq t0 (getvar "TDUSRTIMER")))
(defun time1 ()
(princ "用时:")
(princ (* (- (getvar "TDUSRTIMER") t0) 86400))
(princ "秒")
(princ)
)
狂刀无痕
发表于 2007-7-25 14:41:00
本帖最后由 作者 于 2007-7-25 15:09:20 编辑
要说表处理和cad更容易结合,我没话说.但是说比cal快那我得说点不同意见.
cal也是arx(c系列语言)编写的.怎么可能慢.
上面的测试本来就是个错误.为什么这么说.单就计算本身(比如1次运算),cal快是没画说.但是为什么多次运算又慢了呢?根本不是计算的效率问题.而是程序的结构问题.是lisp把它拖慢的.因为没计算一次,数据传给计算函数计算,结果传回lisp,慢就慢在传出传入上.是错误的应用方法.发挥不了优势. 要慢也不是因为cal慢.
就是说,如果用cal或其它用vb,vc等编写的函数或模块来进行大量运算,应该把要计算的条件一次传入,计算(包括多次循环)完了,再一次性把结果传回lisp.
因此,对简单的计算.用lisp或其它都无所谓,时间感觉不出来.
要进行大量运算.就放手让外部计算程序计算.一次出结果.不要把时间花在lisp的数据转换上面.;;;平方表达式的lisp运算和cal运算。
(defun C:test (/ cal-express lisp-express)
(arxload "Geomcal.arx")
(setq cal-express "x^2")
(setq lisp-express (read "(* x x)"))
(defun cal-sqr (x)
(cal cal-express)
)
(eval (list 'defun'lisp-sqr(list 'x)lisp-express ))
(time0)
(setq i 0)
(repeat 10000
(setq end (cal-sqr i))
(setq i (1+ i))
)
(time1)
(print (rtos end 2 8))
(princ)
)
;;; 对比测试:
(defun c:xx ()
(setq n(getdist "\n 输入运算次数:"))
(time0)
(setq end
(xcal (strcat "a=0 : for i = 1 to "
(itoa (fix n))
" : a=i^2 : next :") "a" ));; 可在12楼下载本函数
(time1)
(print (rtos end 2 8))
(princ)
)
测试结果:命令: test
用时:11.984秒
"99980001.00000002"
命令:
命令: xx
输入运算次数:10000
用时:0.016秒
"100000000.0000000"
命令:
命令: xx
输入运算次数:1000000
用时:1.656秒
"1000000000000.000"
命令:
命令:
XX
输入运算次数:8000000
用时:11.609秒
"64000000000000.00"
也就是说,用xcal函数(调用了vba) 800万次运算和用lisp1万次运算用时相当.而且越到后面每次运算时间需要更多, 因为是 (* x x)
如果都是1万次运算.11.984秒 秒和0.016秒有多大差别,不用说了吧....
另外,test的测试结果有误差(几次测试都是),可能是浮点运算造成,楼上可再检查一下
highflybir
发表于 2007-7-25 17:05:00
回楼上的,我觉得应该这样做比较:
(注意是:lisp-sqr,而不是cal-sqr)
;;;平方表达式的lisp运算和cal运算。
(defun C:test (/ i lisp-express)
(setq n(getreal "\n 输入运算次数:"))
(setq lisp-express (read "(* x x)"))
(eval
(list 'defun
'lisp-sqr
(list 'x)
lisp-express
)
)
(time0)
(setq i 0.0)
(repeat (fix n)
(setq end1 (lisp-sqr i))
(setq i (1+ i))
)
(time1)
(print (rtos end1 2 8))
(princ)
)
;;; 对比测试:(
(defun c:xx ()
(setq n(getreal "\n 输入运算次数:"))
(time0)
(setq end1
(xcal (strcat "a=0 : for i = 1 to "
(itoa (fix n))
" : a=i^2 : next :"
)
"a"
)
)
;; 可在12楼下载本函数
(time1)
(print (rtos end1 2 8))
(princ)
)
;;计时子函数
(defun time0 () (setq t0 (getvar "TDUSRTIMER")))
(defun time1 ()
(princ "\n用时:")
(princ (* (- (getvar "TDUSRTIMER") t0) 86400))
(princ "秒")
(princ)
)
把这个程序编译成vlx后发现两者在1000000次以内基本打平。不信大家去测试.
highflybir
发表于 2007-7-25 17:20:00
<p>下面是一个一亿次的运算测试:</p><p>命令:<br/>命令: test<br/> 输入运算次数:100000000</p><p>用时:86.203秒<br/>"9999999800000001"</p><p>命令:<br/>命令: xx<br/> 输入运算次数:100000000</p><p>用时:59.093秒<br/>"1.00000000E+16"</p><p>可见在亿次上才可能看见差别。然而这个差别不大,不是数量级的差别。</p><p>至于为什么后面返回的结果不同,也许大家能猜得到。<br/></p>
狂刀无痕
发表于 2007-7-25 20:27:00
本帖最后由 作者 于 2007-7-25 20:32:35 编辑 <br /><br /> <p>不得不说,编译我vlx后lisp的速度确优化了不少.因为vlx被编译为一种介lisp和机器码之间的编码,所以速度提高.</p><p>虽然不是数量级的差别,但是也不会基本打平拉 (我的电脑cpu是 amd3200 内存512兆),另外,连10次运算,在100内都有19的的误差,不知道哪个敢用.</p><p>TEST<br/> 输入运算次数:10</p><p>用时:0.0秒<br/>"81.00000000"</p><p>命令:<br/>命令: xx</p><p> 输入运算次数:10</p><p>用时:0.0秒<br/>"100.00000000"</p><p>命令: test<br/> 输入运算次数:1e4<br/>用时:0.031秒<br/>"99980001.00000000"<br/>命令: xx<br/> 输入运算次数:1e4<br/>用时:0.016秒<br/>"100000000.0000000"<br/>命令:<br/>命令: test<br/> 输入运算次数:1e6<br/>用时:3.563秒<br/>"999998000001.0001"<br/>命令:<br/>命令: xx<br/> 输入运算次数:1e6<br/>用时:1.297秒<br/>"1000000000000.000"<br/>命令:<br/>命令: test<br/> 输入运算次数:1e6<br/>用时:3.672秒<br/>"999998000001.0001"<br/>命令:<br/>命令: xx<br/> 输入运算次数:1e6<br/>用时:1.578秒<br/>"1000000000000.000"<br/>命令:<br/>命令: test<br/> 输入运算次数:1e7<br/>用时:36.5秒<br/>"99999980000001.00"<br/>命令:<br/>命令: xx<br/> 输入运算次数:1e7<br/>用时:13.984秒<br/>"100000000000000.0"</p><p>命令: test</p><p> 输入运算次数:1e8</p><p>用时:433.89秒<br/>"9999999800000001"</p><p>命令:<br/>XX<br/> 输入运算次数:1e8</p><p>用时:160.031秒<br/>"1.00000000E+16"</p>
飞诗(fsxm)
发表于 2007-7-25 21:11:00
本帖最后由 作者 于 2007-7-25 22:07:54 编辑 <br /><br /> <p>我们不要只是简单的说运行的快慢~</p><p>效率是在实际应用上体现出来的! 如果没有用的"快"远不如有用的"慢"</p><p>现在给一个应用例子:</p><p>y = x^3+x*x+5x+cos(x+pi/5)+0.5</p><p>现在用CAD画这个方程线: 取值0<=X<=100 步进为1e-4</p><p>用trans_format运算一次 转化为lisp表达式计算,</p><p>只要给出不同的x就可以算出对应的y,</p><p>cal/vbs呵呵你总不可以"a=* : for i = 1 to 10000...."来计算吧!</p><p>就是一个变量就足以说明与实际应用结合之后的效率问题,</p><p><font color="#ff0033">不管你有多快~如果不能用"变量"快了也没用! 用了"变量"后,这下看你还快不快!!</font></p><p>如果没有一个"变量"计算多次的结果还是一样的,去计算N次也就没有意义了哦!</p><p></p><p><font face="宋体" size="3"><strong><font face="Verdana" color="#da2549">highflybir:</font></strong></font><font face="宋体" size="3">建议,函数不应局限于lisp中的有限几个,可以扩展一些。</font></p><p><font face="宋体" size="3">回: 谢谢提议,不过现在程序就是可以运算自定义函数的啊!</font></p><p><font face="宋体" size="3"></font></p><p><font face="宋体" size="3">来个简明的:</font></p><p><font face="宋体" size="3">(defun test (a b)<br/> (+ (* a b) (/ a b))<br/>)</font></p><p><font face="宋体" size="3">_$ (eval (trans_format "test(5,6)"))<br/>30.8333</font></p><p><font face="宋体" size="3"></font></p>
highflybir
发表于 2007-7-26 09:47:00
狂刀无痕发表于2007-7-25 20:27:00static/image/common/back.gif不得不说,编译我vlx后lisp的速度确优化了不少.因为vlx被编译为一种介lisp和机器码之间的编码,所以速度提高.虽然不是数量级的差别,但是也不会基本打平拉 (我的电脑cpu是 amd3200 内存512兆),另外
<p></p><p>对于1百万次的运算才相差一秒,我认为这个结果是可以接受的。而且用lisp语言就能完成这个功能,而不用其他语言,所以我认为fxsm的程序是有效的。</p><p>至于你说的误差,我想也许你可能没弄清楚lisp的递增加法和vb的不同之处。-这并不是lisp的计算出现失误,相反我认为是正确的,你得出的最后结果反而是错误的。</p><p>因为我们知道:计算机计数是从0开始,假设要重复运算10次,那么实际上第10次平方应该是9X9=81,而不是10X10=100,</p><p>这就是为什么在10次以内存在19的差距。lisp是先求值后计数器加1。(当然你也可以改变这流程)</p><p>在我的电脑上是持平的,也许电脑可能有差别,我的电脑是1G内存,3.0G intel pentium D。不同的cpu对浮点运算是有区别的。</p>