Можете ли вы объяснить мне этот рекурсивный код" N choose k"?

вот код проблемы подмножества с аргументами n и k. n представляет общее количество студентов, а k представляет количество студентов, которых я хочу получить из n. Код пытается дать количество возможных комбинаций вытягивания k числа студентов из n числа студентов.

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)

Я понимаю первую часть рекурсивного вызова, но у меня возникли проблемы с пониманием части + subset(n-1, k). Кто-нибудь может мне это объяснить?

3 ответов


всякий раз, когда вы выбираете k элементы n есть два случая:

  1. вы выбираете элемент #n
  2. вы не выбираете элемент #n

поскольку эти события являются взаимоисключающими, общее количество комбинаций дается по количеству комбинаций при выборе #n, и те, когда вы не выбираете #n.

выбор элемента #n

поскольку мы уже выбрали один элемент, нам нужно только выбрать другой k-1 элементы. Кроме того, поскольку мы уже определились с одним элементом – включен он или нет – нам нужно рассмотреть только оставшийся n-1 элементы.

таким образом, количество комбинаций для выбора элемента #n дана by

    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 и k снижение в унисон или только n уменьшается на единицу, т. е. отсюда следует

это означает, что должен был быть вызов subset(n - 1, k) чтобы это случилось, что n снижается ниже k. Однако, это невозможно, так как у нас есть базовый случай на n = k где мы возвращаем постоянным 1.

Мы заключаем, что либо n уменьшается в какой-то момент такой, что n = k, или уменьшение в унисон точно k времена такие, что k = 0.

таким образом, в базовом варианте работает.


это скорее математическая проблема, а не вопрос программирования. То, что вы делаете, это вычисление биномиального коэффициента, формула

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

см. здесь для полного объяснения. Рекурсивное решение можно увидеть здесь. В случае, если указанная ссылка недействительна, просто найдите ее с помощью Google. Я сомневаюсь, что вы получите лучшее объяснение, чем после прочтения этой статьи в Википедии.


фрагмент кода:

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-1)C(k-1) + (n-1)Ck

если у вас есть трудности в понимании того, как выше уравнение выполняется, то читайте дальше: В nCr мы выбираем R элементов из n. Выбор может быть классифицирован на два взаимоисключающих класса, если мы рассмотрим какой-либо конкретный пункт:

  • элемент происходит среди R выбранного элемента

    в этом случае мы должны выбрать оставшиеся элементы r-1 из оставшихся элементов n-1. Это можно сделать в (n-1) C(k-1).

  • элемент не встречается среди R выбранного элемента

    в этом случае мы должны выбрать все R элементов из оставшихся N-1 элементов, которые могут быть сделаны в (n-1)CK способами.

мы складываем эти два, как я уже сказано, что эти два класса взаимоисключающие и, следовательно, вместе образуют общие способы выбора R элементов из n.