【问题标题】:Reconstructing input to encoder from output从输出重构编码器的输入
【发布时间】:2018-12-26 06:17:48
【问题描述】:

我想了解如何解决 Codility ArrayRecovery 挑战,但我什至不知道该咨询哪个知识分支。是组合学、优化、计算机科学、集合论还是其他?

编辑: 要咨询的知识分支是constraint programming,尤其是constraint propagation。您还需要一些 combinatorics 来知道,如果您一次从 [1..n] 范围内获取 k 个数字,并且限制不能是数字比前面那个大,结果是 (n+k-1)!/k!(n-1)! 种可能的组合 这与一次 kn 个事物的 combinations with replacement 的数量相同,其具有数学符号 。您可以了解为什么它会像 here 那样工作。

Peter Norvig 通过他的Sudoku solver 提供了一个很好的例子来说明如何解决这类问题。


您可以通过上面的链接阅读 ArrayRecovery 问题的完整描述。简短的故事是,有一个 encoder 接收范围为 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基于索引)

编辑#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 个输出即可。第三个序列被限制到不可能。

但是示例 #2 中的 A5 约束集有点难以表达。当然 A5 > O5,这是对所有输入的基本约束。但是任何 > A4 和 O5 之后的输出都必须出现在 A4 之后的输入中,因此 A5 必须是 A5 之后的数字集合中的一个元素,也就是 > A4。因为只有 1 个这样的数字(A6 =​​= 4),所以 A5 必须是它,但如果后面有更长的数字串,它会变得更加复杂。 (编者注:实际上并没有。)

随着输出集变得越来越长,我担心这些约束会变得更加复杂且难以正确处理。我想不出任何数据结构可以有效地表示这些,从而有效地计算可能组合的数量。我也不太明白如何通过算法将约束集添加在一起。

这是我目前看到的任何给定 An

的约束
  • An > On
  • An1 到 n-1 的其他可能数的集合 > On子>)。如何定义大于 On 的可能数集?
    1. 在输入中最近出现的 On 之后出现的大于 On 的数字
  • An >= max(从 O1n-1n 的其他可能数的集合子>)。如何定义小于 On 的可能数集?
    1. 实际上这个集合是空的,因为根据定义,On 是前一个输入序列中最大的可能数。 (这并不是说它严格来说是前一个输入序列中的最大数字。)
    2. 由于“最近”规则,在输入中最后一次出现之前出现的小于 On 的任何数字都是不合格的。由于“最近”规则和传递属性,在最近一次出现之后不可能出现小于 On 的数字:如果 Ai n 和 Aji 然后 Ajn
  • 然后是集合论:
    1. An 必须是 On+1 到 Om 集合的未解释元素集合中的一个元素,其中 m 是最小的 m > n 使得 Omn。在这样的 Om 之后并且大于 Om (其中 An 是)的任何输出都必须出现在 Am 之后或之后.
    2. 如果一个元素在输出中可见但未出现在输入中的与输出的其余部分一致的位置,则该元素是下落不明的。显然,我需要一个比这更好的定义来编写代码和算法来计算它。

似乎某种集合论和/或组合学或线性代数可能有助于确定可能的序列数量,这些序列将解释所有未解释的输出并适合其他约束。 (编者注:实际上,事情从来没有变得那么复杂。)

