如何检测无限序列中的重复数字?我试过 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)
答案 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()
答案 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
,这仍然是一个有限的数字。
让L
为d
的长度,D_i = d_{i mod L}
为i < F
。 D_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的算法不能直接适用于您的情况。但他们可能会扩展到完成任务:
k
)。k
元素k
- 元素子序列的下一个匹配项。k
,请更新k
并继续执行第3步。如果序列循环从第一个元素开始,则忽略步骤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)
为了弥补这一点,您需要跟踪偏移量。