哪些因素决定了 lambda 函数使用的内存?

The*_*ter 4 lambda google-sheets space-complexity google-sheets-formula named-function

=SUM(SEQUENCE(10000000))
Run Code Online (Sandbox Code Playgroud)

上面的公式最多可以对 1000 万个虚拟数组元素进行求和。根据这个问答我们知道1000万是极限。现在,如果使用 Lambda 辅助函数将其实现为 Lambda REDUCE

=REDUCE(,SEQUENCE(10000000),LAMBDA(a,c,a+c))
Run Code Online (Sandbox Code Playgroud)

我们得到,

尝试计算此公式时已达到计算限制

官方文档

这可能在两种情况下发生:

  • 公式的计算时间太长。
  • 它使用了太多内存。

要解决此问题,请使用更简单的公式来降低复杂性。

所以,它说原因是空间和时间复杂性。但是抛出这个错误的确切空间是多少呢?这是如何确定的?

REDUCE上面的函数中,虚拟数组的限制约为 66k:

=REDUCE(,SEQUENCE(66660),LAMBDA(a,c,a+c))
Run Code Online (Sandbox Code Playgroud)

但是,如果我们删除添加条件并使其仅返回当前值c,则允许的虚拟数组大小似乎会增加到 190k:

=REDUCE(,SEQUENCE(190000),LAMBDA(a,c,c))
Run Code Online (Sandbox Code Playgroud)

之后它会抛出一个错误。那么,这里的内存限制是由哪些因素决定的呢?我认为这是内存限制,因为它几乎在几秒钟内抛出错误。

The*_*ter 7

如果您受到此问题的影响,您可以向 Google 发送反馈:

  1. 打开电子表格,最好是您遇到问题的电子表格。
  2. 用匿名但真实的数据替换任何敏感信息。删除重现问题不需要的任何敏感信息。
  3. 选择“帮助”>“报告问题”“帮助”>“改进帮助表”。如果您使用的是付费 Google Workspace 网域,请参阅联系 Google Workspace 支持人员
  4. 解释为什么计算限制对您来说是一个问题。
  5. 要求:
    • 正义:消除 lambda 函数的任意限制
    • 平等:避免对 lambda 函数的歧视
    • 透明度:更清晰、更详细地记录上述歧视
  6. 包含此 Stack Overflow 答案帖子的链接。

22 年 10 月更新MaxMarkhov 提供

现在该限制提高了 10 倍,达到 190 万1999992。这仍然不到非 lambda 公式的 1000 万个虚拟数组限制的 1/5,但比以前好得多。此外,非 lambda 公式的限制不会随着运算次数的增加而减少。但 lambda 辅助公式限制仍然会随着操作次数的增加而减少。因此,尽管它高出 10 倍,但这仅意味着 lambda 内部需要大约 5 个额外操作(见下表)。


部分答案

我们知道,事实上,以下因素决定了滚轮的计算极限:

  • 操作次数
  • (嵌套)LAMBDA()函数调用

1 运算的基数似乎是199992 1 2 ( =REDUCE(,SEQUENCE(199992),LAMBDA(a,c,c)))。但是对于零操作或无操作(=REDUCE(,SEQUENCE(10000000),LAMBDA(a,c,0))),内存限制要高得多,但您仍然会遇到时间限制。我们还知道操作次数是一个因素,因为

  • =REDUCE(,SEQUENCE(66664/1),LAMBDA(a,c,a+c))失败
  • =REDUCE(,SEQUENCE(66664),LAMBDA(a,c,a+c))作品。
  • =REDUCE(,SEQUENCE(66664),LAMBDA(a,c,a+c+0))失败

请注意,操作数的大小并不重要。如果=REDUCE(,SEQUENCE(39998),LAMBDA(a,c,a+c+0))有效,=REDUCE(,SEQUENCE(39998),LAMBDA(a,c,a+c+100000))也将有效。

对于 lambda 函数内操作数量的每次增加,允许的最大数组大小都会下降2n-1感谢@OlegValter,实际上发现这里有一个因子倍数):

最大序列 操作数
(在 lambda 内)
减少
(从199992开始)
公式
199992 1 1 减少(,序列(199992),LAMBDA(a,c,c))
66664 2 1/3 减少(,序列(66664),LAMBDA(a,c,a + c))
39998 3 1/5 减少(,序列(39998),LAMBDA(a,c,a + c + 10000))
28570 4 1/7 减少(,序列(28570),LAMBDA(a,c,a + c + 10000 + 0))

函数之外的操作LAMBDA 也很重要。例如,=REDUCE(,SEQUENCE(199992/1),LAMBDA(a,c,c))会由于额外的/1操作而失败,但您只需要每次操作将数组大小线性减少 1 或 2,即,这=REDUCE(,SEQUENCE(199990/1),LAMBDA(a,c,c))将起作用3

此外,LAMBDA函数调用本身的成本更高。因此,重构代码并不能消除内存限制,而是进一步减少内存限制。例如,如果您的代码使用LAMBDA(a,c,(a-1)+(a-1)),如果您添加另一个像这样的 lambda: LAMBDA(a,c,LAMBDA(aminus,aminus+aminus)(a-1)),则错误的数组元素比以前少得多(减少了约 20%)。LAMBDA比重复调用要贵得多。

还有许多其他因素在起作用,尤其是其他LAMBDA功能。谷歌稍后可能会改变对这些任意限制的看法。但这是一个开始。


可能的解决方法:

  • LAMBDA本身不受限制。您可以随心所欲地嵌套。仅 LAMBDA 辅助函数受到限制。(归功于玩家0

  • 本身不使用LAMBDA(辅助函数)的命名函数不受相同的限制。但它们受到最大递归限制。

  • 另一种解决方法是避免使用lambdaasarrayformula并使用自动填充或拖动填充功能,方法是使 lambda 函数每个函数仅返回一个值。请注意,这实际上可能会使您的工作表变慢。但显然,谷歌对此表示同意——多个单独的调用而不是单个数组调用。例如,我在这里编写了一个排列函数来获取所有排列的列表。虽然它抱怨具有超过 6 个项目的数组的“内存限制”,但它可以通过相对范围的自动填充/拖动填充/复制+粘贴轻松工作。