Я пишу программное обеспечение для обработки сигналов и начинаю с написания функции дискретной свертки< /а>сильный>.
Это отлично работает для первых десяти тысяч или около того списка значений, но по мере того, как они становятся больше (скажем, 100 000), я, конечно, начинаю получать ошибки StackOverflow.
К сожалению, у меня много проблем с преобразованием имеющегося у меня императивного алгоритма свертки в рекурсивную и ленивую версию, которая на самом деле достаточно быстра для использования (имея хотя бы немного Элегантность тоже не помешала бы).
Я также не на 100% уверен, что эта функция работает правильно, но, пожалуйста, дайте мне знать, если я что-то упускаю или делаю что-то не так. Я думаю, что это правильно.
(defn convolve
"
Convolves xs with is.
This is a discrete convolution.
'xs :: list of numbers
'is :: list of numbers
"
[xs is]
(loop [xs xs finalacc () acc ()]
(if (empty? xs)
(concat finalacc acc)
(recur (rest xs)
(if (empty? acc)
()
(concat finalacc [(first acc)]))
(if (empty? acc)
(map #(* (first xs) %) is)
(vec-add
(map #(* (first xs) %) is)
(rest acc)))))))
Я был бы очень признателен за любую помощь: я все еще разбираюсь в Clojure, и сделать это элегантным, ленивым и/или рекурсивным было бы замечательно.
Я немного удивлен, насколько сложно выразить алгоритм, который достаточно легко выразить на императивном языке Лиспа. Но, возможно, я делаю это неправильно!
EDIT: Просто чтобы показать, как легко выразить на императивном языке, и дать людям алгоритм, который хорошо работает и легко читается, вот версия Python. Помимо того, что он короче, лаконичнее и гораздо проще для понимания, он выполняется на несколько порядков быстрее, чем код Clojure: даже мой императивный код Clojure, использующий массивы Java.
from itertools import repeat
def convolve(ns, ms):
y = [i for i in repeat(0, len(ns)+len(ms)-1)]
for n in range(len(ns)):
for m in range(len(ms)):
y[n+m] = y[n+m] + ns[n]*ms[m]
return y
Здесь, с другой стороны, императивный код Clojure. Он также удаляет последние, не полностью погруженные значения из свертки. Таким образом, помимо того, что он медленный и уродливый, он не на 100% функционален. Ни функциональный.
(defn imp-convolve-1
[xs is]
(let [ys (into-array Double (repeat (dec (+ (count xs) (count is))) 0.0))
xs (vec xs)
is (vec is)]
(map #(first %)
(for [i (range (count xs))]
(for [j (range (count is))]
(aset ys (+ i j)
(+ (* (nth xs i) (nth is j))
(nth ys (+ i j)))))))))
Это так обескураживает. Пожалуйста, кто-нибудь, покажите мне, что я только что пропустил что-то очевидное.
ИЗМЕНИТЬ 3:
Вот еще одна версия, которую я придумал вчера, показывающая, как я хотел бы выразить ее (хотя другие решения довольно элегантны, я просто добавляю еще одно!)
(defn convolve-2
[xs is]
(reduce #(vec-add %1 (pad-l %2 (inc (count %1))))
(for [x xs]
(for [i is]
(* x i)))))
Он использует эту служебную функцию vec-add
:
(defn vec-add
([xs] (vec-add xs []))
([xs ys]
(let [lxs (count xs)
lys (count ys)
xs (pad-r xs lys)
ys (pad-r ys lxs)]
(vec (map #(+ %1 %2) xs ys))))
([xs ys & more]
(vec (reduce vec-add (vec-add xs ys) more))))
(vec (reduce vec-add (vec-add xs ys) more))))
into-array
и ему подобные? - person Isaac   schedule 16.07.2010