Восстановление входа в энкодер из выхода - PullRequest
0 голосов
/ 26 декабря 2018

Я хотел бы понять, как решить проблему Codility ArrayRecovery , но я даже не знаю, к какой отрасли знаний обращаться.Это комбинаторика, оптимизация, информатика, теория множеств или что-то еще?

Редактировать: Область знаний, к которой следует обращаться - программирование с ограничениями , в частности ограничение распространения .Вам также понадобится немного комбинаторики , чтобы знать, что если вы берете k чисел за раз из диапазона [1 .. n ], с ограничением на отсутствие числаможет быть больше, чем предыдущая, это получается (n + k-1)! / k! (n-1)! возможных комбинаций, равных количеству комбинации с заменами из n взятых вещей k за один раз, что имеет математическое обозначение nCRk.Вы можете прочитать о том, почему это так работает здесь .

Питер Норвиг (Peter Norvig) представляет отличный пример того, как решить эту проблему с помощью своего Решателя Судоку .


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

input 1, 2, 3, 4 => output 0, 1, 2, 3
input 2, 4, 3 => output 0, 2, 2

Задача состоит в том, чтобы с учетом выходных данных (и диапазона допустимых входных данных) выяснить, сколько возможных входных данных могло его сгенерировать.Но прежде чем я смогу сделать это, я не уверен в том, как подойти к формулировке уравнения.Вот с чем я прошу помощи.(Конечно, было бы также полезно полное решение, если бы оно было объяснено.)

Я просто смотрю на некоторые возможные результаты и удивляюсь.Вот некоторые примеры выходов кодировщика и входов, которые я могу придумать, с * означает любой действительный вход и что-то вроде > 4 означает любой действительный вход больше 4. Если необходимо, входы называются A1, A2, A3, ... (1индексирование на основе)

Edit # 2

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

output #1: 0, 0, 0, 4 
possible inputs: [>= 4, A1 >= * >= 4, 4, > 4]                     

output #2: 0, 0, 0, 2, 3, 4          # A5 ↴  See more in discussion below
possible inputs: [>= 2, A1 >= * >=2, 2, 3, 4, > 4]

output #3: 0, 0, 0, 4, 3, 1
possible inputs: none # [4, 3, 1, 1 >= * > 4, 4, > 1] but there is no number 1 >= * > 4 

Вторая входная последовательность очень сильно ограничена по сравнению с первой, просто добавив еще 2 выхода.Третья последовательность настолько ограничена, что становится невозможной.

Но набор ограничений для A5 ​​в примере # 2 немного сложнее сформулировать.Конечно, A5> O5, это основное ограничение для всех входов.Но любой выход> A4 и после O5 должен появиться на входе после A4, поэтому A5 должен быть элементом набора чисел, который идет после A5, который также> A4.Поскольку существует только 1 такое число (A6 == 4), оно должно быть A5, но оно усложняется, если за ним следует более длинная строка чисел. (Примечание редактора: на самом деле это не так.)

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

Вот ограничения, которые я пока вижу для любого данного A n

  • A n > O n
  • A n <= min (Набор других возможных чисел от O <sub>1 до n-1 > O n ).Как определить множество возможных чисел больше, чем O n ?
    1. Числа, превышающие O n , которые появились после самого последнего появления O n на входе
  • A n> = max (набор других возможных чисел от O 1 до n-1 n ).Как определить множество возможных чисел меньше, чем O n ?
    1. На самом деле этот набор пуст, потому что O n по определению является наибольшим возможным числом из предыдущей входной последовательности.(Нельзя сказать, что это строго наибольшее число из предыдущей входной последовательности.)
    2. Любое число, меньшее O n , которое пришло до того, как оно в последний раз появилось на входе, будетнеподходящий из-за "ближайшего" правила.Никаких чисел, меньших, чем O n , не могло произойти после самого последнего вхождения из-за "ближайшего" правила и из-за переходного свойства: если A i n и A j i , затем A j n
  • Тогда есть теория множеств:
    1. A n должен быть элементом множества неучтенных элементов множества O n + 1 в O m , где m наименьшее m> n, такое что O m n .Любой выходной сигнал после такого O m и больше, чем O m (которым является A n ), должен отображаться как или после A m .
    2. Элемент не учитывается, если он виден на выходе, но не отображается на входе в положении, совместимом с остальными выходными данными.Очевидно, мне нужно лучшее определение, чем это, чтобы код и алгоритм для его расчета.

