2014-01-12 3 views
4

это код Java, который рекурсивно подсчитывает количество платежей в определенных монетах (например, 1, 2, 5, 20, 50 и т. Д.). Я попытался выяснить, как это работает, но, похоже, не может его получить. Может ли кто-нибудь быть таким добрым и объяснить математику и логику кода и как эта рекурсия работает? Я был бы очень признателен.Рекурсивный метод для подсчета количества комбинаций

// Returns the count of ways we can sum S[0...m-1] coins to get sum n 
int count(int S[], int m, int n){ 

    // If n is 0 then there is 1 solution (do not include any coin) 
    if (n == 0) 
     return 1; 

    // If n is less than 0 then no solution exists 
    if (n < 0) 
     return 0; 

    // If there are no coins and n is greater than 0, then no solution exist 
    if (m <=0 && n >= 1) 
     return 0; 

    // count is sum of solutions (i) including S[m-1] (ii) excluding S[m-1] 
    return count(S, m - 1, n) + count(S, m, n-S[m-1]); 
} 
+0

Как это называется? Что такое S? – Behe

+0

@Behe Похоже, что массив монет суммирует –

+0

@ Java Devil точно, он выглядит как массив целых чисел – user3185735

ответ

6

Метод работает следующим образом:

  1. Первые заявления являются условием остановки для текущего рекурсии (без них для всех случаев, то вы в конечном итоге с бесконечным циклом, который в конечном счете закончится в StackOverflow)

  2. Последняя строка - это расчеты. Каждый из утверждений уменьшает проблему на более мелкие куски с помощью:

    • count(S, m - 1, n) уменьшает количество монет (m-1), которая исключает последнюю монету в следующем рекурсивном вызове к
    • count(S, m, n-S[m-1]) использует последнюю монету в массиве и уменьшает сумму, которая необходимо достичь на величину этой монеты

Рассмотрим небольшой пример:

S[] = {1,2) // We have a 1 and 2 cent coin 
m = S.length // Consider all possibilities (= 2) 
n = 3  // How many ways can we make 3c 
       // Obviously with: 1x1c + 1x2c 
       //   and: 3x1c 

Рекурсия как дерево; Левая ветвь = count(S, m - 1, n), правая ветвь = count(S, m, n-S[m-1]):

        m=2;n=3 
           /  \ 
          m=1;n=3   m=2;n=1 
         /  \  / \ 
        m=0;n=3 m=1;n=2 m=1;n=1 m=2;n=-1 
          / \ / \ 
        m=0;n=2 m=1;n=1 m=0;n=1 m=1;n=0 
          / \ 
         m=0;n=1 m=1;n=0 

Это рекурсии можно рассматривать как Pre-order Traversal этого дерева.

Если вы затем рассмотрите условия метода, в котором найдено решение, или нет. Поэтому на листовых узлах, где n = 0.

Каждый который приходит примерно так:

Первое решение

  1. т = 1; п = 3 - Исключить последнюю монету (2c)
  2. т = 1; п = 2 - Использование эту монету (1c) и уменьшить на 1
  3. m = 1; n = 1 - Используйте эту монету (1c) и уменьшите на 1
  4. m = 1; n = 0 - Используйте эту монету (1c) и уменьшите на 1
  5. n = 0 - as olution (3x1c)

Второе решение

  1. т = 2; п = 1 - Используйте эту монету (2c) и уменьшить на 2
  2. т = 1; п = 1 - Исключить последний монета (2c)
  3. т = 1; п = 0 - Используйте эту монету (1c) и уменьшить на 1
  4. п = 0 - раствор (1x2c + 1x2c)

В каждом узле значение является return - 0 (без решения) или 1 (решение) - для добавления общего количества найденных решений. Как только рекурсия заканчивается, возвращается это окончательное значение и количество решений.


Некоторые дополнительные примечания:

  • Этот фрагмент кода будет рассматривать только первые m монеты в массиве S так, чтобы рассмотреть все возможные пути первоначальный вызов метода должен иметь m == S.length
  • Предполагается, что каждая монета может быть использована несколько раз

Код модификации с заявлениями для печати, чтобы увидеть рекурсии:

public static void main(String[] args){ 
    int[] coins = new int[]{1,2}; 
    System.out.println("Final Count = " + count(coins, coins.length, 3, "")); 
} 

public static int calls = 0; 

public static int count(int S[], int m, int n , String from){ 
    calls++; 
    System.out.print("Call#" + calls + ": " + from + "; m = " + m + "; n = " + n); 

    // If n is 0 then there is 1 solution (do not include any coin) 
    if (n == 0) 
    { 
     System.out.println(" - Solution Found"); 
     return 1; 
    } 

    // If n is less than 0 then no solution exists 
    if (n < 0) 
    { 
     System.out.println(" - No Solution Found n < 0"); 
     return 0; 
    } 

    // If there are no coins and n is greater than 0, then no solution exist 
    if (m <=0 && n >= 1) 
    { 
     System.out.println(" - No Solution Found (other Case)"); 
     return 0; 
    } 

    System.out.println(); 
    // count is sum of solutions (i) including S[m-1] (ii) excluding S[m-1] 
    return count(S, m - 1, n , from + "E") + count(S, m, n-S[m-1], from + "I"); 
} 
+0

Вау, спасибо вам за очень подробное объяснение. Это действительно то, что мне нужно. Извините за поздний ответ, мне потребовалось некоторое время, чтобы получить его, но теперь это кристально ясно :) – user3185735

+0

извините за ошибку снова ... но у меня есть еще один вопрос. Я хотел напечатать только допустимые комбинации на консоли (для вышеприведенного примера это будет похоже на «1 + 1 + 1» и «1 + 2», если массив будет содержать только «{1, 2}» и «n = 3'), но когда я добавляю в первый оператор 'if (n == 0)' код 'System.out.println (" (n) "+ n +" + "+ m +" (m) ");' eclipse отмечает следующий оператор if (это 'if (n <0)') как ошибка компиляции 'unreachable code' ... не могли бы вы рассказать, почему это не работает? ... и также пробовал другие способы, но публикация их слишком долго заставила бы sumbit :(спасибо – user3185735

+0

@ user3185735 Я могу только предположить, что после вашего 'if (...)' вам не хватает открывающих '{' и закрывающих '}' брекетов. находится в формате 'if (condition) {statements}'. Если вы этого не сделали, тогда только первый оператор после 'if' является условным, и последующие строки будут всегда выполняться ... являясь оператором return, поэтому остальная часть кода не будет доступна. Если это не так, напишите новый вопрос и оставьте мне ссылку на него. –

3

Из кода, я предполагаю, что S массив по крайней мере m элементов, причем каждый элемент, представляющих собой доступную номинал монеты, и n является предполагаемой суммой.

В комментариях действительно сказано все, кроме последнего комментария назад. count(S, m - 1, n) - количество решений, исключающих последнюю монету в текущем диапазоне. count(S, m, n-S[m-1]) - количество решений, использующих эту монету.

Исключительный случай просто отбрасывает последнюю монету в текущем диапазоне, уменьшая m на единицу.

Этот случай использует его, уменьшая n на величину этой монеты. Поскольку случай включения также не уменьшает m, предположительно любое номинал монеты может использоваться несколько раз. Не имеет значения, слишком ли велика монета, о которой позаботились, вернув 0, если n < 0.

Если что-либо о базовых случаях не ясно из комментариев, задайте конкретный вопрос.

Смежные вопросы