乐筑天下

搜索
欢迎各位开发者和用户入驻本平台 尊重版权,从我做起,拒绝盗版,拒绝倒卖 签到、发布资源、邀请好友注册,可以获得银币 请注意保管好自己的密码,避免账户资金被盗
查看: 208|回复: 11

FFT的LISP实现

[复制链接]

56

主题

346

帖子

68

银币

中流砥柱

Rank: 25

铜币
512
发表于 2022-7-8 16:37:00 | 显示全部楼层 |阅读模式
FFT变换,即快速傅里叶变换,是一种很重要的变换,应用很广泛。
我浏览了网上的实现FFT变换的语言大多数是C或者C++,python等,且具体实现中一般不采用递归方式,而采用蝶形运算,因为这些语言可以较为方便地访问数组。而LISP语言对于访问数组不太灵活,递归是其强项。因此在这个实现中,我采用了递归方式。另外,autolisp语言不支持复数,只能自己构造复数来实现。
对FFT不了解的,可以参考如下链接:
另外,这个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楼陆续增加一些应用实例。

本帖以下内容被隐藏保护;需要你回复后,才能看到!

游客,如果您要查看本帖隐藏内容请回复
回复

使用道具 举报

56

主题

346

帖子

68

银币

中流砥柱

Rank: 25

铜币
512
发表于 2022-7-9 11:35:00 | 显示全部楼层

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

cts5zsgfyq0.png

cts5zsgfyq0.png


  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) (list a)
  13.   )
  14. )
  15. ;;;=============================================================
  16. ;;; 字符串转数字表                                             
  17. ;;; 例如:"12312" => '(1 2 3 1 2)                              
  18. ;;;=============================================================
  19. (defun str->num (a / l)
  20.   (setq l (vl-string->list a))
  21.   (if (vl-every (function (lambda (n) ("12193263121170553265523548251112635269"                  
  22. ;;;=============================================================
  23. (defun GJD (a1 a2 / FA LEN LEN1 LEN2 LL N0 N1)
  24.   (if (and (setq a1 (str->num a1)) (setq a2 (str->num a2)))
  25.     (progn
  26.       ;;倒排表(因为十进制数是从高位到低位),并转换为复数
  27.       (setq a1 (mapcar 'CMP::R2C (reverse a1)))
  28.       (setq a2 (mapcar 'CMP::R2C (reverse a2)))
  29.       ;;计算需要补零个数
  30.       (setq len1 (length a1))
  31.       (setq len2 (length a2))
  32.       (setq len (max len1 len2))
  33.       (if (zerop (logand len (1- len)))
  34.   (setq len (* len 2))
  35.   (setq len (expt 2 (+ 2 (fix (/ (log len) (log 2))))))
  36.       )
  37.       ;;补零
  38.       (setq a1 (AppendZero a1 (- len len1)))
  39.       (setq a2 (AppendZero a2 (- len len2)))
  40.       ;;利用FFT把两大数相乘转为多项式乘法
  41.       (setq a1 (RFFT 1 a1))
  42.       (setq a2 (RFFT 1 a2))
  43.       (setq fa (RFFT -1 (mapcar 'cmp::mul a1 a2)))
  44.       ;;小数转整并消除多余的零
  45.       (setq fa (mapcar (function (lambda (x) (fix (+ 0.5 (/ (car x) len))))) fa))
  46.       (setq fa (reverse fa))
  47.       (while (zerop (car fa))
  48.   (setq fa (cdr fa))
  49.       )
  50.       (setq fa (reverse fa))
  51.       ;;进位
  52.       (setq n0 0)
  53.       (foreach n fa
  54.   (setq n1 (+ n n0))
  55.   (setq n0 (/ n1 10))
  56.   (setq ll (cons (rem n1 10) ll))
  57.       )
  58.       (if (/= n0 0)
  59.         (setq ll (cons n0 ll))
  60.       )
  61.       ;;转字符串
  62.       (vl-list->string (mapcar (function (lambda (n) (+ n 48))) ll))
  63.     )
  64.   )
  65. )

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

使用道具 举报

188

主题

1652

帖子

31

银币

顶梁支柱

Rank: 50Rank: 50

铜币
2391
发表于 2022-7-11 12:35:00 | 显示全部楼层
偶数项不用递归,可能 也快
;;(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)
)
回复

使用道具 举报

28

主题

327

帖子

21

银币

后起之秀

Rank: 20Rank: 20Rank: 20Rank: 20

铜币
432
发表于 2022-7-9 12:05:00 | 显示全部楼层

感谢指导,学习中
回复

使用道具 举报

13

主题

55

帖子

7

银币

初露锋芒

Rank: 3Rank: 3Rank: 3

铜币
105
发表于 2022-7-8 16:46:00 | 显示全部楼层
大师出手,必有精品
回复

使用道具 举报

14

主题

93

帖子

7

银币

初露锋芒

Rank: 3Rank: 3Rank: 3

铜币
147
发表于 2022-7-8 16:52:00 | 显示全部楼层
强势围观学习
回复

使用道具 举报

18

主题

128

帖子

13

银币

后起之秀

Rank: 20Rank: 20Rank: 20Rank: 20

铜币
200
发表于 2022-7-8 16:55:00 | 显示全部楼层
浮点运算是否有误差?
回复

使用道具 举报

15

主题

227

帖子

20

银币

后起之秀

Rank: 20Rank: 20Rank: 20Rank: 20

铜币
276
发表于 2022-7-8 18:44:00 | 显示全部楼层
感谢高飞鸟大师共享代码!
回复

使用道具 举报

28

主题

327

帖子

21

银币

后起之秀

Rank: 20Rank: 20Rank: 20Rank: 20

铜币
432
发表于 2022-7-8 21:08:00 | 显示全部楼层
感谢高飞鸟大师分享,能否给出一些具体的应用?
回复

使用道具 举报

122

主题

647

帖子

223

银币

版主

Rank: 10Rank: 10

铜币
1174
发表于 2022-7-9 06:30:00 | 显示全部楼层
太牛了,点赞
回复

使用道具 举报

发表回复

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

本版积分规则

  • 微信公众平台

  • 扫描访问手机版

  • 点击图片下载手机App

QQ|关于我们|小黑屋|乐筑天下 繁体中文

GMT+8, 2025-3-4 05:41 , Processed in 1.802709 second(s), 77 queries .

© 2020-2025 乐筑天下

联系客服 关注微信 帮助中心 下载APP 返回顶部 返回列表