我写下这篇答案是为了自己理解,希望您能耐心看完。
正如您所提到的那样,相比于生成大的
p
,更容易找出原始
x
个字符中
b[p][q]
的来源。为此,我们将使用循环来查找当前
b[p][q]
的来源,从而减少
p
直到它在
1
和
x
之间,并且
q
减少到
1
。
让我们以
x=3
的例子来看看是否可以得出一个公式:
p N(p) b[p]
- ---- ----
1 1 a
2 1 b
3 1 c
4 3 a b c
5 5 b c abc
6 9 c abc bcabc
7 17 abc bcabc cabcbcabc
8 31 bcabc cabcbcabc abcbcabccabcbcabc
9 57 cabcbcabc abcbcabccabcbcabc bcabccabcbcabcabcbcabccabcbcabc
序列很清晰:
N(p) = N(p-1) + N(p-2) + N(p-3)
,其中
N(p)
是
b
的第p个元素中字符的数量。给定
p
和
x
,您可以计算范围
[1, p]
内所有
N
。这将帮助您确定
b[p][q]
来自哪个先前的
b
元素。
举例说明,假设
x=3
,
p=9
,
q=45
。
- 上面的图表给出了
N(6)=9
,N(7)=17
和N(8)=31
。由于45>9+17
,因此您知道b[9][45]
来自b[8][45-(9+17)] = b[8][19]
。
- 继续迭代/递归,
19>9+5
,所以b[8][19] = b[7][19-(9+5)] = b[7][5]
。
- 现在
5>N(4)
,但5<N(4)+N(5)
,因此b[7][5] = b[5][5-3] = b[5][2]
。
b[5][2] = b[3][2-1] = b[3][1]
- 由于
3 <= x
,我们有终止条件,并且b[9][45]
是b[3]
中的c
。
如果有起始值p
、q
、x
和b
,那么这样的计算可以很容易地通过递归或迭代来完成。我的方法需要p
个数组元素来计算整个序列的N(p)
。如果以递归方式工作,可以在数组或堆栈上分配它们。
以下是使用原生Python实现的参考代码(不需要外部导入,尽管numpy可能会有所帮助):
def so38509640(b, p, q):
"""
p, q are integers. b is a char sequence of length x.
list, string, or tuple are all valid choices for b.
"""
x = len(b)
if p <= x:
if q != 1:
raise ValueError('q={} out of bounds for p={}'.format(q, p))
return p, b[p - 1]
N = [1] * p
for i in range(x, p):
N[i] = sum(N[i - x:i])
print('N =', N)
if q > N[-1]:
raise ValueError('q={} out of bounds for p={}'.format(q, p))
print('b[{}][{}]'.format(p, q), end='')
while p > x:
offset = 0
for i in range(p - x - 1, p):
if i == p - 1:
raise ValueError('q={} out of bounds for p={}'.format(q, p))
if offset + N[i] >= q:
q -= offset
p = i + 1
print(' = b[{}][{}]'.format(p, q), end='')
break
offset += N[i]
print()
return p, b[p - 1]
调用so38509640('abc', 9, 45)
会产生以下结果
N = [1, 1, 1, 3, 5, 9, 17, 31, 57]
b[9][45] = b[8][19] = b[7][5] = b[5][2] = b[3][1]
(3, 'c')
同样地,在问题的示例中,
so38509640('abc', 7, 5)
会产生预期的结果:
N = [1, 1, 1, 3, 5, 9, 17]
b[7][5] = b[5][2] = b[3][1]
(3, 'c') # <
对不起,我想不出更好的函数名: ) 这是足够简单的代码,它应该在Py2和3中同样有效,尽管range
函数/类存在差异。
如果有一种非迭代解决这个问题的方法,我会非常好奇。也许有一种使用模运算或其他方法的方式...