明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 4134|回复: 22

[【高飞鸟】] FFT的LISP实现

  [复制链接]
发表于 2022-7-8 16:37:56 | 显示全部楼层 |阅读模式
本帖最后由 highflybird 于 2022-7-12 12:37 编辑

FFT变换,即快速傅里叶变换,是一种很重要的变换,应用很广泛。
我浏览了网上的实现FFT变换的语言大多数是C或者C++,python等,且具体实现中一般不采用递归方式,而采用蝶形运算,因为这些语言可以较为方便地访问数组。而LISP语言对于访问数组不太灵活,递归是其强项。因此在这个实现中,我采用了递归方式。另外,autolisp语言不支持复数,只能自己构造复数来实现。

对FFT不了解的,可以参考如下链接:快速傅里叶变换(FFT)——有史以来最巧妙的算法?
另外,这个LISP的实现也借鉴(抄袭)了如下链接的代码:A Fast Fourier Transform in Lisp
对此处的代码做了一点点修改。
下面是其主要代码:
  1. ;;;=============================================================
  2. ;;; 获取偶数项                                                  
  3. ;;;=============================================================
  4. (defun evens (f)
  5.   (if f
  6.     (cons (car f) (evens (cddr f)))
  7.   )
  8. )

  9. ;;;=============================================================
  10. ;;; 获取奇数项                                                  
  11. ;;;=============================================================
  12. (defun odds (f)
  13.   (if f
  14.     (cons (cadr f) (odds (cddr f)))
  15.   )
  16. )

  17. ;;;=============================================================
  18. ;;; 求w(利用欧拉公式求系数)                                    
  19. ;;;=============================================================
  20. (defun phase (s k n / x)
  21.   ;;(exp (/ (* 0+2i s k 3.1415926535 ) N) )
  22.   (if (> s 0)
  23.     (setq x (/ (* PI k) N))
  24.     (setq x (/ (* (- pi) k) N))
  25.   )
  26.   (list (cos x) (sin x))
  27. )

  28. ;;;=============================================================
  29. ;;; 求每一项乘以系数(即对复数旋转)                           
  30. ;;;=============================================================
  31. (defun rotate (s k N f / )
  32.   (if f
  33.     (cons
  34.       (CMP::MUL (phase s k N) (car f))
  35.       (rotate s (1+ k) N (cdr f))
  36.     )
  37.   )
  38. )

  39. ;;;=============================================================
  40. ;;; 此处修改仅仅是为了减少递归深度                              
  41. ;;;=============================================================
  42. (defun PFFT (s f / e o)
  43.   (if (= 2 (length f))
  44.     (list
  45.       (apply 'CMP::ADD f)
  46.       (apply 'CMP::SUB f)
  47.     )
  48.     (combine s (PFFT s (evens f)) (PFFT s (odds f)))
  49.   )
  50. )

  51. ;;;=============================================================
  52. ;;; With these preliminaries PFFT is simple                     
  53. ;;; 此段为原来方式(the original way)                           
  54. ;;;=============================================================
  55. (defun PFFT1 (s f / e o)
  56.   (if (= 1 (length f))
  57.     f
  58.     (combine s (PFFT1 s (evens f)) (PFFT1 s (odds f)))
  59.   )
  60. )

  61. ;;;=============================================================
  62. ;;; 合并奇偶两部分                                             
  63. ;;;=============================================================
  64. (defun combine (s ev od)
  65.   (plusminus ev (rotate s 0 (length od) od))
  66. )

  67. ;;;=============================================================
  68. ;;; 奇偶两部分相加减                                            
  69. ;;;=============================================================
  70. (defun plusminus (a b)
  71.   (append (mapcar 'CMP::ADD a b) (mapcar 'CMP::SUB a b))
  72. )


源代码请参见附件:

欢迎诸位的建议或者更好的想法!
以后在10楼陆续增加一些应用实例。

本帖子中包含更多资源

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

x

点评

说明:1,返回的结果可能猛一看,与网上的结果不太一样,其实是相同的,只不过一个是顺时针,一个是逆时针,也可以在函数里面修改1+ 为1-便一致了。2,此FFT是针对大量数据有优化效果,数据少可能起不到加速。  发表于 2022-7-8 17:02

评分

参与人数 10明经币 +11 金钱 +36 收起 理由
自贡黄明儒 + 1 赞一个!
xshrimp + 1 + 6 神马都是浮云
xsso + 1 很给力!
qjchen + 2 + 30 赞一个!
hh_lj007 + 1 很给力!
jltx123456 + 1 赞一个!
tigcat + 1 高飞大神是LISP结合数学的大神,更令人敬佩.
guosheyang + 1 赞一个!
1028695446 + 1 赞一个!
夏生生 + 1 神马都是浮云

查看全部评分

"觉得好,就打赏"
还没有人打赏,支持一下
 楼主| 发表于 2022-7-9 11:35:26 | 显示全部楼层
本帖最后由 highflybird 于 2022-7-12 12:40 编辑
hhh454 发表于 2022-7-8 21:08
感谢高飞鸟大师分享,能否给出一些具体的应用?

先举一个例子:
多项式乘法:


  1. (fft -1 (mapcar 'cmp::MUL (fft 1 '(1 2 3 4 5 6 7 8 0)) (fft 1 '(2 2 1 0 5 5 4 8 0))))
  2. ==> (2.0 6.0 11.0 16.0 26.0 41.0 60.0 87.0 96.0 103.0 117.0 139.0 116.0 88.0 64.0 0.0)

很完美,和软件计算结果一致。计算精度很高。可以到小数后20位。

再来一个例子:
高精度计算:
譬如高精度乘法:
(gjd "1234567890987654321" "9876543210123456789")   
=>"12193263121170553265523548251112635269"
具体代码实现:
  1. ;;;=============================================================
  2. ;;; 实数转复数                                                  
  3. ;;;=============================================================
  4. (defun CMP::R2C (x)
  5.   (if (listp x) x (list x 0))
  6. )

  7. ;;;=============================================================
  8. ;;; 字符串是否只包含数字                                       
  9. ;;;=============================================================
  10. (defun isNumber (a)
  11.   (vl-every
  12.     (function (lambda (n) (< 47 n 58)))
  13.     (vl-string->list a)
  14.   )
  15. )

  16. ;;;=============================================================
  17. ;;; 字符串转数字表                                             
  18. ;;; 例如:"12312" => '(1 2 3 1 2)                              
  19. ;;;=============================================================
  20. (defun str->num (a / l)
  21.   (setq l (vl-string->list a))
  22.   (if (vl-every (function (lambda (n) (< 47 n 58))) l)
  23.     (mapcar (function (lambda (n) (- n 48))) l)
  24.   )
  25. )

  26. ;;;=============================================================
  27. ;;; 末尾补零,使得表长满足需要长度(一般来说是2^n幕)           
  28. ;;;=============================================================
  29. (defun AppendZero (lst len / add)
  30.   (repeat len
  31.     (setq add (cons '(0 0) add))
  32.   )
  33.   (setq lst (append lst add))
  34. )

  35. ;;;=============================================================
  36. ;;; 高精度乘法 void multiply                                    
  37. ;;; 例如:(gjd "1234567890987654321" "9876543210123456789")     
  38. ;;; =>"12193263121170553265523548251112635269"                  
  39. ;;;=============================================================
  40. (defun GJD (a1 a2 / FA LEN LEN1 LEN2 LL N0 N1)
  41.   (if (and (setq a1 (str->num a1)) (setq a2 (str->num a2)))
  42.     (progn
  43.       ;;倒排表(因为十进制数是从高位到低位),并转换为复数
  44.       (setq a1 (mapcar 'CMP::R2C (reverse a1)))
  45.       (setq a2 (mapcar 'CMP::R2C (reverse a2)))
  46.       ;;计算需要补零个数
  47.       (setq len1 (length a1))
  48.       (setq len2 (length a2))
  49.       (setq len (max len1 len2))
  50.       (if (zerop (logand len (1- len)))
  51.   (setq len (* len 2))
  52.   (setq len (expt 2 (+ 2 (fix (/ (log len) (log 2))))))
  53.       )
  54.       ;;补零
  55.       (setq a1 (AppendZero a1 (- len len1)))
  56.       (setq a2 (AppendZero a2 (- len len2)))
  57.       ;;利用FFT把两大数相乘转为多项式乘法
  58.       (setq a1 (RFFT 1 a1))
  59.       (setq a2 (RFFT 1 a2))
  60.       (setq fa (RFFT -1 (mapcar 'cmp::mul a1 a2)))
  61.       ;;小数转整并消除多余的零
  62.       (setq fa (mapcar (function (lambda (x) (fix (+ 0.5 (/ (car x) len))))) fa))
  63.       (setq fa (reverse fa))
  64.       (while (zerop (car fa))
  65.   (setq fa (cdr fa))
  66.       )
  67.       (setq fa (reverse fa))
  68.       ;;进位
  69.       (setq n0 0)
  70.       (foreach n fa
  71.   (setq n1 (+ n n0))
  72.   (setq n0 (/ n1 10))
  73.   (setq ll (cons (rem n1 10) ll))
  74.       )
  75.       (if (/= n0 0)
  76.         (setq ll (cons n0 ll))
  77.       )
  78.       ;;转字符串
  79.       (vl-list->string (mapcar (function (lambda (n) (+ n 48))) ll))
  80.     )
  81.   )
  82. )

当然,在这个位数不多的情况下,没什么优势,当上万或者更大的时候,FFT算法的优势就可以体现出来了。
FFT用于图像处理、数字信号处理、数据采集、噪声分析、字符串匹配、数学计算(譬如积分、卷积、多项式乘法、高精度计算)等等,用途极其广泛。 而有些应用光靠lisp语言无法实现的。在这里不再一一举例,有很多我都不知道,所以读者自行研究。

本帖子中包含更多资源

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

x

点评

太厉害了,佩服  发表于 2022-7-13 19:55
发表于 2022-7-11 12:35:22 | 显示全部楼层
偶数项不用递归,可能 也快
;;(setq L '(1 2 3 4 5 6))=>(2 4 6)
(defun evens (L)
  (vl-remove-if '(lambda (x) (if (setq Flag (not Flag)) x)) L)
)
发表于 2022-7-9 12:05:10 | 显示全部楼层
highflybird 发表于 2022-7-9 11:35
先举一个例子:

多项式乘法

感谢指导,学习中
发表于 2022-7-8 16:46:35 | 显示全部楼层
大师出手,必有精品
发表于 2022-7-8 16:52:35 | 显示全部楼层
强势围观学习
发表于 2022-7-8 16:55:06 | 显示全部楼层
浮点运算是否有误差?

点评

还好,在允许范围内。  发表于 2022-7-8 16:58
发表于 2022-7-8 18:44:53 | 显示全部楼层
感谢高飞鸟大师共享代码!
发表于 2022-7-8 21:08:40 | 显示全部楼层
感谢高飞鸟大师分享,能否给出一些具体的应用?
发表于 2022-7-9 06:30:07 | 显示全部楼层
太牛了,点赞
发表于 2022-7-9 07:19:05 | 显示全部楼层

太牛了,点赞
发表于 2022-7-9 08:31:02 | 显示全部楼层
太深奥了,不懂,还是要点赞!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-5-15 13:35 , Processed in 0.210127 second(s), 36 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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