Кажется, что, возможно, какая-то теория множеств и / или комбинаторика, или, возможно, линейная алгебра, поможет выяснить число возможных последовательностей, которые могли бы объяснить все неучтенные-Для выходов и подходят другие ограничения. (Примечание редактора: на самом деле, все никогда не бывает так сложно.)

Ответы [ 2 ]

0 голосов
/ 29 декабря 2018

Код ниже проходит все тесты Codility.OP добавил функцию main, чтобы использовать ее в командной строке.

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

Единственное усложнение этого правила состоит в том, что иногда максимумом является «значение предыдущего входа», а сам этот вход имеет диапазон.Но даже в этом случае все подобные значения являются последовательными и имеют одинаковый диапазон, поэтому число возможностей можно рассчитать с помощью базовой комбинаторики, и эти входы как группа не зависят от других входов (которые служат только для установки диапазона)Таким образом, возможности этой группы могут быть объединены с возможностями других входных позиций простым умножением.

Обзор алгоритма

Алгоритм выполняет один проход через выходной массив, обновляя возможные числа входных массивов после каждого span , что я и называю повторениями чиселна выходе.(Можно сказать, максимальные подпоследовательности выходных данных, где каждый элемент идентичен.) Например, для выходных данных 0,1,1,2 у нас есть три диапазона: 0, 1,1 и 2.Когда начинается новый промежуток, вычисляется количество возможностей для предыдущего промежутка.

Это решение было основано на нескольких наблюдениях:

  • Для интервалов длиннее 1, минимальное значение входных данных, разрешенных в первой позиции, равноКакое бы значение ни входило во вторую позицию.Вычисление количества возможностей диапазона является простой комбинаторикой, но стандартная формула требует знания диапазона чисел и длины диапазона.
  • Каждый раз, когда изменяется значение выходного сигнала (и нового существа), это сильно ограничивает значение предыдущего диапазона:
    1. Когда выходной сигнал повышается, единственная возможная причина заключается в том, чтопредыдущий вход был значением нового, более высокого выхода, а вход, соответствующий положению нового, более высокого выхода, был еще выше.
    2. Когда выход понижается, устанавливаются новые ограничения, но онинемного сложнее сформулировать.Алгоритм хранит лестницы (см. Ниже), чтобы количественно оценить ограничения, накладываемые при снижении выходного сигнала

Целью здесь было ограничить диапазонвозможные значения для каждого span .Как только мы сделаем это точно, вычислить количество комбинаций просто.

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

Таким образом, чтобы ограничить эти диапазоны ввода при уменьшении последовательности вывода, нам нужно сохранить лестница - список возможных больших значений для позиции в исходном массиве.Например, для 0,2,5,7,2,4 лестница строится следующим образом: 0, 0,2, 0,2,5, 0,2,5,7, 0,2, 0,2,4.

Используя эти границы, мы можем точно сказать, чточисло в позиции второго 2 (рядом с последней позицией в примере) должно быть в (2,5], потому что 5 - следующая ступенька.Если бы входное значение было больше 5, то вместо этого было бы 5 в этом месте. Заметьте, что если бы последним числом в кодированном массиве было не 4, а 6, мы бы вышли досрочно, возвращая 0, потому что мы знаем, что предыдущее число не могло быть больше 5.

Сложность O(n*lg(min(n,m))).

