使用python进行递归实现,以获得冰雹序列或Collat​​z猜想的数学难题?

时间:2016-09-15 09:22:01

标签: python algorithm recursion

有一个数学难题:

  1. 选择正整数n作为开始。
  2. 如果n是偶数,则除以2。
  3. 如果n为奇数,则将其乘以3,然后加1.
  4. 继续此过程,直到n为1。
  5. 我想编写一个递归函数,它将n作为参数,然后返回一个元组,其中包含一个过程中n的轨道序列,以及序列的长度。但失败了。

    我的代码有什么问题?如何完成任务?

    def hailstone(n):
        """the implementation of recursive one,
        return a tuple that includes the hailstone sequence
        starting at n, and the length of sequence.
    
        >>> a = hailstone(1)
        ([1, 4, 2, 1], 4)
        """
        if n % 2 == 0:
            n = n//2
        else:
            n = n*3 + 1
    
        if n == 1:
            return [n]
        else:
            """
            for some magic code here,
            it will return a tuple that includes the list track of n,
            and the length of sequence,
            like ([1, 4, 2, 1], 4)
            """
            return ([n for some_magic in hailstone(n)], lenght_of_seq)
    

2 个答案:

答案 0 :(得分:2)

您可以使用累加器:

def hailstone(n):
    """
    return a tuple that includes the hailstone sequence
    starting at n, and the length of sequence.
    1) Pick a positive integer n as start.
    2) If n is even, then divide it by 2.
    3) If n is odd, multiply it by 3, and add 1.
    4) continue this process until n is 1.
    """

    def hailstone_acc(n, acc):
        acc.append(n)
        if n == 1:
            return acc, len(acc)
        elif n % 2 == 0:
            return hailstone_acc(n//2, acc)
        else:
            return hailstone_acc(3*n + 1, acc)

    if n == 1:
        return hailstone_acc(4,[1])
    else:
        return hailstone_acc(n, [])

现在,在行动中:

(trusty)juan@localhost:~/workspace/testing/hailstone$ python -i hailstone.py 
>>> hailstone(1)
([1, 4, 2, 1], 4)
>>> hailstone(4)
([4, 2, 1], 3)
>>> hailstone(23)
([23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1], 16)
>>> 

答案 1 :(得分:2)

首先,您需要决定是使用迭代还是递归过程 - 在Python中它并不重要(因为Python不使用尾调用优化),但在语言中它可以。有关迭代/递归过程的更深入解释,请参阅this question

在任何一种情况下,通常最好从结束条件开始并从那里开始工作。在这种情况下,n == 1

递归过程

让我们从递归过程开始。在这种情况下,状态在调用链中维护,一旦我们到达最里面的调用(并且我们正在返回调用链),就会计算结果。

def hailstone(n):
    if n == 1:
        return [n], 1

好吧,这就是结束条件 - 现在我们确保函数将在n为1后返回。让我们继续并添加其余条件:

def hailstone_rec(n):
    if n == 1:
        return (n,), 1

    if n % 2 == 0: # Even
        rest = hailstone_rec(n//2)
    else: # Odd
        rest = hailstone_rec(n*3+1)

    return (n,) + rest[0], rest[1] + 1

n不为1时,此处发生的是我们首先递归计算序列的其余部分,然后将当前调用的值添加到该结果中。因此,如果n为2,则表示我们将计算将返回hailstone_rec(2//2)的{​​{1}},然后我们添加当前值并返回结果(((1,), 1))。

迭代过程

使用迭代过程,在继续调用链的同时计算结果,并在递归时将当前状态传递给下一个函数调用。这意味着结果不依赖于调用链中较高的调用,这意味着当到达结尾时,最里面的函数可以返回结果。这在具有尾调用优化的语言中具有重要意义,因为外部调用的状态可能被丢弃。

使用此过程实现函数时,使用带有额外参数的内部帮助函数来传递状态通常很有帮助,所以让我们定义一个面向用户的函数和一个内部帮助函数:

((2, 1), 2)

可以看出,面向用户的函数只是在状态参数设置为初始值的情况下调用内部函数。让我们继续实现实际的逻辑,所有逻辑都将驻留在帮助器中。与前面的解决方案一样,让我们​​从结束条件(# user facing function def hailstone_it(n): # Call the helper function with initial values set return hailstone_it_helper(n, (), 0) # internal helper function def hailstone_it_helper(n, seq, length): pass )开始:

n == 1

这一次,我们从前一个调用中获取部分结果,添加当前值,然后返回。现在,处理其余的案例:

def hailstone_it_helper(n, seq, length):
    if n == 1:
        return seq + (n,), length + 1

当在此处递归时,我们传入序列中的下一个n(取决于当前n是偶数还是奇数),以及到目前为止的结果。

更多的pythonic溶液

最后,因为你通常不会在Python中编写这样的代码,所以让我们用一个更加pythonic的解决方案作为奖励(即一个根本不使用递归的解决方案):

def hailstone_it_helper(n, seq, length):
    if n == 1:
        return seq + (n,), length + 1

    if n % 2 == 0: # Even
        return hailstone_it_helper(n//2, seq + (n,), length + 1)
    else:
        return hailstone_it_helper(n*3+1, seq + (n,), length + 1)

def hailstone_it(n):
    return hailstone_it_helper(n, (), 0)