【问题标题】:Reccurence algorithm: find position after n moves递归算法:在 n 次移动后找到位置
【发布时间】:2016-12-22 22:10:53
【问题描述】:

简介

我正在尝试对Finding the position at n? 中引用的问题进行编码。为此,我只是使用了问题中发布的公式。我没有仔细阅读公式和答案,它们对我来说有点太复杂了。我用python写了代码,这里有表示。

算法描述(复制)

这种舞蹈要求每个表演者都遵循精确的舞步顺序:

• 第 0 阶段:首先,通过将起点设置在位置 0 来避开障碍物

• 第 1 阶段:向前迈出一步(+1 步)

• 第 2 阶段:后退两步(-2 步)

• 要遵循,每次都将获得您下一步必须采取的步骤和方向,这要归功于特定的计算:您在前一阶段采取的步数减去您在前一阶段采取的步数进入倒数第二阶段。

也就是说,在第 3 阶段,舞者必须向后退 3 步 (-2 - 1)。

在第 3 阶段,舞者在位置 -4。

阶段(n) = 阶段(n-1) - 阶段(n-2)

pos(n) = pos(n-1) + stage(n)

在第 4 阶段,舞者在位置 -5。


源代码

#!/usr/bin/python
if __name__=="__main__":
    s = [0, 1, -2]
    p = [0, 1, -1]
    for n in range(3, 5):
        diff = s[n - 1] - s[n - 2]
        s.append(diff)
        p.append(p[n - 1] + diff)
        print "Position at stage %s is %s" %(n, p[len(p) - 1])

问题

我的问题是假设我们有超过 1000 万个阶段。列表 p 和 s 将增长并可能导致内存出现问题。有没有办法解决这个问题。除了使用列表之外,我找不到其他解决方案。

如果我删除第一个元素s.pop() p.pop(),这是一个超出范围异常的索引。这是正常的。除此之外,我不知道从哪里继续。


更新

这比我想象的要简单。

#!/usr/bin/python
if __name__=="__main__":
    last_move = -2
    penultimate_move = 1
    previous_position = -1
    for n in range(3, 5):
        #compute current move and position
        current_move = last_move - penultimate_move
        current_position = previous_position + current_move
        
        #do switch in here
        penultimate_move = last_move
        last_move = current_move
        previous_position = current_position

        print "current position %s" %current_position

【问题讨论】:

  • 你总共只需要三个变量:当前位置、最后一步和倒数第二步。剩下的只是迭代。问题出在哪里?
  • 或者,将其转换为您的代码:您只使用索引 n-1 和 n-2。为什么要记住其余的?
  • 我的问题是如何记录这些变量。这就是我尝试使用列表的原因。这让我陷入了麻烦,我想哈哈

标签: python algorithm


【解决方案1】:

对此有一个简单的解决方案:在第 6、7 和 8 阶段,位置恰好分别为 0、1 和 -1,它们与初始位置相同。由于下一个阶段和位置只取决于前一对阶段和上一个位置,因此保证相同的序列重复。

因此计算给定 n 位置的函数可以是:

def position(n):
    return [0, 1, -1, -4, -5, -3][n % 6]

以及计算阶段数n的函数:

def stage(n):
    return [3, 1, -2, -3, -1, 2][n % 6]

【讨论】:

  • 太聪明了
  • 它在 c# 中的意义是什么?是两个数组相乘吗?
  • @Benoit,不,第一部分是一个数组,第二部分是对该数组的索引访问。因此,在 C 风格的语言中,position 函数将是 int a[] = {0, 1, -1, -4, -5, -3}; return a[n % 6];
  • @trincot 谢谢,很明显不,非常聪明。我从来没有在 python 中开发过 ^^
【解决方案2】:

对于此类问题,您必须尝试在某些情况下找到解决方案,可能您会找到像我发现的那样可以帮助您在 O(1) 时间内解决此问题的模式,并且只需 6 个元素的列表。

让我们迭代它几个初始阶段,

           Steps to take      New position