【问题讨论】:

    标签: algorithm data-structures combinatorics constraint-programming


    【解决方案1】:

    下面的代码通过了 Codility 的所有测试。 OP 添加了一个main 函数以在命令行上使用它。

    约束并不像 OP 想象的那么复杂。特别是,永远不会出现需要添加限制,即输入是输出中其他地方看到的某些特定整数集合的元素。每个输入位置都有明确定义的最小值和最大值。

    该规则的唯一复杂之处在于,有时最大值是“前一个输入的值”,并且该输入本身有一个范围。但即便如此,所有类似的值都是连续的并且具有相同的范围,因此可以使用基本组合计算可能性的数量,并且这些输入作为一个组独立于其他输入(仅用于设置范围) ,因此该组的可能性可以通过简单的乘法与其他输入位置的可能性相结合。

    算法概述

    该算法在每个 span 之后通过输出数组更新输入数组的可能数量,这就是我所说的输出中数字的重复。 (您可能会说每个元素都相同的输出的最大子序列。)例如,对于输出0,1,1,2,我们有三个跨度:01,12。当一个新的跨度开始时,计算前一个跨度的可能性数量。

    这个决定是基于一些观察:

    • 对于长度大于 1 的 spans,输入的最小值 允许在第一个位置是输入的任何值 在第二个位置。计算a的可能性的数量 span 是简单的组合,但标准公式 需要知道数字的范围和跨度的长度。
    • 每次的值 输出变化(和一个新的跨度存在),这强烈地限制了前一个跨度的值:
      1. 当输出上升时,唯一可能的原因是之前的输入是新的更高输出的值,而与新的更高输出的位置对应的输入甚至更高。
      2. 当输出下降时,会建立新的约束,但这些约束有点难以表达。该算法存储 stairs(见下文),以便量化输出下降时施加的约束

    这里的目的是限制每个 span 的可能值范围。一旦我们准确地做到了这一点,计算组合的数量就很简单了。

    因为编码器回溯希望以 2 种方式输出与输入相关的数字,更小和更接近,我们知道我们可以抛出更大和更远的数字。在输出中出现一个小数字后,该位置之前的更大数字不会对后面的内容产生任何影响。

    因此,为了在输出序列减少时限制这些输入范围,我们需要存储 stairs - 原始数组中位置的可能值越来越大的列表。例如,0,2,5,7,2,4 楼梯的构建方式如下:00,20,2,50,2,5,70,20,2,4

    使用这些边界,我们可以确定第二个2 位置的数字(示例中的最后一个位置旁边)必须在(2,5] 中,因为5 是下一个楼梯。如果输入大于 5,则会在该空间中输出 5 而不是 2。请注意,如果编码数组中的最后一个数字不是4,而是6,我们将退出提前返回@ 987654341@,因为我们知道前面的数字不能大于5

    复杂度是O(n*lg(min(n,m)))

    功能

    • CombinationsWithReplacement - 从 n 数字中计算 number of combinations with replacements 的大小为 k。例如。对于(3, 2),它计数3,33,23,12,22,11,1,所以返回6它与choose(n - 1 + k, n - 1)相同。

      李>
    • nextBigger - 在一个范围内找到下一个更大的元素。例如。对于4 在子数组1,2,3,4,5 中返回5,在子数组1,3 中返回其参数Max

    • countSpan (lambda) - 计算 我们刚刚通过 的跨度可以有多少种不同的组合。考虑跨度2,20,2,5,7,2,2,7

      1. curr 到达最终位置时,curr7prev2 范围内的最后一个2
      2. 它计算prev 范围的最大和最小可能值。此时楼梯由2,5,7 组成,那么最大可能值为5nextBigger2 之后stair 2,5,7)。在此范围内大于 5 的值将输出 5,而不是 2
      3. 它计算跨度的最小值(这是跨度中每个元素的最小值),此时为prev,(记住此时curr等于7和@987654383 @ 到 2)。我们肯定知道,代替最终的2 输出,原始输入必须有7,所以最小值是7。 (这是“输出上升”规则的结果。如果我们有7,7,2curr 将是2,那么前一个跨度(7,7)的最小值将是8,即@ 987654393@.
      4. 它调整组合的数量。对于长度为 L 且具有 n 个可能性范围(1+max-min)的跨度,有 个可能性,kLL-1 取决于跨度之后的内容。

        • 对于后跟 更大 数字的跨度,例如 2,2,7k = L - 1 因为2,2 跨度的最后位置必须是 @ 987654396@(跨度后第一个数字的值)。
        • 对于后跟 较小 数字的跨度,例如 7,7,2k = L 因为 7,7 的最后一个元素没有特殊限制。
      5. 最后,它调用CombinationsWithReplacement 找出分支的数量(或可能性),计算新的res 部分结果值(我们正在做的模运算中的剩余值),并返回新的@987654401 @value 和 max 供进一步处理。

    • solution - 遍历给定的编码器输出数组。在主循环中,在跨度中,它计算跨度长度,并在跨度边界处通过调用countSpan 更新res,并可能更新楼梯

      • 如果当前跨度包含比前一个更大的数字,则:
        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 来计算最后一个跨度,这会触发计算的“输出下降”分支。
    • normalizeModextendedEuclidInternalextendedEuclidinvMod - 这些辅助函数有助于处理模运算。

    对于楼梯,我使用存储编码数组,因为楼梯的数量永远不会超过当前位置。

    #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 &gt;=..2(抱歉,麻烦了写作方式)
    1 3 4..5 1 3..5 &gt;=..3 4..5
    现在数一下:
    1 1 2 1 3 2 总计 12

    【讨论】:

    • 谢谢。请添加更多解释。首先,我的 C++ 生锈了,auto countSpan = [&amp;](int curr) 是什么意思?其次,如何定义span 边界。第三,您如何表示/存储长度为 5 的跨度必须在 [n..m] 范围内并且还包含数字 5 和 9?那么,给定这组约束,你如何计算组合的数量?
    • 是的。感谢您添加的解释。我打算给你奖金,但我还有更多问题。请更具体地解释什么是跨度。你如何决定一个结束和下一个开始?如果7,7,2curr2 为什么是最小值prev + 1?最大不会是prev,否则输出将是另一个7而不是2?另外,当输入中永远不会出现零时,为什么所有楼梯中都有零?我看不出你在哪里缩小楼梯的长度:你在哪里做的?
    • @OldPro span 只是一个子序列,所有数字都相同。在1,1,2,2 我们有两个跨度。在某个时刻,prev 正是我们刚刚通过的 span 中的数字(我们可以说它的 generator)。因此,当索引为2(以0 开头)prev = 1 并且我们通过的跨度为1,1。此时,在跨度1,1 的任何位置中,原始数组中可能存在的最小数量是prev + 1 = 1 + 1 = 2 &gt; 1
    • @OldPro 在 7,7,2 的情况下,如果我们刚刚完成 span 7,7,则最小值将是 8,因为 8 是大于 7 的最小数字。跨度>
    • @OldPro 这行stairsEnd = s; 缩小了楼梯。使用lower_bound,我们找到值不小于curr的位置,我们用curr重写这个数字,并将楼梯的尽头设置到这个位置。例如。如果我们有楼梯0,1,5,7,我们在编码数组中遇到的下一个值是3,那么新楼梯看起来像0,1,3。我们对57 不再感兴趣,因为任何大于57 的即将出现的值也大于33 显然更接近它。
    【解决方案2】:

    这是一个想法。构造输出的一种已知方法是使用堆栈。我们在元素大于或等于时弹出它,然后输出较小的元素(如果存在),然后将较大的元素压入堆栈。现在,如果我们尝试从输出向后执行此操作会怎样?

    首先,我们将使用 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]
    

    (归因于Brian M. Scott的解释。)

    粗略的 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)));

    【讨论】:

    • 我看不出这有什么帮助。
    • @OldPro 您怀疑哪一部分 - 能够逆向工程本来的堆栈序列或从中推断出可能性的数量?
    • @OldPro 我认为重构堆栈过程的逻辑是可行的,并且有一个相当简单的组合来计算绑定的非递增序列。如果我有时间,我会尝试编写一些代码。
    • @我不明白堆栈如何帮助逆向工程。如果我对所有输入都有下限和上限,我已经知道如何计算结果。
    • @OldPro 我认为本来堆栈的想法可以帮助制定一种算法,以生成类似于我的答案中三个示例中的最后一个表达式的表达式(并在适当的情况下包括 M,codility 的上限)。给定这些表达式,你能推断出可能性的数量吗?您能否添加一个示例,说明如何计算给定下限和上限的结果?我正在谈论的表达式在我看来是每个输入的一个表达式(嗯,技术上是“输出”:) 你的意思是每个问题输入有多个表达式(意思是“输出”)吗?
    猜你喜欢
    • 2023-03-04
    • 2021-08-05
    • 2019-10-14
    • 2019-07-21
    • 1970-01-01
    • 2013-10-03
    • 2012-10-29
    • 2016-06-07
    • 1970-01-01
    相关资源
    最近更新 更多