我有一个稍微不寻常的问题,但我试图避免重新编码FFT。通用编程:日志FFT或高精度卷积(python)
在一般情况下,我想知道这一点:如果我有一个用于float
类型实现的算法,但只要有一定的操作集定义(如复杂的数字,这也定义了它的工作+
,*
, ...),在支持这些操作的另一种类型上使用该算法的最佳方式是什么?在实践中,这是棘手的,因为通常数值算法是为了速度而不是通用性编写的。
具体做法是:
我的工作与价值观具有非常高的动态范围,所以我想将它们存储在日志空间(主要是为了避免下溢)。
我想是一些系列的FFT的日志:
x = [1,2,3,4,5]
fft_x = [ log(x_val) for x_val in fft(x) ]
即使这会导致显著下溢。我想是存储日志的价值观和到位的+
使用+
代替*
和logaddexp
等
我如何做到这一点的想法是实现一个简单的LogFloat类,定义这些基本操作(但在日志空间中运行)。然后,我可以简单地运行FFT代码,让它使用我的记录值。
class LogFloat:
def __init__(self, sign, log_val):
assert(float(sign) in (-1, 1))
self.sign = int(sign)
self.log_val = log_val
@staticmethod
def from_float(fval):
return LogFloat(sign(fval), log(abs(fval)))
def __imul__(self, lf):
self.sign *= lf.sign
self.log_val += lf.log_val
return self
def __idiv__(self, lf):
self.sign *= lf.sign
self.log_val -= lf.log_val
return self
def __iadd__(self, lf):
if self.sign == lf.sign:
self.log_val = logaddexp(self.log_val, lf.log_val)
else:
# subtract the smaller magnitude from the larger
if self.log_val > lf.log_val:
self.log_val = log_sub(self.log_val, lf.log_val)
else:
self.log_val = log_sub(lf.log_val, self.log_val)
self.sign *= -1
return self
def __isub__(self, lf):
self.__iadd__(LogFloat(-1 * lf.sign, lf.log_val))
return self
def __pow__(self, lf):
# note: there may be a way to do this without exponentiating
# if the exponent is 0, always return 1
# print self, '**', lf
if lf.log_val == -float('inf'):
return LogFloat.from_float(1.0)
lf_value = lf.sign * math.exp(lf.log_val)
if self.sign == -1:
# note: in this case, lf_value must be an integer
return LogFloat(self.sign**int(lf_value), self.log_val * lf_value)
return LogFloat(self.sign, self.log_val * lf_value)
def __mul__(self, lf):
temp = LogFloat(self.sign, self.log_val)
temp *= lf
return temp
def __div__(self, lf):
temp = LogFloat(self.sign, self.log_val)
temp /= lf
return temp
def __add__(self, lf):
temp = LogFloat(self.sign, self.log_val)
temp += lf
return temp
def __sub__(self, lf):
temp = LogFloat(self.sign, self.log_val)
temp -= lf
return temp
def __str__(self):
result = str(self.sign * math.exp(self.log_val)) + '('
if self.sign == -1:
result += '-'
result += 'e^' + str(self.log_val) + ')'
return result
def __neg__(self):
return LogFloat(-self.sign, self.log_val)
def __radd__(self, val):
# for sum
if val == 0:
return self
return self + val
然后,想法是构建的LogFloat
秒的列表,然后在FFT使用它:
x_log_float = [ LogFloat.from_float(x_val) for x_val in x ]
fft_x_log_float = fft(x_log_float)
这绝对可以做,如果我再执行FFT(和简单。使用LogFloat
的地方我会用float
过,但我想我会请教这是一个相当反复出现的问题:我有一只股票的算法,我想在日志空间操作(和它仅使用像“操作了一把+ ',' - ','','/'等)。
这让我想起使用模板编写泛型函数,以便返回参数,参数等由相同类型构造而成。例如,如果你可以做一个浮点数的FFT,你应该能够很容易地对复数值做一个(通过简单地使用一个为复数值提供必要操作的类)。
当前标准的,它看起来像所有的FFT实现是带血的速度写的,所以不会很一般。因此,截至目前,它看起来像我不得不重新实现FFT泛型类型...
的原因,我这样做是因为我要非常高精度的卷积(和N^2运行非常缓慢)。 任何意见将不胜感激。
*注意,我可能需要实现三角函数的LogFloat
,这将是罚款。
编辑: 这并不工作,因为LogFloat
是一个交换环(和它不要求执行的三角函数的LogFloat
)。最简单的方法是重新实现FFT,但@ J.F.Sebastian还指出了一种使用通用卷积的方法,该方法避免了对FFT进行编码(使用DSP教科书或使用DSP教科书或the Wikipedia pseudocode也很容易)。
我不知道你的问题是什么。如果fft是用python编写的,那么上面应该(模仿雄心勃勃的疯狂)工作。如果它调用了一个c实现,那么它将无法工作,因为c代码就是c代码,它不会执行python的操作。 那么问题是什么? – 2012-03-10 01:25:15
有[通用卷积](http://www.math.ucla.edu/~jimc/mathnet_d/sage/reference/sage/rings/polynomial/convolution.html)。 'LogFloat'可能不是处理下溢的最佳方法。 – jfs 2012-03-10 05:22:41
DSP教科书和教程网站中的大量FFT提供了非常简单的FFT源代码示例,通常大约有1或2页代码。 (在我的DSP网站上有几个FFT只有30到40行BASIC) – hotpaw2 2012-03-10 05:50:38