Stage 0        ---                0
Stage 1         1                 1
Stage 2        -2                -1
Stage 3        -3                -4
Stage 4        -1                -5
Stage 5         2                -3
Stage 6         3                 0
Stage 7         1                 1
Stage 8        -2                -1
Stage 9        -3                -4
Stage 10       -1                -5

所以你可以看到在Stage 6 之后模式重复了。所以下面的python代码将帮助你更快地解决这个问题。

def getpos(n):
    '''Returns the position of the performer after nth stage.'''
    ls = [0, 1, -1, -4, -5, -3]
    return ls[n % 6]

def getstep(n):
    '''Returns the step taken at nth stage'''
    ls = [3, 1, -2, -3, -1, 2]
    if n == 0:
        return None
    return ls[n % 6]

函数getpos()getstep() 是你在这个问题中需要用到的实用函数。

【讨论】:

  • 谢谢。我正在检查sheesh。这太聪明了。熄灭
  • 看起来您不小心在最后的函数签名中包含了类型声明...
  • @HaniGoc 看看我们上面所做的迭代,尝试更多到 12,你可以看到,只要我们有一个可以被 6 整除的阶段数,我们将采取 3 个步骤,在阶段 1、7、 13 我们迈出一步,以此类推。
【解决方案3】:

好的;让我们从递归定义开始:

stage(n) = stage(n-1) - stage(n-2)
pos(n) = pos(n-1) + stage(n)

现在,让我们创建三个变量:

pos is for pos(n)     -- position
ult is for stage(n-1) -- ultimate
pen is for stage(n-2) -- penultimate

更新很简单,如上所示。 这个初始值在问题和你的代码中给出:

pos = -1
ult = -2
pen = 1

现在,每次通过循环,更新上面给出的值。

stage_n = ult - pen
pos += stage_n

最后一步是为下一次迭代做准备。当我们再迈出一步时,那将成为下一次迭代的终极;当前的大招被降到倒数第二个:

pen = ult
ult = stage_n

...现在我们准备好回到循环的顶部。

总的来说,它看起来像这样:

limit = <wherever you want to stop>

pos = -1
ult = -2
pen = 1

for n in range (3, limit):
    stage_n = ult - pen
    pos += stage_n

    pen = ult
    ult = stage_n

print "Step", limit, "is at position", pos

【讨论】:

    【解决方案4】:

    您拥有 1000 万多个阶段的原因是为了迫使您学习技巧。

    它有助于以差分方程的形式查看它,

    s(n+1) = s(n) - s(n-1)
    p(n+1) = p(n) + s(n+1)
    

    将每个步骤视为 2 个数组 s_np_n 的状态,

    s_n = [ s(n), s(n-1) ]
    p_n = [ p(n) ] 
    

    我们如何找到s_n+1p_n+1?看公式,

    s_n+1 = [ s_n[0] - s_n[1], s_n[0] ]
    p_n+1 = [ s_n+1[0] + p_n[0] ]
    

    希望这是有道理的。您只需要指定求解方程的状态,而不是整个历史。我可能弄错了,但我认为这在 LTE 系统中被称为“无记忆”。本质上,您正在计算一个差分方程(离散动态系统),这在 DSP 中很常见。我在实时 DSP 实验室中学习了这项技术。 :)

    【讨论】:

      【解决方案5】:
      public int calculate( int iterationNumber )
         {
            int position = 0;
            int step1 = 1;
            position = position + step1;
            iterationNumber = iterationNumber - 1;
            int step2 = -2;
            position = position + step2;
            iterationNumber -= 1;
            while( iterationNumber > 0 )
            {
               position = position + ( step2 - step1 );
               int buffer = step1;
               step1 = step2;
               step2 = step2 - buffer;
               iterationNumber -= 1;
            }
            return position;
         }
      
         @Test
         public void positionOfTheDancerTest()
         {
            assertEquals( -4, positionOfTheDancer.calculate( 3 ) );
            assertEquals( -5, positionOfTheDancer.calculate( 100000 ) );
            assertEquals( 1, positionOfTheDancer.calculate( 2147483647 ) );
         }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-06-23
        • 1970-01-01
        • 1970-01-01
        • 2023-03-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多