你能解释一下这个递归的“n选择k”代码吗?

时间:2012-10-19 09:07:57

标签: python recursion

以下是参数n和k的子集问题的代码。 n代表学生总数,k代表我想要离开的学生数量。该代码试图给出从n个学生中拉出k个学生的可能组合的数量。

def subset(n, k): 
    if k == 0:
        return 1
    if n == k:
        return 1
    else:
        return subset(n-1, k-1) + subset(n-1, k)

我理解递归调用的第一部分,但是我无法理解+子集(n-1,k)部分。任何人都可以向我解释这个吗?

3 个答案:

答案 0 :(得分:21)

递归是基于一个简单的观察,我将给出一个组合论证,为什么它是真的,而不是通过公式的数学证明。

每当您从k中选择n个元素时,有两种情况:

  1. 您选择元素#n
  2. 您不选择元素#n
  3. 由于这些事件是互斥的,因此在选择#n时以及未选择#n时的组合数量会给出组合总数。

    选择元素#n

    由于我们已经选择了一个元素,因此我们只需要选择另一个k-1元素。此外,由于我们已经决定了一个元素 - 关于它是否包括在内 - 我们只需要考虑剩余的n-1元素。

    因此,选择元素#n的组合数量由

    给出
        subset(n - 1, k - 1)
    

    不选择元素#n

    仍然可以选择k个元素,但由于我们已经对元素#n做出了决定,因此只有n - 1元素可供选择。因此:

        subset(n - 1, k)
    

    基本情况

    递归使用的事实是,我们通常可以区分两种情况,即解决方案,其中元素n是解决方案的一部分,而不是解决方案的一部分。

    然而,并不总能做出这样的区分:

    • 选择所有元素时(对应下面代码中的案例n == k
    • 或根本不选择任何元素(对应于下面代码中的案例k == 0

    在这些情况下,只有一个解决方案,因此

    if k == 0:
        return 1
    if n == k:
        return 1
    

    确保其有效

    要做到这一点,我们需要说服自己(或证明)基本情况总是在某个时刻被击中。

    我们假设,n < k在某个时刻。根据我们的假设,n最初大于或等于k必须已经n = k的某个点,因为n和{ {1}}同时减少或仅k减少1,即遵循

    这意味着,必须要求n调用subset(n - 1, k)n减少到k以下。但是,这是不可能的,因为我们在n = k上有一个基本案例,我们返回一个常量1

    我们得出的结论是n在某些时候会降低n = k,或者在k时完全降低k = 0次。

    因此,基本案例有效。

答案 1 :(得分:1)

这是一个数学问题而不是编程问题。你在做的是计算二项式系数,公式是

(n, k) = (n-1, k-1) + (n-1, k) with all (n, 0) and (n, n) having a value of 1.

请参阅here以获取完整说明。可以看到递归解决方案here。如果上述链接无效,只需使用Google搜索即可。我怀疑你会在阅读维基百科上的这篇文章后得到更好的解释。

答案 2 :(得分:0)

代码片段:

row_number

使用两个组合事实:

if k == 0:
    return 1
if n == k:
    return 1

作为基本案例。

接下来,在递归调用之后:

nC0 = 1
nCn = 1

直接将以下事实转换为代码:

return subset(n-1, k-1) + subset(n-1, k)

如果您难以理解上述等式的含义,请继续阅读: 在nCr中,我们选择n中的r项。如果我们考虑任何特定项目,可以将选择分为两个互斥的类:

  • 项目出现在所选项目

    在这种情况下,我们必须从剩余的n-1项中选择剩余的r-1项。这可以在(n-1)C(k-1)中完成。

  • 在所选项目中
  • 项目

    在这种情况下,我们必须从剩余的n-1项中选择所有r项,这些项可以用(n-1)Ck方式完成。

我们添加了这两个,因为我已经说过这两个类是互斥的,因此共同形成了从n中选择r项的总方式。