用指数有效地计算数学公式

时间:2017-07-01 18:43:41

标签: python

我正在实施一个计算方程的程序: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

2 个答案:

答案 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)))

此时有几点意见:

  1. 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)的想法直接是可笑的:我们需要一种不同的方法。

  2. 通过构造,每个F(10**2017)都是F(n)的前缀。所以我们真正拥有的是一个明确定义的无限字符串,其中每个F(n+1)仅仅为我们提供了该无限字符串的第一个F(n)字符,我们正在寻找计算其2**n-1个字符。出于任何实际目的,k也可能 无限字符串:例如,当我们进行计算时,我们不需要检查F(10**2017) ,因为超出此范围的k < 2**(10**2017)-1甚至不能在此宇宙中以正常的二进制表示法表示。

  3. 幸运的是,字符串的结构非常简单,直接计算k字符非常简单。当我们在偶数和奇数位置查看角色时,主要线索就出现了:

    k

    偶数位置的字符只是在>>> F(6)[::2] 'acacacacacacacacacacacacacacacac' >>> F(6)[1::2] 'aacaaccaaaccaccaaacaacccaaccacc' a之间交替显示(根据构造,它可以直接证明这是真的)。因此,如果我们的c是偶数,我们只需查看k是奇数还是偶数,以确定我们是否会获得k/2a。< / 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:我不确定效率。