Рекурсия проверяет, дает ли серия операций заданное число

В книге Eloquent JS в разделе рекурсии дана программа:

Consider this puzzle: by starting from the number 1 and repeatedly either adding 5 or multiplying by 3, an infinite amount of new numbers can be produced. How would you write a function that, given a number, tries to find a sequence of such additions and multiplications that produce that number? For example, the number 13 could be reached by first multiplying by 3 and then adding 5 twice, whereas the number 15 cannot be reached at all.

У меня есть следующая программа, которая, похоже, проверяет ее, но я не знаю, как заставить ее печатать последовательность.

function tester (value, key) {
    if (value == key) {
        return 1;
    }

    else if (value > key) {
        return 0;
    }

    else {
        if ( tester(value+5, key) || tester(value*3, key) ) {
            return 1;
        }
        return 0;
    }
}

Что вы вкладываете в эту функцию? Кажется, должен быть только один ...

Scott Sauyet 27.05.2018 15:13

@ScottSauyet Всегда вводится значение = 1. Ключ - это номер, который нужно проверить. В Js нет параметров по умолчанию

jeea 27.05.2018 15:23

В JS есть параметры по умолчанию, и они были в течение некоторого времени. Если это то, что вы хотите, вы можете написать function tester(key, value=1) { /* ... */ }.

Scott Sauyet 27.05.2018 15:25
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
3
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете сохранить последовательность, и, если она будет найдена, вычисление вернет последовательность.

function tester(key, value = 1, sequence = value) {
    if (value > key) {
        return false;
    }

    if (value === key) {
        return sequence;
    }

    return tester(key, value + 5, '(' + sequence + ' + 5)')
        || tester(key, value * 3, sequence + ' * 3');
}

console.info(tester(15));
console.info(tester(11));
console.info(tester(24));
console.info(tester(37));

Мне нравится ваш выходной формат ... почти. Почему вы решили заключить в скобки только добавление? Это кажется немного странным, хотя и законным. Очевидно, вы не можете делать только умножение по причинам распределения. Но оба кажутся яснее. И я пытаюсь решить, нравится ли мне это больше, чем моя запись шагов в виде массива.

Scott Sauyet 27.05.2018 15:52

@ScottSauyet, просто чтобы сохранить приоритет оператора.

Nina Scholz 27.05.2018 15:53

Верно. Это то, что я хотел описать, когда сказал «по причинам распространения». Но, например, я считаю, что вывод tester(37) труднее читать, чем (((((1 * 3) * 3) * 3) + 5) + 5), но, возможно, это только я.

Scott Sauyet 27.05.2018 15:59
Ответ принят как подходящий

Ваша версия для меня немного странная, она возвращает 1 или 0, а не true или false. Вы в основном привыкли к языку, который объединяет логические значения с такими целыми числами? Но, похоже, должно работать.

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

const m3a5 = (n) => n < 1 
  ? false 
  : n == 1
    ? true
    : m3a5(n - 5) || (n % 3 === 0 && m3a5(n / 3))

console.info(m3a5(13)) //=> true
console.info(m3a5(15)) //=> false
console.info(m3a5(18)) //=> true

Это должно быть полностью эквивалентно вашему, по модулю различий boolean / int.

С его помощью вы можете довольно просто расширить его, чтобы вы могли фиксировать шаги:

const m3a5 = (n, steps = []) => n < 1 
  ? false 
  : n == 1 
    ? steps
    : m3a5(n - 5, ['+5'].concat(steps)) 
      || (n % 3 === 0 && m3a5(n / 3, ['*3'].concat(steps)))

console.info(m3a5(13)) //=> ['*3', '+5', '+5']
console.info(m3a5(15)) //=> false
console.info(m3a5(18)) //=> ['*3', '+5, '+5', '+5']

Обратите внимание, что это покажет один возможный путь, а не все из них. Например, ['+5', '*3'] - еще один возможный результат для m3a5(18), который вы получите, переключив основную ветвь на

: (n % 3 === 0 && m3a5(n / 3, ['*3'].concat(steps))) 
  || m3a5(n - 5, ['+5'].concat(steps))

Но если вы хотите, чтобы пути все, это был бы существенно другой код.

Вау, отличный ответ! Мне очень нравятся тернарные операторы, и ваша реализация выглядит действительно круто: 3

Limbo 27.05.2018 15:46

Условные (тернарные) операторы для меня в основном способ использовать выражения, а не операторы, насколько это возможно в моем коде. Я почти полностью перестал использовать for, и все ближе к if.

Scott Sauyet 27.05.2018 16:01

Другие вопросы по теме