Ответы пользователя по тегу Рекурсия
  • Не понимаю, почему программа "тяжелая"?

    Alexandroppolus
    @Alexandroppolus
    кодир
    40000000 чисел Фибоначчи - это лютая вещь со всех сторон. Даже если ты врубишь мемоизацию, как посоветовал Максим Припадчев , то там дохрена вычислений, потому что числа будут длиной в миллионы цифр.

    Мы суммируем только четные числа Фибоначчи.
    Легко заметить, что F(n) четное, только если n делится на 3, т.е. n = 3m.
    То есть тебе нужна сумма F(3*m) для всех m от 0 до floor((40000000-2) / 3) включительно, если правильно понимаю этот ваш range.

    я тут пояндексил и промыслил формулы:
    1) F(3*n) = F(n+1)^3 + F(n)^3 + F(n-1)^3 (из википедии)
    2) sum[0...N] F(i)^3 = (1/2)*(F(n)*F(n+1)^2 + (-1)^(n-1)*F(n-1) + 1) отсюда

    сумму F(3*n) можно выразить через сумму F(n)^3 и потом применить формулу (2)

    в итоге получается

    sum[0...N] F(3*i) = (1/2)*(F(n)*F(n+1)^2 + (-1)^(n-1)*F(n-1) + 1) - 1 + F(n)^3 + F(n+1)^3

    для этого выражения нам нужны F(n), F(n+1) и F(n-1) = F(n+1) - F(n)

    F(n) и F(n+1) вычисляем методом "fast doubling", который делает O(ln(n)) действий вместо O(n) стандартного способа. Ускорение знатное.

    Итого:
    const fib = (n, a = []) => {
        if (n < 1) {
            a[0] = 0n;
            a[1] = 1n;
        } else {
            const m = Math.floor(n/2);
            fib(m, a);
            const x = a[0] * (2n * a[1] - a[0]);
            const y = a[0] * a[0] + a[1] * a[1];
            if (n % 2) {
                a[0] = y;
                a[1] = x + y;
            } else {
                a[0] = x;
                a[1] = y;
            }
            
        }
        return a;
    }
    
    const sumFib3n = (n) => {
        if (n < 1) { return 0n; }
    
        const [fn, fnp1] = fib(n); // fn = fib(n), fnp1 = fib(n+1), 
        const fnm1 = fnp1 - fn;  // fnm1 = fib(n-1), 
        const sgn = n % 2 ? 1n : -1n;
        const sumPow3 = (fn * fnp1 * fnp1 + sgn * fnm1 + 1n) / 2n;
        
        return sumPow3 - 1n + fn * fn * fn + fnp1 * fnp1 * fnp1;
    }
    
    console.log(sumFib3n(Math.floor((40000000 - 2)/3)));
    Ответ написан
    2 комментария
  • Как извлечь из вложенной структуры элементы удовлетворяющие условию?

    Alexandroppolus
    @Alexandroppolus
    кодир
    function getIntermediateItems(tree, result = []) {
      tree?.forEach((item) => {
        if (item.workControl?.includes('intermediate')) {
          result.push(item);
        }
        getIntermediateItems(item.content, result);
      });
      return result;
    }
    
    const arr = getIntermediateItems(myarr);
    document.getElementById("dd").prepend(JSON.stringify(arr, '', 4))
    Ответ написан
    Комментировать
  • Как внутри рекурсии создать результирующий массив из значений вложенных объектов?

    Alexandroppolus
    @Alexandroppolus
    кодир
    как вариант:

    function recur(obj, result = []) {
      for (let key in obj) {
        if (typeof obj[key] !== 'object' || obj[key] === null) {
          result.push(obj[key])
        } else recur(obj[key], result)
      }
      return result
    }
    Ответ написан
  • Как логичнее закончить?

    Alexandroppolus
    @Alexandroppolus
    кодир
    Сейчас код выводит только половину

    ну так дорисуй вторую половину
    ...
    return str(n) * m + '\n' + print_pattern(n - 1, m - n) + '\n' + print_pattern(n - 1, m - n) + '\n' + str(n) * m
    Ответ написан
    2 комментария
  • Как убрать переполнение программного стека?

    Alexandroppolus
    @Alexandroppolus
    кодир
    Не помню паскалевского синтаксиса, но как-то так:
    function f(n: integer): integer;
    var a, b, c: integer;
    begin
      if n < 2
      then f:= -2
      else if n = 2
      then f:= 2
      else
      begin
         a := -2;
         b := 2;
         for i = 3...n begin
            c := b;
            b := 3*b - a;
            a := c;
         end;
         f:= b;
       end;
    end;


    Как видим, рекурсия тут без надобности
    Ответ написан
  • Как написать рекурсивную. функцию для полинома Чебышева?

    Alexandroppolus
    @Alexandroppolus
    кодир
    else return 2*(2*x - 1) * f(n-1, x) - f(n - 2, x);

    кстати, слово else тут лишнее.
    Ответ написан
  • Как правильно рендерить рекурсивный массив используя useMemo?

    Alexandroppolus
    @Alexandroppolus
    кодир
    reply.replies = [newReply,...reply.replies]
    setReplies(rootReplies=>[...rootReplies])

    ты неправильно обрабатываешь стейт. По факту здесь меняется содержимое некоего узла, а потом возвращается поверхностная копия всего дерева.

    есть 3 варианта:
    1) Продолжать использовать дерево в useState. Но менять его надо иммутабельно. То есть если у какого-то узла что-то поменялось, то надо заменить узел и все его паренты, а не только корневой массив.
    2) Нормализовать данные в плоскую структуру. Каждому реплаю присваивается уникальный id, все реплаи складываются в объект, где ключем будет id. В массиве replies у каждого объекта будут лежать не сами объекты, а только id, и потом надо будет их соединять во время рендера. Этот вариант канонично используется для redux, но как зайдет для useState, навскидку не совсем понятно.
    3) Использовать MobX с этими его observable.deep (или просто observable). Как всегда, самый простой и удобный вариант.
    Ответ написан
    Комментировать
  • Найти наибольшее количество слагаемых из переменных двух массивов?

    Alexandroppolus
    @Alexandroppolus
    кодир
    function maxCount(s, a1, a2, i1 = 0, i2 = 0) {
        const c1 = i1 < a1.length && a1[i1] <= s ? 1 + maxCount(s - a1[i1], a1, a2, i1 + 1, i2) : 0;
        const c2 = i2 < a2.length && a2[i2] <= s ? 1 + maxCount(s - a2[i2], a1, a2, i1, i2 + 1) : 0;
    
        return Math.max(c1, c2);
    }
    
    console.log(maxCount(10, [5, 1, 1, 1, 1], [1, 3, 3, 3, 3]));


    на каждом шаге выбираем максимум из с1 (случай, когда можно взять очередной элемент из массива a1) и с2 (из массива а2)

    можно добавить мемоизацию для ускорения (мемоизовать по паре i1, i2)
    Ответ написан
    1 комментарий
  • Как посчитать сумму элементов вложенных массивов без использования циклов?

    Alexandroppolus
    @Alexandroppolus
    кодир
    function summArr(a, len) {
        return len < 1 ? 0 : summArr(a, len - 1) + summ(a[len - 1]);
    }
    
    function summ(a) {
        return Array.isArray(a) ? summArr(a, a.length) : a;
    }
    Ответ написан
    Комментировать
  • Как дерево представить в видемассива?

    Alexandroppolus
    @Alexandroppolus
    кодир
    Что у тебя там за дерево?
    Например, если это сбалансированное двоичное дерево, то можно использовать подход как здесь
    Если что-то другое, то надо смотреть, что именно.
    Ответ написан
    Комментировать
  • Можно ли оптимизировать рекурсию?

    Alexandroppolus
    @Alexandroppolus
    кодир
    Это тривиально делается за линейное время и без рекурсии.
    Сначала составляем словарь dict (id => узел дерева), в твоем случае таким словарем может быть исходный массив, если id идут по порядку от нуля, и $data[id] всегда содержит элемент с таким id, а элементы массива станут узлами дерева.
    Потом обходим массив снова и в чилдрены dict[$item['parent_id']] добавляем $item.
    Ответ написан
    2 комментария
  • Как с помощью рекурсии найти минимальный элемент массива?

    Alexandroppolus
    @Alexandroppolus
    кодир
    int Min(int arr[], int n, int current) {
      return n > 0 ? Min(arr, n - 1, min(current, arr[n-1])) : current;
    }
    
    
    int minValue = Min(arr, n, +бесконечность)


    ещё вариант
    int Min(int arr[], int n) {
      return n > 0 ? min(Min(arr, n - 1), arr[n-1])) : +бесконечность;
    }
    
    
    int minValue = Min(arr, n);


    Однако первый способ хорош тем, что там хвостовая рекурсия, и компилятор теоретически может развернуть её в цикл.
    Ответ написан
  • Рекурсия, зачем она нужна, и используете ли вы её?

    Alexandroppolus
    @Alexandroppolus
    кодир
    Если внутри функции делается несколько рекурсивных вызовов, то вариант цикл+стек будет смотреться сложно и вышеупомянутые джуны ощутят трудности его понимания. Или, например, кейс, когда есть взаимная рекурсия двух или более функций - там придется код как-то перекроить, смотря по ситуации.

    Нерекурсивный вариант обязательно следует использовать, когда можно обойтись без стека, как в избитом примере вычисления факториала. Так же - когда существует ненулевая вероятность, что будет переполнение стека: например, функция работает с произвольным двоичным деревом, и может получить на вход очень сильно несбалансированное дерево большой высоты. Ещё есть разные специальные кейсы. Допустим, обход графа в глубину: если сделать его нерекурсивным, то можно будет легко заменить в функции стек на очередь и получить обход в ширину, или даже на приоритетную очередь, тогда будет "поиск по критерию стоимости"
    Ответ написан
    Комментировать
  • Вопрос python программистам?

    Alexandroppolus
    @Alexandroppolus
    кодир
    Кейс, где рекурсивный вызов - самое последнее действие функции, это "хвостовая рекурсия". В некоторых языках и средах выполнения имеет место "оптимизация хвостовой рекурсии", то есть компилятор переписывает это дело так, что текущий вызов завершается перед следующим, и таким образом не будет переполнения стека, какой бы глубокой рекурсия ни была.
    В языках программирования, куда не завезли цикл, это имеет смысл. В нормальных ЯП такая фича бесполезна, потому что легко переделать на цикл вручную. Есть ли оно в Питоне, непонятно, беглый поиск ответа не дал.
    Ответ написан
    Комментировать
  • Как правильно написать скрипт через рекурсию?

    Alexandroppolus
    @Alexandroppolus
    кодир
    сделать юзерскрипт.
    для Хрома использовать плагин Tampermonkey, для других браузеров - аналогичное.
    Ответ написан
    2 комментария