明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
楼主: chlh_jd

lisp应用递归算法10题

    [复制链接]
 楼主| 发表于 2010-9-25 23:04:00 | 显示全部楼层
本帖最后由 chlh_jd 于 2012-7-8 02:27 编辑

<p>以上10题,伪答案已经陆陆续续都有,大家有没有心得看法——到底什么时候使用递归更高效</p>
<p>(递归不一定是要定义一个递归函数,使用while也可以实现函数内部递归)</p>
<p>欢迎多提些自己的想法</p>
<p>接着这个贴子和大家一起讨论更复杂更高效一点的递归算法(如交叉递归或者说相互嵌套递归或者说结合动态规划的嵌套)在LISP的应用</p>
<p></p>
<p>&nbsp;</p>
<p>&nbsp;</p>

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
 楼主| 发表于 2010-9-26 22:31:00 | 显示全部楼层
漏了一贴,现在补上
remove-nth,我把qj-chen老师的代码延伸了下,适合矩阵等二维表使用(为嵌套递归做准备)
  1. ;;by GSLS(SS)
  2. ;;;删除(nth n lst)项,返回新表
  3. ;;;如n为列表(i j)则删除i行j列
  4. (defun remove-nth (n lst)
  5.   (if (numberp n)
  6.     (if (zerop n)
  7.       (cdr lst)
  8.       (cons
  9. (car lst)
  10. (remove-nth
  11.    (1- n)
  12.    (cdr lst)
  13. )
  14.       )
  15.     )
  16.     (mapcar (function (lambda (x)
  17.    (remove-nth (cadr n) x)
  18.    )
  19.         )
  20.      (remove-nth (car n) lst)
  21.      )
  22.   )
  23. )
 楼主| 发表于 2010-9-27 00:32:00 | 显示全部楼层
本帖最后由 作者 于 2010-9-27 15:28:39 编辑

下面举个例子来说明下递归效率:
编写一个叫cartesian的函数,
功能:> (cartesian '(1 2 3) '(4 5))  ((1 . 4) (1 . 5) (2 . 4) (2 . 5) (3 . 4) (3 . 5))
分别采用3种写法
方法1:嵌套递归
  1. ;;by GSLS(SS)
  2. ;;;要求使用嵌套递归函数完成
  3. (defun stitch (l2 e)
  4.   (cond ((null (cadr l2))
  5.   (list (cons e (car l2))))
  6. (t (cons (cons e (car l2)) (stitch (cdr l2) e))))  
  7.    )
  8. (defun cartesian (l1 l2)  
  9.    (cond ((null (cadr l1))
  10.    (stitch l2 (car l1))
  11.    )
  12.   (t (append (stitch l2 (car l1)) (cartesian (cdr l1) l2)))
  13.   )
  14.   )
方法二:使用while循环递归
  1. ;;;by GSLS(SS)
  2. ;;;使用while递归
  3. (defun cartesian0 (l1 l2 / lst a b l3)
  4.   (setq lst nil)
  5.   (while (setq a (car l1))
  6.     (setq l1 (cdr l1)
  7.    l3 l2)
  8.     (while (setq b (car l3))
  9.       (setq l3 (cdr l3))
  10.       (setq lst (cons (cons a b) lst))
  11.       )
  12.     )
  13.   (reverse lst)
  14.   )
方法三:使用mapcar加临时函数遍历
  1. (defun cartesian1 (l1 l2)
  2.   (apply 'append
  3.   (mapcar '(lambda (x)
  4.       (mapcar '(lambda (y) (cons x y)) l2)
  5.     )
  6.    l1
  7.   )
  8.   )
  9. )
测试参数1——'(1 2 3 5 6 7) '(8 9 10) 运行次数10w次,时间结果如下:
  1. _$
  2. 函数:CARTESIAN运行100000次测试结果00:00:08:437
  3. 函数:CARTESIAN0运行100000次测试结果00:00:04:905
  4. 函数:CARTESIAN1运行100000次测试结果00:00:06:983
  5. _$
复制代码
测试参数2——'(1 2 3 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20) '(21 22 23 24 25 26 27 28 29 30) 行次数1w次,时间结果如下:
  1. _$
  2. 函数:CARTESIAN运行10000次测试结果00:00:11:405
  3. 函数:CARTESIAN0运行10000次测试结果00:00:04:186
  4. 函数:CARTESIAN1运行10000次测试结果00:00:03:812
  5. _$
复制代码
测试参数3——'(1 2 3 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40) '(41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60) 行次数1w次,时间结果如下:
  1. _$
  2. 函数:CARTESIAN运行10000次测试结果00:01:09:812
  3. 函数:CARTESIAN0运行10000次测试结果00:00:16:563
  4. 函数:CARTESIAN1运行10000次测试结果00:00:12:937
  5. _$
