• Пояснения по алгоритму нахождения суммы четырех квадратов?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    1) l не вычисляется. l перебирает некоторые простые числа до log n (2 и те, которые дают остаток 1 при делении на 4).

    2) Процесс описан внизу страницы. Вы представляете n в виде произведения нечетного (и не делящегося на описанные выше простые числа) n' и вот этих всех простых чисел в каких-то степенях. Далее, поскольку мы можем эти простые числа разложить в сумму двух квадратов после предподсчета, то используя описанный в статье ранее трюк можно получить разложение на квадраты n из разложения n'

    3) Это трюк, чтобы все эти простые числа найти до логарифма найти.

    4) Кажется не обязательно и натуральный логарифм тут используется, чтобы оценка сложности была оптимальная. Но я не уверен. Лучше не надо.
    Ответ написан
    Комментировать
  • Кратчайший путь через несколько точек?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    В общем случае такая задача красиво и легко не решается. Если бы можно было самопересекаться, то несколько последовательных A* легко справились бы. А так остаются переборы всякие, да целочисленное линейное программирование и подобные NP алгоритмы.

    Так, вашу задачу можно переформулировать в виде максимального потока минимальной стоимости из нескольких разных жидкостей (исток k-ой жидкости в точке k, сток - в k+1. Чтобы вершины нельзя было переиспользовать их надо раздвоить на входную и выходную, соедененные ребром).

    Похоже, что в вашей задаче может хорошо работать метод отжига, или генетический алгоритм.

    Я сомневаюсь, что даже для графа-сетки есть какой-то более простой алгоритм.
    Ответ написан
    2 комментария
  • Как исправить функцию?

    VlasenkoFedor
    @VlasenkoFedor
    Программист: php, js, go
    const nextBigger = arr => {
        const num = arr.join('');
        const max = arr.sort((a, b) => b - a).join('');
        const tpl = ''.padStart(arr.length, '-')
        const check = str => {
            arr.forEach(v => str = str.replace(v, '-'))
            return str === tpl
        }
        if (max === num) return -1;
        let n = +num;
        while (true) {
            n += 9
            if (check(n.toString())) return n
        }
    }

    это решение основано на приведенной задаче в качестве примера
    здесь результатом является строка
    результатом [21, 1, 2, 12, 11] ->21122111 может быть несколько
    [21, 12, 2, 1, 11], [21, 12, 2, 11, 1] ...
    нужно преобразовать строку результата в массив, сами напишите
    Ответ написан
    3 комментария
  • Как исправить функцию?

    Rsa97
    @Rsa97
    Для правильного вопроса надо знать половину ответа
    Например, так:
    function bank(summ, nominals, pos = 0, result = null) {
      if (result === null) {
        result = Array(nominals.length).fill(0);
      }
      if (summ === 0) {
        return result;
      }
      if (pos > nominals.length - 1) {
        return null;
      }
      if (summ % nominals[pos] === 0) {
        result[pos] = summ / nominals[pos];
        return result;
      }
      for (result[pos] = Math.trunc(summ / nominals[pos]); result[pos] >= 0; result[pos] -= 1) {
        if (bank(summ - nominals[pos] * result[pos], nominals, pos + 1, result) !== null) {
          return result;
        }
      }
      return null;
    }
    
    bank(2650, [1100, 650, 230, 70, 20]); // [ 2, 0, 1, 2, 4 ]
    Ответ написан
    Комментировать
  • Двумерный массив одним циклом?

    axifive
    @axifive
    Software Engineer
    Если без заполнения, то можно.
    let arr = new Array(5);
    for(let i of arr) {
       i = new Array(3);
    }

    Также можно встроенным методом fill:
    let arr = new Array(5).fill(new Array(3));
    Ответ написан
    9 комментариев
  • Можно ли разделить строку с сохранением разделителей без регулярного выражения?

    Stalker_RED
    @Stalker_RED
    Можно и без регулярного.

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

    Но с регуляркой проще.
    Для начала удалим пробельные символы при помощи replace.

    \D - любые символы кроме цифр
    | - логическое ИЛИ
    \d - любые числа, то-же самое что [0-9]
    [\d.] - список символов включающий любые числа и точку. Также можно записать как [0-9.]
    + - квантификатор, который указывает на неограниченное кол-во повторений (но не меньше одного символа).

    Все вместе: \D|[\d.]+ любые не цифровые символы ИЛИ последовательность цифр и точек любой длины.
    Ответ написан
    7 комментариев
  • Как найти количество перестановок числа?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Это комбинаторика.

    Если бы не было ограничения на то, что первый символ не 0, то было бы гораздо проще. Частый прием в комбинаторике - подсчитать количество всех возможных вариантов, а потом вычесть количество "плохих" вариантов. Временно разрешим нулю быть первой цифрой, решим задачу. Потом зафиксируем ноль на первой позиции и подсчитаем сколько таких вариантов и вычтем их. Для этого можно вычеркнуть один ноль из числа, решить ту же самую задачу.

    Еще, в вашей формуле направшивается добавить пустое число ("") в ответ. Давайте разрешим его и вычтем в конце 1.

    Еще, очевидно, результат зависит только от количества каждой цифры в исходном числе, но не от их порядка.

    Поэтому пусть f(a0,a1,...a9) - сколько есть способов расставить некторые из a0 нулей, a1 единиц, a2 двоек, и т.д. (ноль может идти в начале, число может быть пустым).

    Ответ к задаче f(a0,a1,...a9)-1-min(a0,1)*f(a0-1,a1,...a9). Последнее слагаемое считает варианты, начинающиеся с нуля. Оно не вычитается, если нулей в числе нет. -1 посередине вычитает пустое число из ответа (но ее нет в последнем слагаемом, ведь мы там еще 0 в начале приписываем и пустое число надо считать).

    Теперь самое интересное, формула для f(a0,a1,...a9). Замкнутой формулы я не нашел, но придумал, как считать алгоритмом.

    Можно все варинаты разделить по количеству символов в числе (n), от 0 до суммы a0...a9. Давайте подумаем, где могуть быть девятки? i <= a9 из них попадут в ответ. Они могут быть на C(n, i) позициях. Останется разместить остальные цифры на n-i позиций.

    Вырисовывается следующее динамическое программирование:

    F(i, n) - сколько способов разместить первые i цифр на n позициях (возможно, беря не все цифры).

    F(i,n) = sum (j=0..min(a[i-1], n)) F(i-1, n-j)*C(n, j)
    F(0, n>0) = 0
    F(i,0) = 1.
    Ответ - sum(k=0..a0+...+a9) F(9, k)

    У функции как бы неявный параметр массив a[] количеств всех цифр, но я его не включаю в параметры динамики, потому что по нему не надо мемоизировать.

    Не забудьте, что для подсчета второй динамики, для f(a0-1,...a9) надо будет полностью отчистить мемеоизацию, потому что массив a поменялся же.

    Этот алгоритм работает за O(9*n), где n - длина входного числа.

    Вот пример для входного числа 112: все цифры, которых в числе нет можно выкинуть из рассмотрения и работать с массивом a={2,1} (для всех десяти цифр слишком много писать).

    F(0,0) = 1
    F(0,1) = 0
    F(0,2) = 0
    F(0,3) = 0
    F(1, 0) = 1
    F(1,1) = F(0, 1)*C(1,0) + F(0,0)*C(1,1) = 1
    F(1,2) = F(0,2)*C(2, 0)+ F(0,1)*C(2,1) + F(0,0)*C(2,2) = 1
    F(1,3) = F(0,3)*C(3, 0)+ F(0,2)*C(3,1) + F(0,1)*C(3,2) = 0
    F(2,0) = 1
    F(2,1) = F(1,1)*C(1, 0) + F(1,0)*C(1,1) = 2
    F(2,2) = F(1,2)*C(2, 0) + F(1,1)*C(2,1) = 3
    F(2,3) = F(1,3)*C(3, 0) + F(1,2)*C(3,1) = 3

    Ответ F(2,3)+F(2,2)+F(2,1)+F(2,0) = 3+3+2+1= 9.

    Вычитаем 1 (пустое число) и получаем 8 чисел, как у вас в примере для 112.
    Ответ написан
    2 комментария
  • Можно ли здесь избавиться от цикла while?

    WblCHA
    @WblCHA
    Как я не пытался, я так и не понял в чём заключается задача, но, переписав код, я таки выкинул ряд бесполезных проверок и столь же бесполезную сортировку, а также, технически, избавился от вайла.)
    Вариант 1
    (() => {
      const solution = (arr, iteration = 1) => {
        if(arr.every(num => num === arr[0])) {
          return arr[0] * arr.length;
        }
        
        const minNum = Math.min(...arr)
    
        arr.forEach((num, i) => {
          if(num > minNum) {
          	arr[i] = num % minNum || minNum;
          }
        });
    
        console.log(`Iteration ${iteration}`, arr);
        
        return solution(arr, iteration + 1);
    	}
      
      console.log(solution([6,9,21]));
      console.log(solution([56, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 142]));
    })()

    Вариант 2
    (() => {
      const solution = (arr, iteration = 1) => {
        const minNum = Math.min(...arr);
        let hasDif = false;
    
        arr.forEach((num, i) => {
          if(num > minNum) {
            hasDif = true;
          	arr[i] = num % minNum || minNum;
          }
        });
        
        if(hasDif) {
       		console.log(`Iteration ${iteration}`, arr);
    
        	return solution(arr, iteration + 1);
        }
        
        return arr[0] * arr.length;
    	}
      
      console.log(solution([6,9,21]));
      console.log(solution([56, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 142]));
    })()

    Вариант 3
    (() => {
      const solution = (arr) => {
        let iteration = 1;
        const baseSet = new Set(arr);
        
        if(baseSet.size < 2) {
          return arr[0] * arr.length;
        }
        
        const solve = (set) => {
          const minNum = Math.min(...set);
          const nextSet = new Set();
    
          set.forEach((num, i) => {
            if(num > minNum) {
              nextSet.add(num % minNum || minNum);
            }
          });
          
          if(nextSet.size < 2) {
            return minNum * arr.length;
          }
          
          console.log(`Iteration ${iteration}`, arr);
          
          return solve(nextSet);
        }
        
        return solve(baseSet);
    	}
      
      console.log(solution([6,9,21]));
      console.log(solution([56, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 142]));
    })()

    П.с.: чего только не сделаешь, чтобы не лечь спать...
    Ответ написан
    3 комментария
  • Следующее наименьшее число. Как упростить нахождение второго индекса?

    @dimoff66
    Кратко о себе: Я есть
    Ну естественно. По логике нахождения первого числа можно понять, что все числа после него идут в порядке возрастания, поэтому из реверса находите индекс первого числа которое меньше forv[idx1] и все. Естественно с экранированием на нормальный порядок.

    По той же причине после перестановки сортировка чисел в обратном порядке не требуется, достаточно реверса.

    К тому же forv тут вообще не нужен, достаточно rev

    function nextSmaller(n){
      const rev = [...`${n}`].map(Number).reverse()
      const idx1 = rev.findIndex((v, i, a) => i && a[i-1] < v)
    
      if (idx1 < 0) return idx1
    
      const idx2 = rev.slice(0, idx1).findIndex(v => v < rev[idx1])
      if (idx2 < 0) return idx2;
    
      [rev[idx1], rev[idx2]] = [rev[idx2], rev[idx1]]
       
      const nn = +rev.slice(idx1).reverse().concat(rev.slice(0, idx1)).join('')
      return nn
    }
      
    console.log(nextSmaller(133232766512347)) //133232766475321
    Ответ написан
    2 комментария
  • Как понять этот скрипт?

    @tukreb
    Вы погулилте для начала.

    Рекурсия начнёт возвращать результат тогда, когда дойдёт до самого конца и результаты начнёт возвращаться с самого конца. Отсюда и получается, что отправили 5, но выводить началось с 1, потому что return, до этого не работал, он вызывал функцию myFun, а возвращаться результат начал, когда сработал этот if if (n === 1) return 1;, в котором уже не вызывается myFun и соответственно заканчивается рекурсия и return начинает возвращать ответ.
    Ответ написан
    1 комментарий
  • Следующее наименьшее число. Как упростить нахождение второго индекса?

    @Giperoglif
    подозреваю что тут поможет бинарный поиск
    Ответ написан
    Комментировать
  • Как последовательные числа сложить в отдельные массивы?

    0xD34F
    @0xD34F Куратор тега JavaScript
    arr.reduce((acc, n, i, a) => (
      n - 1 !== a[i - 1] && acc.push([]),
      acc[acc.length - 1].push(n),
      acc
    ), [])
    Ответ написан
    Комментировать
  • Рекурсивное умножение разрядов целого числа, как узнать количество вызовов функции?

    @Karpion
    Если человек не умеет работать с циклами, то браться за рекурсию ему не следует.
    В данном случае лучше всего - цикл while(x>9) { ... }. Внутри цикла надо использовать операции "mod 10" (остаток отделения на десять - для получения последней цифры) и "div 10" (остальная часть числа) - и так, извлекая цифры по очереди, перемножать их.
    Ответ написан
    Комментировать
  • Рекурсивное умножение разрядов целого числа, как узнать количество вызовов функции?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Проверяем число, если однозначное - возвращаем 0; в противном случае возвращаем сумму единицы и результата рекурсивного вызова:

    const multiply = num =>
      num > 9
        ? 1 + multiply([...`${num}`].reduce((acc, n) => acc * n))
        : 0;
    Ответ написан
    Комментировать