我正在实施一个计算方程的程序:F(n)= F(n-1)+'a'+ func1(func2(F(n-1)))。
func1取每个'a'并使其成为'c',每个'c'变为'a'。
func2反转字符串(e.x。“xyz”变为“zyx”)。
我想计算F的Kth字符(10 ** 2017)。 基本规则是F(0)=“”(空字符串),例子是F(1)=“a”,F(2)=“aac”,依此类推。
我该如何有效地做到这一点?
我的代码的基本部分是:
def op1 (str1):
if str1 == 'a':
return 'c'
else:
return 'a'
def op2 (str2):
return str2[::-1]
sinitial = ''
while (counter < 10**2017):
Finitial = Finitial + 'a' + op1(op2(Finitial))
counter += 1
print Finitial
答案 0 :(得分:36)
首先修复原始代码并定义一个函数来计算小F(n)
的{{1}}。我们还会打印n
的前几个值。以下所有代码均适用于Python 3;如果您使用的是Python 2,则需要进行一些小的更改,例如将F
替换为str.maketrans
,将string.maketrans
替换为range
。
xrange
这给出了以下输出:
swap_ac = str.maketrans({ord('a'): 'c', ord('c'): 'a'})
def F(n):
s = ''
for _ in range(n):
s = s + 'a' + s[::-1].translate(swap_ac)
return s
for n in range(7):
print("F({}) = {!r}".format(n, F(n)))
此时有几点意见:
F(0) = ''
F(1) = 'a'
F(2) = 'aac'
F(3) = 'aacaacc'
F(4) = 'aacaaccaaaccacc'
F(5) = 'aacaaccaaaccaccaaacaacccaaccacc'
F(6) = 'aacaaccaaaccaccaaacaacccaaccaccaaacaaccaaaccacccaacaacccaaccacc'
是一个长度为F(n)
的字符串。这意味着2**n-1
快速增长 。计算F(n)
已经需要一些严肃的硬件:即使我们每位存储一个字符,我们也需要超过100太字节来存储完整的字符串。 F(50)
比太阳系中估计的原子具有更多的特征。所以计算 F(200)
的想法直接是可笑的:我们需要一种不同的方法。
通过构造,每个F(10**2017)
都是F(n)
的前缀。所以我们真正拥有的是一个明确定义的无限字符串,其中每个F(n+1)
仅仅为我们提供了该无限字符串的第一个F(n)
字符,我们正在寻找计算其2**n-1
个字符。出于任何实际目的,k
也可能 无限字符串:例如,当我们进行计算时,我们不需要检查F(10**2017)
,因为超出此范围的k < 2**(10**2017)-1
甚至不能在此宇宙中以正常的二进制表示法表示。
幸运的是,字符串的结构非常简单,直接计算k
字符非常简单。当我们在偶数和奇数位置查看角色时,主要线索就出现了:
k
偶数位置的字符只是在>>> F(6)[::2]
'acacacacacacacacacacacacacacacac'
>>> F(6)[1::2]
'aacaaccaaaccaccaaacaacccaaccacc'
和a
之间交替显示(根据构造,它可以直接证明这是真的)。因此,如果我们的c
是偶数,我们只需查看k
是奇数还是偶数,以确定我们是否会获得k/2
或a
。< / p>
奇怪的位置怎么样?好c
应该看起来有点熟悉:它只是F(6)[1::2]
:
F(5)
同样,可以直接证明(例如,通过归纳)这不仅仅是巧合,而>>> F(6)[1::2] == F(5)
True
对于所有非负F(n+1)[1::2] == F(n)
。
我们现在有一种有效的方法来计算无限字符串中的n
字符:如果k
是偶数,我们只看k
的奇偶校验。如果k/2
为奇数,则我们知道位置k
处的字符等于位置k
处的字符。所以这是计算这个角色的第一个解决方案:
(k-1)/2
检查这是否正确:
def char_at_pos(k):
"""
Return the character at position k of the string F(n), for any
n satisfying 2**n-1 > k.
"""
while k % 2 == 1:
k //= 2
return 'ac'[k//2%2]
但我们可以做得更好。我们有效地盯着>>> ''.join(char_at_pos(i) for i in range(2**6-1))
'aacaaccaaaccaccaaacaacccaaccaccaaacaaccaaaccacccaacaacccaaccacc'
>>> ''.join(char_at_pos(i) for i in range(2**6-1)) == F(6)
True
的二进制表示,删除所有尾随的k
和下一个'1'
,然后简单地查看下一位以确定我们是否&#39}。得到'0'
或'a'
。识别尾随1可以通过位操作技巧来完成。这为我们提供了以下半混淆的无循环解决方案,我将它留给您放松:
'c'
再次,让我们检查一下:
def char_at_pos2(k):
"""
Return the character at position k of the string F(n), for any
n satisfying 2**n-1 > k.
"""
return 'ac'[k//(1+(k+1^k))%2]
最后的评论:这是一个非常着名且经过充分研究的序列:它被称为龙曲线序列,或者regular paper-folding sequence,并且是sequence A014577在线的百科全书整数序列。某些Google搜索可能会为您提供许多其他方法来计算其元素。另请参阅this codegolf question。
答案 1 :(得分:-2)
根据您已经编码的内容,这是我的建议:
def main_function(num):
if num == 0:
return ''
previous = main_function(num-1)
return previous + 'a' + op1(op2(previous))
print(main_function(10**2017))
P.S:我不确定效率。