我有一个稍微不寻常的问题,但我试图避免重新编码FFT。
一般来说,我想知道这个:如果我有一个为float
类型实现的算法,但它可以在定义某组操作的任何地方工作(例如复数,也为其定义{ {1}},+
,...),在支持这些操作的另一种类型上使用该算法的最佳方法是什么?在实践中,这很棘手,因为通常数字算法是为了速度而非通用性而编写的。
具体地:
我正在使用具有非常高动态范围的值,因此我想将它们存储在日志空间中(主要是为了避免下溢)。
我想要的是某些系列的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
的列表,然后在FFT中使用它:
LogFloat
如果我重新实现FFT(并且在我之前使用x_log_float = [ LogFloat.from_float(x_val) for x_val in x ]
fft_x_log_float = fft(x_log_float)
的任何地方只使用LogFloat
),这肯定可以完成,但我想我会征求意见。这是一个相当反复出现的问题:我有一个库存算法,我想在日志空间中操作(它只使用一些操作,如'+',' - ','','/'等)。< / p>
这让我想起用模板编写泛型函数,以便返回参数,参数等是从相同的类型构造的。例如,如果你可以对浮点数进行FFT,你应该能够轻松地对复数值进行迭代(通过简单地使用为复杂值提供必要操作的类)。
目前看来,看起来所有的FFT实现都是针对最前沿的速度编写的,因此不是很通用。到目前为止,看起来我不得不为通用类型重新实现FFT ......
我这样做的原因是因为我想要非常高精度的卷积(并且 N ^ 2 运行时非常慢)。 任何建议将不胜感激。
*注意,我可能需要为float
实现三角函数,这没关系。
修改
这确实有效,因为LogFloat
是一个可交换的环(并且它不需要为LogFloat
实现三角函数)。最简单的方法是重新实现FFT,但@JFSebastian也指出了一种使用LogFloat
通用卷积的方法,这避免了编码FFT(再次,使用DSP教科书或the Wikipedia pseudocode)。
答案 0 :(得分:1)
我承认我在你的问题中没有完全跟上数学。然而,听起来你真正想知道的是如何处理极小和大(绝对值)数字而不会出现下溢和溢出。除非我误解你,否则我认为这类似于我处理货币单位的问题,而不是由于四舍五入而导致数十亿美元交易的便士。如果是这种情况,我的解决方案是Python的内置十进制数学模块。该文档适用于Python 2和Python 3。简短版本是十进制数学是一种任意精度的浮点和定点类型。 Python模块符合IBM / IEEE十进制数学标准。在Python 3.3(目前处于alpha形式,但我一直使用它没有任何问题),该模块已在C中重写,速度提高了100倍(在我的快速测试中)。
答案 1 :(得分:0)
您可以将时域样本缩放大量s以避免下溢,然后,如果
F (f(t))= X (j * w)
然后
F (s f(s * t))&lt; - &gt; <强> X 强>(W / S)
现在使用卷积定理,您可以找出如何缩放最终结果以消除缩放因子的影响。