复制代码
随着链表长度的增加,由于未申请临时变量的递归耗用大量存储空间也随着增加,而申请了局部变量的递归和顺序遍历效率互有上下,小表时while循环递归更快,大表时mapcar遍历更快。
 楼主| 发表于 2010-9-27 15:03:00 | 显示全部楼层
本帖最后由 作者 于 2010-9-27 15:30:03 编辑

  
23楼嵌套递归之所以低效,是因为他并不是真正的意义上的交叉嵌套,您可以修改它,使它变得最高效!最近时间比较少,先来一道交叉嵌套递归在矩阵求模中的应用;
编制一个嵌套递归函数deter,用于矩阵求模,您可以编写一个临时函数cofact,cofact必须调用到deter函数,deter函数必须调用到cofact函数
  1. ;;m为矩阵列表
  2. ;;格式如(setq m '((1 2 3) (2 4 5) (3 5 6)))
  3. ;;求矩阵m的模
  4. (defun cofact (m)
  5.   ;;写出您的代码,要求调用deter和remove-nth函数
  6. )
  7. (defun deter (m)
  8. ;;写出您的代码,要求调用cofact函数
  9. )

 楼主| 发表于 2010-9-27 15:18:00 | 显示全部楼层
为了便于比较交叉递归的效率,这里贴出一个粗糙的矩阵求模函数
  1. (defun [mo] (a / m*n m n |A| zh_lst zh_lsti i ∑ti i% aij i%_goon ∏aij
  2.       si_i)
  3.   (setq m*n (get-m&n a)
  4. m   (car m*n)
  5. n   (cadr m*n)
  6.   )
  7.   (if (/= m n)
  8.     (setq |A| 0)
  9.     (progn
  10.       (setq n    (length a)
  11.      zh_lst (get-zh_lst n)
  12.       )
  13.       (setq i -1
  14.      |A| 0
  15.       )
  16.       (repeat (length zh_lst)
  17. (setq zh_lsti (nth (setq i (1+ i)) zh_lst)
  18.        ∑ti    (get-t zh_lsti)
  19.        i%      0
  20.        ∏aij   1
  21.        i%_goon T
  22. )
  23. (while (and i%_goon (<= (1+ i%) n))
  24.    (setq aij (get-aij (1+ i%) (nth i% zh_lsti) a))
  25.    (if (or (= aij 0) (null aij))
  26.      (setq ∏aij 0
  27.     i%_goon nil
  28.      )
  29.      (setq ∏aij (* ∏aij aij)
  30.     i% (1+ i%)
  31.      )
  32.    )
  33. )
  34. (setq si_i (* (expt -1 ∑ti) ∏aij)
  35.        |A|  (+ |A| si_i)
  36. )
  37.       )
  38.     )
  39.   )
  40.   |A|
  41. )
配套函数:
  1. ;;获取矩阵的行列数
  2. (defun get-m&n (a / m n)
  3.   (setq m (length a)
  4. n 0
  5.   )
  6.   (foreach b a
  7.     (if (> (length b) n)
  8.       (setq n (length b))
  9.     )
  10.   )
  11.   (list m n)
  12. )
  13. ;;;获取(1~n)列表的所有不重复组合
  14. (defun get-zh_lst (n / get-nIA-lst i lst)
  15.   (defun get-nIA-lst (lst / ret midlst)
  16.     (setq ret nil)
  17.     (foreach a lst
  18.       (foreach b lst
  19. (if (/= a b)
  20.    (setq ret (cons (list a b) ret))
  21. )
  22.       )
  23.     )
  24.     (repeat (- (length lst) 2)
  25.       (setq midlst nil)
  26.       (foreach a lst
  27. (foreach b ret
  28.    (if (null (member a b))
  29.      (setq midlst (cons (cons a b) midlst))
  30.    )
  31. )
  32.       )
  33.       (setq ret midlst)
  34.     )
  35.     ret
  36.   )
  37.   (setq i   0
  38. lst nil
  39.   )
  40.   (repeat n
  41.     (setq i (1+ i))
  42.     (setq lst (cons i lst))
  43.   )
  44.   (get-nIA-lst lst)
  45. )
  46. ;;
  47. ;;;take out the ij-item of a Matrix list
  48. (defun get-aij (i j a / aij)
  49.   (nth (1- j) (nth (1- i) a))
  50. )
  51. ;;;求某个自然数n排列的逆系数(Inverse coefficient)
  52. (defun get-t (zh_lst / i mid_t end_t)
  53.   ;;获取某个组合排列中的第i个元素的逆系数
  54.   (defun get-ti (i zh_lst / a mid_t i% mid_a)
  55.     (setq a (nth (setq i (1- i)) zh_lst))
  56.     (setq mid_t 0
  57.    i% i
  58.     )
  59.     (repeat i
  60.       (setq mid_a (nth (setq i% (1- i%)) zh_lst))
  61.       (if (> mid_a a)
  62. (setq mid_t (1+ mid_t))
  63.       )
  64.     )
  65.     mid_t
  66.   )
  67.   (setq i 0
  68. mid_t 0
  69. end_t 0
  70.   )
  71.   (repeat (length zh_lst)
  72.     (setq i (1+ i))
  73.     (setq mid_t (get-ti i zh_lst))
  74.     (setq end_t (+ end_t mid_t))
  75.   )
  76.   end_t
  77. )
 楼主| 发表于 2010-10-5 21:24:00 | 显示全部楼层
本帖最后由 作者 于 2010-10-12 1:28:37 编辑

gile大师是这样写的(好像应该叫交叉嵌套,并未使用递归;当然您可以修改成递归函数)
  1. ;;; gile-cofact (gile)
  2. ;;; returns the gile-cofactor associated to ij item of a matrix
  3. ;;;
  4. ;;; arguments
  5. ;;; i = row index (first row = 1)
  6. ;;; j = column index (first column = 1)
  7. ;;; m = a matrix
  8. (defun gile-cofact (i j m)
  9.   (* (gile-determ
  10.        (remove-nth (list (1- i) (1- j)) m)
  11.      )
  12.      (expt -1 (+ i j))
  13.   )
  14. )
  15. ;;; gile-determ (gile)
  16. ;;; returns the determinant of a matrix
  17. ;;;求矩阵的模,即行列式结果
  18. ;;; argument : a matrix
  19. ;;;(gile-determ m) ([mo] m)([tr] m)
  20. ;;;
  21. (defun gile-determ (m)
  22.   (if (= 2 (length m))
  23.     (- (* (caar m) (cadadr m)) (* (caadr m) (cadar m)))
  24.     ((lambda (r n)
  25.        (apply
  26.   '+
  27.   (mapcar
  28.     (function (lambda (x)
  29.          (* x
  30.      (gile-cofact
  31.        1
  32.        (setq n (1+ n))
  33.        m
  34.      )
  35.          )
  36.        )
  37.     )
  38.     r
  39.   )
  40.        )
  41.      )
  42.       (car m)
  43.       0
  44.     )
  45.   )
  46. )
 楼主| 发表于 2011-1-21 15:49:24 | 显示全部楼层
参考qj-chen老师的表项替换函数二分法,修改了下函数ch-lst,使得大表替换更为高效
  1. ;;;表项替换,支持2重表,当指定i为list如(3 1)时,替换第3个子表中的第1个元素
  2. ;;;lsp较快算法,二分递归法,
  3. ;;;written by qj-chen
  4. ;;;Edited by GSLS(SS)
  5. (defun ch-lst (new i lst / j len fst mid)
  6.   (if (/= (type i) 'list)
  7.     (cond
  8.       ((minusp i)
  9.        lst
  10.       )
  11.       ((> i (setq len (length lst)))
  12.        lst
  13.       )
  14.       ((> i (/ len 2))
  15.        (reverse (ch-lst new (1- (- len i)) (reverse lst)))
  16.       )
  17.       (t
  18.        (append
  19.          (progn
  20.            (setq fst nil)
  21.            (repeat (rem i 4)
  22.              (setq fst (cons (car lst) fst)
  23.                    lst (cdr lst)
  24.              )
  25.            )
  26.            (repeat (/ i 4)
  27.              (setq fst (cons (cadddr lst)
  28.                              (cons (caddr lst)
  29.                                    (cons
  30.                                      (cadr lst)
  31.                                      (cons
  32.                                        (car lst)
  33.                                        fst
  34.                                      )
  35.                                    )
  36.                              )
  37.                        )
  38.                    lst (cddddr lst)
  39.              )
  40.            )
  41.            (reverse fst)
  42.          )
  43.          (list new)
  44.          (cdr lst)
  45.        )
  46.       )
  47.     )
  48.     (progn
  49.       (setq j (cadr i)
  50.             i (car i)
  51.       )
  52.       (if j
  53.         (progn
  54.           (setq mid (nth i lst))
  55.           (setq mid (ch-lst new j mid))
  56.           (ch-lst mid i lst)
  57.         )
  58.         (ch-lst new i lst)
  59.       )
  60.     )
  61.   )
  62. )
发表于 2011-1-30 09:53:29 | 显示全部楼层
请教楼主,如何建立一棵树?
d:\tree.jpg
 楼主| 发表于 2011-2-11 18:06:33 | 显示全部楼层
本帖最后由 chlh_jd 于 2011-2-11 18:07 编辑

建立一棵树,没有理解;qj-chen老师在立面树很有心得
 楼主| 发表于 2012-7-8 02:23:09 | 显示全部楼层
本帖最后由 chlh_jd 于 2012-7-8 07:36 编辑

再复杂点的递归,经常在复杂问题的动态规划中用到,硬币问题
这里收录下ElpanovEvgeniy硬币换零问题的代码,原文地址http://www.theswamp.org/index.php?topic=39082.msg443766#msg443766
  1. ;;; **************************************硬币兑换问题****************************************
  2. ;;; n ---- 整币面值
  3. ;;; l ---- 分币面值表
  4. ;;; 返回最少张数找零方案((分币面值 . 分币张数)...)
  5. (defun eea-change (n l / foo f1 f2 f3)
  6.   ;;by ElpanovEvgeniy
  7.   ;;from http://www.theswamp.org/index.php?topic=39082.msg443766#msg443766
  8.   ;;
  9.   (defun foo (n l / i)
  10.     (cond ((or (not l) (< n 0)) (list 32767 nil))
  11.    ((< n (car l)) (foo n (cdr l)))
  12.    ((zerop (rem n (car l)))
  13.     (list (setq i (/ n (car l))) (cons (car l) i))
  14.    )
  15.    ((f1 (f2 n (cdr l)) (foo (- n (car l)) l) (car l)))
  16.     )
  17.   )
  18.   (defun f1 (a b c)
  19.     (cond ((not a) (f3 n l))
  20.    ((< (car a) (1+ (car b))) a)
  21.    ((= (caadr b) c)
  22.     (cons (1+ (car b)) (cons (cons c (1+ (cdadr b))) (cddr b)))
  23.    )
  24.    ((cons (1+ (car b)) (cons (cons c 1) (cdr b))))
  25.     )
  26.   )
  27.   ;|
  28.   (defun f2 (n l / i)
  29.     (setq i 0
  30.     l (mapcar (function (lambda (a / b c)
  31.         (setq b  n
  32.               n  (rem n a)
  33.               c  (/ b a)
  34.               i  (+ i c)
  35.         )
  36.         (cons a c)
  37.             )
  38.         )
  39.         (vl-sort l (function >))
  40.       )
  41.     )
  42.     (if  (= n 0)
  43.       (cons i l)
  44.     )
  45.   )|;  ;|;;only for art by GSLS(SS)
  46.   ;;Use (f2 n l '(0)) replace (f2 n l)
  47.   (defun f2 (n l r / i a b c)  
  48.     (cond ((or (not l) (minusp n)) nil)
  49.    ((zerop n)
  50.     (cons (car r) (reverse (cdr r)))
  51.    )
  52.    ((f2 (rem n (car l))
  53.         (cdr l)
  54.         (cons (+ (car r) (/ n (car l)))
  55.        (cons (cons (car l) (/ n (car l))) (cdr r))
  56.         )
  57.     )
  58.    )
  59.     )
  60.   )|;
  61.   (defun f2 (n l / i a b c)
  62. ;_by GSLS(SS)
  63.     (setq i 0)
  64.     (while (and (> n 0) l)
  65.       (setq a (car l)
  66.      l (cdr l)
  67.      b (/ n a)
  68.      c (cons (cons a b) c)
  69.      n (rem n a)
  70.      i (+ i b)
  71.       )
  72.     )
  73.     (if (zerop n)
  74.       (cons i (reverse c))
  75.     )
  76.   )  (defun f3 (n l / i)
  77.     (cond ((or (not l) (< n 0)) (list 32767 nil))
  78.    ((< n (car l)) (foo n (cdr l)))
  79.    ((zerop (rem n (car l)))
  80.     (list (setq i (/ n (car l))) (cons (car l) i))
  81.    )
  82.    ((f1 (f3 n (cdr l)) (foo (- n (car l)) l) (car l)))
  83.     )
  84.   )
  85.   (foo n l)
  86. )
该算法的主要思路,C[j]= {if denom>j  , C[i+1][j] , min{ C[i+1][j],1+C[j-denom}} ,其中denom为币种
简单比喻成计算最少零钱张数,如下

  1. (defun _coins ( n l );_by Lee Mac
  2.   (cond
  3.     ( (< n 0) 32767)
  4.     ( (zerop n) 0)
  5.     ( (null l) 32767)
  6.     ( (min (_coins n (cdr l)) (1+ (_coins (- n (car l)) l))))
  7.   )
  8. )

测试范举
  1. (eea-change 32 '(20 10 5 3)) ;>> (5 (20 . 1) (3 . 4))
  2. (eea-change 98 '(20 10 5 2 1)) ;>>(8 (20 . 4) (10 . 1) (5 . 1) (2 . 1) (1 . 1))
复制代码

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-11-22 21:15 , Processed in 0.176282 second(s), 17 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表