检测未知来源的时间段

时间:2012-06-26 10:09:36

标签: python algorithm math floyd-cycle-finding

如何检测无限序列中的重复数字?我试过 Floyd&布伦特检测算法却一无所获...... 我有一个生成数字,从0到9(含)的数字,我必须识别其中的一个句号。

示例测试用例:

import itertools

# of course this is a fake one just to offer an example
def source():
    return itertools.cycle((1, 0, 1, 4, 8, 2, 1, 3, 3, 1))

>>> gen = source()
>>> period(gen)
(1, 0, 1, 4, 8, 2, 1, 3, 3, 1)

4 个答案:

答案 0 :(得分:10)

经验方法

这是一个有趣的问题。您的问题的更一般形式是:

  

给定长度未知的重复序列,确定周期   信号。

确定重复频率的过程称为Fourier Transform。在您的示例情况下,信号是干净且离散的,但即使连续的噪声数据,以下解决方案也能正常工作! FFT将尝试通过在所谓的“波浪空间”或“傅里叶空间”中逼近它们来复制输入信号的频率。基本上,该空间中的峰值对应于重复信号。信号的周期与达到峰值的最长波长有关。

import itertools

# of course this is a fake one just to offer an example
def source():
    return itertools.cycle((1, 0, 1, 4, 8, 2, 1, 3, 3, 2))

import pylab as plt
import numpy as np
import scipy as sp

# Generate some test data, i.e. our "observations" of the signal
N = 300
vals = source()
X = np.array([vals.next() for _ in xrange(N)])

# Compute the FFT
W    = np.fft.fft(X)
freq = np.fft.fftfreq(N,1)

# Look for the longest signal that is "loud"
threshold = 10**2
idx = np.where(abs(W)>threshold)[0][-1]

max_f = abs(freq[idx])
print "Period estimate: ", 1/max_f

这给出了这种情况的正确答案10,但如果N没有干净地划分周期,你会得到一个接近的估计。我们可以通过以下方式对此进行可视化:

plt.subplot(211)
plt.scatter([max_f,], [np.abs(W[idx]),], s=100,color='r')
plt.plot(freq[:N/2], abs(W[:N/2]))
plt.xlabel(r"$f$")

plt.subplot(212)
plt.plot(1.0/freq[:N/2], abs(W[:N/2]))
plt.scatter([1/max_f,], [np.abs(W[idx]),], s=100,color='r')
plt.xlabel(r"$1/f$")
plt.xlim(0,20)

plt.show()

enter image description here

答案 1 :(得分:4)

Evgeny Kluev的回答提供了一种方法来获得可能正确的答案。

定义

假设您有一些重复序列的序列D。也就是说,有d长度为L的序列D_i = d_{i mod L}t_i,其中i是序列t的{​​{1}}元素,从0开始编号。我们会说序列d生成D

定理

给定一个序列D,你知道它是由一些有限序列t生成的。鉴于某些d,无法在有限的时间内决定是否生成D

证明

由于我们只允许有限的时间,我们只能访问有限数量的D元素。我们假设我们访问F的第一个D元素。我们选择了第一个F,因为如果我们只允许访问有限数,那么包含我们访问过的元素索引的集合是有限的,因此具有最大值。最大值为M。然后我们可以让F = M+1,这仍然是一个有限的数字。

Ld的长度,D_i = d_{i mod L}i < FD_F有两种可能性,它与d_{F mod L}相同,或者不是d。在前一种情况下,D_F似乎有效,但在后一种情况下却没有。在访问F+1之前,我们无法知道哪种情况属实。但是,这需要访问F元素,但我们仅限于F元素访问。

因此,对于任何d,我们都没有足够的信息来判断D是否生成d,因此无法在有限的时间内知道D是否会生成d

结论

可以在有限的时间内知道序列D 生成d,但这对您没有帮助。由于您希望找到生成D的序列D,但这包括能够证明某些序列生成D的其他事项。

除非您有关于d的更多信息,否则此问题无法解决。使这个问题可判定的一点信息是生成D的最短D长度的上限。如果您知道生成{{1}}的函数只有已知量的有限状态,则可以计算此上限。

因此,我的结论是除非你稍微改变一下规范,否则你无法解决这个问题。

答案 2 :(得分:1)

由于您的序列不是X n + 1 = f(X n )的形式,Floyd或Brent的算法不能直接适用于您的情况。但他们可能会扩展到完成任务:

  1. 使用Floyd或Brent的算法找到序列的一些重复元素。
  2. 查找具有相同值的下一个序列元素。这些元素之间的距离是假设的时期(k)。
  3. 请记住序列的下一个k元素
  4. 查找此k - 元素子序列的下一个匹配项。
  5. 如果子序列之间的距离大于k,请更新k并继续执行第3步。
  6. 重复步骤4几次以验证结果。如果重复子序列的最大长度是先验已知的,则使用适当的重复次数。否则使用尽可能多的重复,因为每次重复都会增加结果的正确性。
  7. 如果序列循环从第一个元素开始,则忽略步骤1并从步骤2开始(找到等于第一个元素的下一个序列元素)。

    如果序列循环不是从第一个元素开始,则Floyd或Brent算法可能会找到序列中不属于该循环的重复元素。因此,限制步骤2和4中的迭代次数是合理的,如果超出此限制,请继续执行步骤1.

答案 3 :(得分:1)

我不知道在这里应用适当的算法,但我的理解是,如果你只消耗了有限数量的术语,你永远无法确定你已经检测到了一段时间。无论如何,这是我提出的,这是一个非常天真的实现,更多的是从评论中教育而不是提供一个好的解决方案(我猜)。

def guess_period(source, minlen=1, maxlen=100, trials=100):
    for n in range(minlen, maxlen+1):
        p = [j for i, j in zip(range(n), source)]
        if all([j for i, j in zip(range(n), source)] == p
               for k in range(trials)):
            return tuple(p)
    return None

然而,这个“忘记”初始顺序并返回一个元组,它是实际周期的循环排列:

In [101]: guess_period(gen)
Out[101]: (0, 1, 4, 8, 2, 1, 3, 3, 1, 1)

为了弥补这一点,您需要跟踪偏移量。