当前位置: 首页 > 知识库问答 >
问题:

功能上惯用的FFT

卞轶
2023-03-14

我编写这个基-2 FFT的目的是在不牺牲太多性能的情况下使其功能地道:

let reverse x bits =
    let rec reverse' x bits y =
        match bits with
        | 0 -> y
        | _ -> ((y <<< 1) ||| (x &&& 1))
               |> reverse' (x >>> 1) (bits - 1) 
     reverse' x bits 0 

let radix2 (vector: Complex[]) (direction: int) =
    let z = vector.Length
    let depth = floor(Math.Log(double z, 2.0)) |> int
    if (1 <<< depth) <> z then failwith "Vector length is not a power of 2"

    // Complex roots of unity; "twiddle factors"
    let unity: Complex[] =
        let xpn = float direction * Math.PI / double z
        Array.Parallel.init<Complex> (z/2) (fun i ->
            Complex.FromPolarCoordinates(1.0, (float i) * xpn))

    // Permutes elements of input vector via bit-reversal permutation
    let pvec = Array.Parallel.init z (fun i -> vector.[reverse i depth])

    let outerLoop (vec: Complex[]) =
        let rec recLoop size =
            if size <= z then
                let mid, step = size / 2, z / size
                let rec inrecLoop i =
                    if i < z then
                        let rec bottomLoop idx k =
                            if idx < i + mid then
                                let temp = vec.[idx + mid] * unity.[k]
                                vec.[idx + mid] <- (vec.[idx] - temp)
                                vec.[idx] <- (vec.[idx] + temp)
                                bottomLoop (idx + 1) (k + step)
                        bottomLoop i 0
                        inrecLoop (i + size)
                inrecLoop 0
                recLoop (size * 2)
        recLoop 2
        vec

    outerLoop pvec

共有1个答案

端木飞
2023-03-14

我不是算法工作的专家,所以可能有一个很好的函数实现,但值得注意的是,在F#中使用局部变异是非常惯用的。

您的radix2函数是从外部起作用的--它将vector数组作为输入,从不对其进行突变,创建一个新数组pvec,然后对其进行初始化(沿途使用一些突变),然后返回它。这与array.map等内置函数使用的模式类似(初始化一个新数组,对其进行突变,然后返回它)。这通常是一种明智的做法,因为有些算法更好地使用变异来编写。

在这种情况下,使用局部可变变量和循环是完全合理的。与尾递归版本相比,这样做将使您的代码更具可读性。我没有对此进行测试,但我对outerloop函数的简单翻译就是使用三个嵌套循环--如下所示:

let mutable size = 2
while size <= z do
    let mid, step = size / 2, z / size
    let mutable i = 0
    while i < z do
        for j in 0 .. mid - 1 do
            let idx, k = i + j, step * j
            let temp = pvec.[idx + mid] * unity.[k]
            pvec.[idx + mid] <- (pvec.[idx] - temp)
            pvec.[idx] <- (pvec.[idx] + temp)
        i <- i + size
    size <- size * 2

这可能不完全正确(我这样做只是为了重构您的代码),但我认为在这种情况下,它实际上比使用复杂的嵌套尾递归函数更地道。

 类似资料:
  • 问题内容: 我有以下JavaScript代码: 我如何确保仅在完成后调用? 问题答案: 指定一个匿名回调,并使function1接受它:

  • Polar M600 有两个按钮:正面按钮与侧边电源按钮。 正面按钮 侧边电源按钮 触屏 前置按钮 在训练过程中使用正面按钮可轻易地启用 Polar 应用程式。您可使用正面按钮进行以下操作: 从主屏幕上迅速打开 Polar 应用程式 在 Polar 应用程式菜单中打开Training (训练)。 选择您想进行的运动并在运动内容列表中开始训练。 训练期间手动记圈。 暂停训练。 停止训练课。 训练结束

  • 问题内容: 这段代码的结果为56。 知道里面发生了什么吗?我很困惑。 问题答案: X返回(值+3),而Y返回(值* 2) 给定值为4,这表示。 尽管函数不受范围限制(这意味着您可以安全地“嵌套”函数定义),但是此特定示例容易出错: 1)您不能在调用 之前先调用,因为函数只有执行一次才真正定义。 2)调用两次将导致PHP重新声明function ,从而导致致命错误: 致命错误:无法重新声明y() 两

  • Navicat Data Modeler 提供数种在创建模型时能改善用户体验的工具。 模型转换 自动布局 打印和导出模型 搜索筛选 全屏模式

  • Navicat Data Modeler 提供数种在创建模型时能改善用户体验的工具。 模型转换 自动布局 打印和导出模型 搜索筛选 深色布景主题 全屏模式

  • 问题内容: 我正在开发一个Web应用程序,正在尝试为其提供功能齐全的窗口系统。目前进展顺利,我只遇到一个小问题。有时,当我去拖动应用程序的一部分时(通常是窗口的角落div,这应该触发调整大小的操作),Web浏览器变得很聪明,并认为我的意思是拖放东西。最终结果是,当浏览器进行拖放操作时,我的动作被暂停。 有没有一种简便的方法来禁用浏览器的拖放?理想情况下,我希望能够在用户单击某些元素时将其关闭,但是