Функции

  • CombinationsWithReplacement - подсчитывает количество комбинаций с заменами размера k из n чисел.Например, для (3, 2) это считается 3,3, 3,2, 3,1, 2,2, 2,1, 1,1, поэтому возвращается 6 Это то же самое, что choose(n - 1 + k, n - 1).

  • nextBigger - находит следующий больший элемент в диапазоне.Например, для 4 в подмассиве 1,2,3,4,5 он возвращает 5, а в подмассиве 1,3 возвращает свой параметр Max.

  • countSpan (лямбда) - подсчитывает, сколько разных комбинаций может быть у пролетного , который мы только что прошли .Рассмотрим диапазон 2,2 для 0,2,5,7,2,2,7.

    1. Когда curr достигает конечной позиции, curr равно 7, а prev является окончательным 2 диапазона 2,2.
    2. Itвычисляет максимально и минимально возможные значения диапазона prev.В этот момент ступени состоят из 2,5,7, тогда максимально возможное значение равно 5 (nextBigger после 2 в stair 2,5,7).Значение, превышающее 5 в этом диапазоне, будет иметь выходное значение 5, а не 2.
    3. . Оно вычисляет минимальное значение для диапазона (которое является минимальным значением для каждого элемента вspan), который на данный момент равен prev (помните, что curr на данный момент равен 7 и prev - 2).Мы точно знаем, что вместо окончательного вывода 2 исходный ввод должен иметь 7, поэтому минимум равен 7.(Это является следствием правила «выход идет вверх». Если бы у нас было 7,7,2, а curr было бы 2, то минимум для предыдущего диапазона (7,7) был бы 8, который равен prev + 1.
    4. Регулирует количество комбинаций. Для диапазона длины L с диапазоном n возможностей (1 + макс-мин), есть k combinations with replacement of n возможностей, с k , равными L или L-1 в зависимости от того, что следует за пролетом.

      • Для интервала, за которым следует большее число, например 2,2,7, k = L - 1 , поскольку последняя позиция интервала 2,2 должнабыть 7 (значение первого числа после интервала).
      • Для интервала, за которым следует меньшее число, например 7,7,2, k = L потому что последний элемент 7,7 не имеет особых ограничений.
    5. Наконец, он вызывает CombinationsWithReplacement, чтобы узнать количество ветвей (или возможностей), вычисляет новыеres частичные результаты value (остальные значения в арифметике по модулю, которую мы делаем) и возвращает новое значение res и max для дальнейшей обработки.

  • solution -выполняет итерацию по заданному выходному массиву кодировщика.В основном цикле, находясь в промежутке, он считает длину пролета, а на границах пролета обновляет res, вызывая countSpan и, возможно, обновляет лестницу .

    • Если текущий диапазон состоит из большего числа, чем предыдущий, то:
      1. Проверьте правильность следующего числа.Например, 0,2,5,2,7 является недопустимым вводом, потому что не может быть 7 в позиции, следующей за последней, только 3, или 4, или 5.
      2. Обновлениелестница.Когда мы видели только 0,2, ступени равны 0,2, но после следующего 5 ступеньки становятся 0,2,5.
    • Если текущий диапазон состоит изменьшее число, чем предыдущее, затем:
      1. Обновляет лестницу.Когда мы видели только 0,2,5, наша лестница 0,2,5, но после того, как мы увидели 0,2,5,2, лестница становится 0,2.
    • После основного цикла она составляетпоследний промежуток путем вызова countSpan с -1, который запускает ветвь вычислений "выход идет вниз".
  • normalizeMod, extendedEuclidInternal, extendedEuclid, invMod - эти вспомогательные функции помогают работать с арифметикой по модулю.

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

#include <algorithm>
#include <cassert>
#include <vector>
#include <tuple>

const int Modulus = 1'000'000'007;

int CombinationsWithReplacement(int n, int k);

template <class It>
auto nextBigger(It begin, It end, int value, int Max) {
    auto maxIt = std::upper_bound(begin, end, value);
    auto max = Max;
    if (maxIt != end) {
        max = *maxIt;
    }
    return max;
}

auto solution(std::vector<int> &B, const int Max) {
    auto res = 1;
    const auto size = (int)B.size();
    auto spanLength = 1;
    auto prev = 0;
    // Stairs is the list of numbers which could be smaller than number in the next position
    const auto stairsBegin = B.begin();
    // This includes first entry (zero) into stairs
    // We need to include 0 because we can meet another zero later in encoded array
    // and we need to be able to find in stairs
    auto stairsEnd = stairsBegin + 1;

    auto countSpan = [&](int curr) {
        const auto max = nextBigger(stairsBegin, stairsEnd, prev, Max);
        // At the moment when we switch from the current span to the next span
        // prev is the number from previous span and curr from current.
        // E.g. 1,1,7, when we move to the third position cur = 7 and prev = 1.
        // Observe that, in this case minimum value possible in place of any of 1's can be at least 2=1+1=prev+1.
        // But if we consider 7, then we have even more stringent condition for numbers in place of 1, it is 7
        const auto min = std::max(prev + 1, curr);
        const bool countLast = prev > curr;
        const auto branchesCount = CombinationsWithReplacement(max - min + 1, spanLength - (countLast ? 0 : 1));
        return std::make_pair(res * (long long)branchesCount % Modulus, max);
    };

    for (int i = 1; i < size; ++i) {
        const auto curr = B[i];
        if (curr == prev) {
            ++spanLength;
        }
        else {
            int max;
            std::tie(res, max) = countSpan(curr);
            if (prev < curr) {
                if (curr > max) {
                    // 0,1,5,1,7 - invalid because number in the fourth position lies in [2,5]
                    // and so in the fifth encoded position we can't something bigger than 5
                    return 0;
                }
                // It is time to possibly shrink stairs.
                // E.g if we had stairs 0,2,4,9,17 and current value is 5,
                // then we no more interested in 9 and 17, and we change stairs to 0,2,4,5.
                // That's because any number bigger than 9 or 17 also bigger than 5.
                const auto s = std::lower_bound(stairsBegin, stairsEnd, curr);
                stairsEnd = s;
                *stairsEnd++ = curr;
            }
            else {
                assert(curr < prev);
                auto it = std::lower_bound(stairsBegin, stairsEnd, curr);
                if (it == stairsEnd || *it != curr) {
                    // 0,5,1 is invalid sequence because original sequence lloks like this 5,>5,>1
                    // and there is no 1 in any of the two first positions, so
                    // it can't appear in the third position of the encoded array
                    return 0;
                }
            }
            spanLength = 1;
        }
        prev = curr;
    }
    res = countSpan(-1).first;
    return res;
}

template <class T> T normalizeMod(T a, T m) {
    if (a < 0) return a + m;
    return a;
}

template <class T> std::pair<T, std::pair<T, T>> extendedEuclidInternal(T a, T b) {
    T old_x = 1;
    T old_y = 0;
    T x = 0;
    T y = 1;
    while (true) {
        T q = a / b;
        T t = a - b * q;
        if (t == 0) {
            break;
        }
        a = b;
        b = t;
        t = x; x = old_x - x * q; old_x = t;
        t = y; y = old_y - y * q; old_y = t;
    }
    return std::make_pair(b, std::make_pair(x, y));
}

// Returns gcd and Bezout's coefficients
template <class T> std::pair<T, std::pair<T, T>> extendedEuclid(T a, T b) {
    if (a > b) {
        if (b == 0) return std::make_pair(a, std::make_pair(1, 0));
        return extendedEuclidInternal(a, b);
    }
    else {
        if (a == 0) return std::make_pair(b, std::make_pair(0, 1));
        auto p = extendedEuclidInternal(b, a);
        std::swap(p.second.first, p.second.second);
        return p;
    }
}

template <class T> T invMod(T a, T m) {
    auto p = extendedEuclid(a, m);
    assert(p.first == 1);
    return normalizeMod(p.second.first, m);
}


int CombinationsWithReplacement(int n, int k) {
    int res = 1;
    for (long long i = n; i < n + k; ++i) {
        res = res * i % Modulus;
    }
    int denom = 1;
    for (long long i = k; i > 0; --i) {
        denom = denom * i % Modulus;
    }
    res = res * (long long)invMod(denom, Modulus) % Modulus;
    return res;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Only the above is needed for the Codility challenge. Below is to run on the command line.
//
// Compile with: gcc -std=gnu++14 -lc++ -lstdc++ array_recovery.cpp
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <string.h>

// Usage: 0 1 2,3, 4 M
// Last arg is M, the max value for an input.
// Remaining args are B (the output of the encoder) separated by commas and/or spaces
// Parentheses and brackets are ignored, so you can use the same input form as Codility's tests: ([1,2,3], M)
int main(int argc, char* argv[]) {
  int Max;
  std::vector<int> B;
  const char* delim = " ,[]()";

  if (argc < 2 ) {
    printf("Usage: %s M 0 1 2,3, 4... \n", argv[0]);
    return 1;
  }

  for (int i = 1; i < argc; i++) {
    char* parse;
    parse = strtok(argv[i], delim);
    while (parse != NULL)
    {
       B.push_back(atoi(parse));
       parse = strtok (NULL, delim);
    }
  }
  Max = B.back();
  B.pop_back();

  printf("%d\n", solution(B, Max));
  return 0;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Only the above is needed for the Codility challenge. Below is to run on the command line.
//
// Compile with: gcc -std=gnu++14 -lc++ -lstdc++ array_recovery.cpp
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <string.h>

// Usage: M 0 1 2,3, 4
// first arg is M, the max value for an input.
// remaining args are B (the output of the encoder) separated by commas and/or spaces
int main(int argc, char* argv[]) {
  int Max;
  std::vector<int> B;
  const char* delim = " ,";

  if (argc < 3 ) {
    printf("Usage: %s M 0 1 2,3, 4... \n", argv[0]);
    return 1;
  }

  Max = atoi(argv[1]);
  for (int i = 2; i < argc; i++) {
    char* parse;
    parse = strtok(argv[i], delim);
    while (parse != NULL)
    {
       B.push_back(atoi(parse));
       parse = strtok (NULL, delim);
    }
  }


  printf("%d\n", solution(B, Max));
  return 0;
}

Давайте рассмотрим пример:

Макс = 5
Массив равен
0 1 3 0 1 1 3
1
1 2..5
1 3 4..5
1 3 4..5 1
1 3 4..5 1 2..5
1 3 4..5 1 2..5 >=..2 (извините, за громоздкий способ написания)
1 3 4..5 1 3..5 >=..3 4..5
Теперь посчитайте:
1 1 2 1 3 2, что составляет 12 всего.

0 голосов
/ 26 декабря 2018

Вот идея.Один из известных способов создания выходных данных - использовать стек.Мы выталкиваем его, пока элемент больше или равен, затем выводим меньший элемент, если он существует, а затем помещаем больший элемент в стек.А что если мы попытаемся сделать это в обратном направлении из вывода?

Сначала мы продемонстрируем метод стека, используя пример c∅dility.

[2, 5, 3, 7, 9, 6]
2: output 0, stack [2]
5: output 2, stack [2,5]
3: pop 5, output, 2, stack [2,3]
7: output 3, stack [2,3,7]
... etc.

Final output: [0, 2, 2, 3, 7, 3]

Теперь давайте попробуем реконструкцию!Мы будем использовать stack как мнимый стек и как восстановленный ввод:

(Input: [2, 5, 3, 7, 9, 6])
Output: [0, 2, 2, 3, 7, 3]

* Something >3 that reached 3 in the stack
stack = [3, 3 < *]

* Something >7 that reached 7 in the stack
but both of those would've popped before 3
stack = [3, 7, 7 < x, 3 < * <= x]

* Something >3, 7 qualifies
stack = [3, 7, 7 < x, 3 < * <= x]

* Something >2, 3 qualifies
stack = [2, 3, 7, 7 < x, 3 < * <= x]

* Something >2 and >=3 since 3 reached 2
stack = [2, 2 < *, 3, 7, 7 < x, 3 < * <= x]

Давайте попробуем ваши примеры:

Пример 1:

[0, 0, 0, 2, 3, 4]

* Something >4
stack = [4, 4 < *]

* Something >3, 4 qualifies
stack = [3, 4, 4 < *]

* Something >2, 3 qualifies
stack = [2, 3, 4, 4 < *]

* The rest is non-increasing with lowerbound 2
stack = [y >= x, x >= 2, 2, 3, 4, >4]

Пример 2:

[0, 0, 0, 4]

* Something >4
stack [4, 4 < *]

* Non-increasing
stack = [z >= y, y >= 4, 4, 4 < *]

Расчет количества комбинаций достигается путем умножения возможностей для всех разделов.Сечение - это либо ограниченная отдельная ячейка;или связанный, не увеличивающийся подмассив из одной или нескольких клеток.Чтобы вычислить последнее, мы используем многозначный бином, (n + k - 1) choose (k - 1).Предположим, что мы можем выразить различия между ячейками связанной, не увеличивающейся последовательности 3 ячеек как:

(ub - cell_3) + (cell_3 - cell_2) + (cell_2 - cell_1) + (cell_1 - lb) = ub - lb

Тогда число способов распределения ub - lb в (x + 1) ячейках равно

(n + k - 1) choose (k - 1)
or
(ub - lb + x) choose x

For example, the number of non-increasing sequences between
(3,4) in two cells is (4 - 3 + 2) choose 2 = 3: [3,3] [4,3] [4,4]

And the number of non-increasing sequences between
(3,4) in three cells is (4 - 3 + 3) choose 3 = 4: [3,3,3] [4,3,3] [4,4,3] [4,4,4]

(Объяснение Брайан М. Скотт .)

Грубый эскиз JavaScript (код ненадежный; он предназначен только для иллюстрации кодировки. Кодировщикперечисляет [lower_bound, upper_bound] или не увеличивающуюся последовательность как [non_inc, length, lower_bound, upper_bound]):

function f(A, M){
  console.log(JSON.stringify(A), M);
  let i = A.length - 1;
  let last = A[i];
  let s = [[last,last]];
  if (A[i-1] == last){
    let d = 1;
    s.splice(1,0,['non_inc',d++,last,M]);
    while (i > 0 && A[i-1] == last){
      s.splice(1,0,['non_inc',d++,last,M]);
      i--
    }
  } else {
    s.push([last+1,M]);
    i--;
  }
  if (i == 0)
    s.splice(0,1);

  for (; i>0; i--){
    let x = A[i];

    if (x < s[0][0])
      s = [[x,x]].concat(s);

    if (x > s[0][0]){
      let [l, _l] = s[0];
      let [lb, ub] = s[1];
      s[0] = [x+1, M];
      s[1] = [lb, x];
      s = [[l,_l], [x,x]].concat(s);
    }

    if (x == s[0][0]){
      let [l,_l] = s[0];
      let [lb, ub] = s[1];
      let d = 1;
      s.splice(0,1);
      while (i > 0 && A[i-1] == x){
        s =
    [['non_inc', d++, lb, M]].concat(s);
        i--;
      }
      if (i > 0)
        s = [[l,_l]].concat(s);
    }
  }

  // dirty fix
  if (s[0][0] == 0)
    s.splice(0,1);

  return s; 
}

var a = [2, 5, 3, 7, 9, 6]
var b = [0, 2, 2, 3, 7, 3]
console.log(JSON.stringify(a));
console.log(JSON.stringify(f(b,10)));
b = [0,0,0,4]
console.log(JSON.stringify(f(b,10)));
b = [0,2,0,0,0,4]
console.log(JSON.stringify(f(b,10)));
b = [0,0,0,2,3,4]
console.log(JSON.stringify(f(b,10)));
b = [0,2,2]
console.log(JSON.stringify(f(b,4)));
b = [0,3,5,6]
console.log(JSON.stringify(f(b,10)));
b = [0,0,3,0]
console.log(JSON.stringify(f(b,10)));
...