我想用离散滤波器卷积离散信号。信号和滤波器是F#中的浮点序列。

我能弄清楚如何做的唯一方法是使用两个嵌套的for循环和一个可变数组来存储结果,但它感觉不是很有用。

以下是我将如何执行此操作:

conv = double[len(signal) + len(filter) - 1]
for i = 1 to len(signal)
  for j = 1 to len(filter)
    conv[i + j] = conv[i + j] + signal(i) * filter(len(filter) - j) 
有帮助吗?

解决方案

尝试此功能:

let convolute signal filter =
    [|0 .. Array.length signal + Array.length filter - 1|] |> Array.map (fun i ->
        [|0 .. i|] |> Array.sum_by (fun j -> signal.[i] * filter.[Array.length filter - (i - j) - 1]))

它可能不是最好的功能解决方案,但它应该完成这项工作。我怀疑是否存在一种纯粹的功能性解决方案,它将与速度的命令相匹配。

希望有所帮助。

注意:该功能目前尚未经过测试(虽然我已经确认它已编译)。让我知道它是不是应该做的事情。另外,请注意 i j 变量不会引用与原始帖子相同的内容。

其他提示

我不知道F#,但我会发布一些Haskell,希望它足够接近使用。 (我只有VS 2005和F#的古老版本,所以我认为在我的机器上发布可以发布的东西会更加令人困惑)

首先让我发布您的伪代码的Python实现,以确保我得到正确的答案:

def convolve(signal, filter):
    conv = [0 for _ in range(len(signal) + len(filter) - 1)]
    for i in range(len(signal)):
        for j in range(len(filter)):
            conv[i + j] += signal[i] * filter[-j-1]
    return conv

现在 convolve([1,1,1],[1,2,3])给出 [3,5,6,3,1] 。如果这是错的,请告诉我。

我们能做的第一件事就是将内循环变成一个zipWith;我们实际上是以一种特殊的方式添加一系列行,在上面的示例中: [[3,2,1],[3,2,1],[3,2,1]] 。要生成每一行,我们将使用反向过滤器压缩 signal 中的每个 i

makeRow filter i = zipWith (*) (repeat i) (reverse filter)

(注意:根据快速谷歌, zipWith 是F#中的 map2 。您可能必须使用列表理解而不是 repeat ) 现在:

makeRow [1,2,3] 1
=> [3,2,1]
makeRow [1,2,3] 2
=> [6,4,2]

要为所有 i 获取此信息,我们需要映射信号:

map (makeRow filter) signal
=> [[3,2,1], [3,2,1], [3,2,1]]

好。现在我们只需要一种正确组合行的方法。我们可以通过注意组合将新行添加到现有数组来实现这一点,除了第一个元素,它被卡在前面。例如:

[[3,2,1], [6,4,2]] = 3 : [2 + 6, 1 + 4] ++ [2]
// or in F#
[[3; 2; 1]; [6; 4; 2]] = 3 :: [2 + 6; 1 + 4] @ [2]

因此,我们只需编写一些代码来执行此操作:

combine (front:combinable) rest =
    let (combinable',previous) = splitAt (length combinable) rest in
    front : zipWith (+) combinable combinable' ++ previous

现在我们有了一种生成所有行的方法以及一种将新行与现有数组相结合的方法,我们所要做的就是用折叠将两者放在一起:

convolve signal filter = foldr1 combine (map (makeRow filter) signal)

convolve [1,1,1] [1,2,3]
=> [3,5,6,3,1]

这是一个功能版本。只要你理解 foldr zipWith ,我认为这是相当清楚的。但它至少与命令式版本一样长,并且像其他评论者所说的那样,在F#中效率可能较低。这是一个地方的整个事情。

makeRow filter i = zipWith (*) (repeat i) (reverse filter)
combine (front:combinable) rest =
    front : zipWith (+) combinable combinable' ++ previous
    where (combinable',previous) = splitAt (length combinable) rest
convolve signal filter = foldr1 combine (map (makeRow filter) signal)

编辑:

正如所承诺的,这是一个F#版本。这是在VS2005上用严肃的版本(1.9.2.9)编写的,所以要小心。另外我在标准库中找不到 splitAt ,但后来我不太了解F#。

open List
let gen value = map (fun _ -> value)
let splitAt n l = 
  let rec splitter n l acc =
    match n,l with
    | 0,_ -> rev acc,l
    | _,[] -> rev acc,[]
    | n,x::xs -> splitter (n - 1) xs (x :: acc)
  splitter n l [] 
let makeRow filter i = map2 ( * ) (gen i filter) (rev filter)
let combine (front::combinable) rest =
  let combinable',previous = splitAt (length combinable) rest
  front :: map2 (+) combinable combinable' @ previous
let convolve signal filter = 
  fold1_right combine (map (makeRow filter) signal)

实际上,您通常希望避免循环(普通,嵌套,等等)以及函数式编程中的任何变量。

F#中恰好有一个非常简单的解决方案(可能几乎所有其他功能语言):

let convolution = Seq.zip seq1 seq2

zip 函数简单地将两个序列组合成一对,其中包含 seq1 中的元素和 seq2 中的元素。作为注释, List Array 模块也存在类似的zip函数,以及将三个列表组合成三元组的变体( zip3 )。如果你想将tom通常压缩(或“convolute”)n列表放入n元组列表中,那么你需要编写自己的函数,但它非常简单。

(我一直按照此描述进行卷积 - 告诉我你是否还有其他意思。)

原则上,应该可以使用(快速)傅里叶变换或相关(离散)余弦变换来合理有效地计算两个函数的卷积。您计算两个函数的FFT,将它们相乘,并对结果应用逆FFT。

数学背景

这就是理论。在实践中,你最好找到一个为你实现它的数学库。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top