【问题标题】:A non-VBA recursive factorial function?非VBA递归阶乘函数?
【发布时间】:2020-12-20 20:21:42
【问题描述】:

是否可以通过启用循环引用(在Excel OptionsFormulas 部分中选择enable iterative calculation)在Excel 中创建递归阶乘函数?我当然知道FACT(),我并不是在寻找一种计算阶乘的实用方法。相反,我的目标是找到一种利用循环引用作为在 Excel 中创建和使用递归函数的通用工具的方法,阶乘提供了一个有趣的测试用例。

使用 Jan Karel Pietrerse 的 website 的一个想法,我可以接近,但生成的函数取决于两个单元格而不是一个单元格,因此它不能解决问题。在

我使用顶行中的字符串创建了命名范围。在现在名为 factorial 的单元格中,我输入了:

=IF(initializing,1,IF(factor=0,factorial,factorial*factor))

在单元格factor 中我输入了:

=IF(initializing,n,IF(factor=0,factor,factor-1))

上图显示了n = 10initializing = True 时的样子。 factorial 中的公式对应于函数式编程中的标准技巧,通过引入具有累积参数的辅助函数来生成递归函数 tail-call recursive。问题是需要调用辅助函数,而阶乘中的公式在某种意义上既是函数本身也是它的辅助函数,initializing 的内容决定了它当前扮演的角色。

从某种意义上说,如果我将 initializing 的值从 True 切换(例如删除它),那么 factorial 的值将成为正确的阶乘:

initializing可以从图片中去掉吗?是否可以修改设置,例如,如果n 更改为 5 然后 factorial 立即更改为 120 而无需先设置然后更改其他单元格?我尝试了几种不同的方法,但最终还是以 2 步而不是 1 步功能结束。也许是一些涉及数组公式的魔法?

【问题讨论】:

  • 当您让公式直接引用自身时,我不明白这是如何工作的。它不是给你一个循环引用错误吗?从概念上讲,说“= 本身”是什么意思?
  • 是的,当然是循环引用。在问题的第一句话中,我提到了启用循环引用。 (我只是添加了一个简短的括号来说明如何这样做)。
  • 内置函数 FACT() 不够好有什么特别的原因吗?
  • 您可以在 VBA 中轻松地做到这一点,但我怀疑在您的工作表中出于此类目的使用循环引用会导致一些不必要的复杂和性能噩梦。
  • @iDevlop 我们的目标是尽可能利用 Excel 进行迭代计算的能力。 Excel 的计算引擎比它的 VBA 解释器更快,所以我的最终目标是找到一种方法将某些递归计算从 VBA 分流到 Excel。这可能不值得,但探索可能性不会有坏处。

标签: excel recursion excel-formula


【解决方案1】:

这是一个只有一个辅助单元格的公式,每当 n 更改时自动更新,当然辅助列比原来的更复杂:

  • 帮手:=IF(C2<>TEXT(A2,"0"),IF(ISNUMBER(C2),IF(C2=A2,TEXT(A2,"0"),C2+1),1),C2)
  • 阶乘:=IF(ISNUMBER(C2),IF(C2=1,1,C2*D2),D2)

关键是在达到结果时更改辅助单元格(也可以否定,用文本完成(例如A1&" Finished",重要的是要明确它已经到达计算结束并保持它与输入单元格)。

只是为了好玩,F2中没有迭代的数组公式:=PRODUCT(ROW(INDIRECT("a1:a"&A2,TRUE)))

更新

公式一步一步:
帮手:

  • 稳定状态被格式化为文本,只要文本的值与n相同,什么都不会发生,公式不会改变helper中的值
  • 一旦n 更改:第一个条件C2<>TEXT(A2,"0") 将为假,但helper 仍然是文本,所以第二个条件也是假,helper 重置为1
  • 之后helper递增直到达到n,当达到目标时helper转换为文本以标记计算完成

阶乘:

  • 虽然helper 是文本,但没有任何反应
  • 一旦 helper 是数字:如果是 1,则 factorial 重置为 1,否则计算乘法 helper * factorial

【讨论】:

  • 非常好。在我完全理解 TextIsNumber 发生了什么之前,我需要考虑一段时间,但似乎文本“0”到数字 0 的转换大致对应于删除 @ 的值987654343@ 在我的原始设置中,并且由于 Excel 自动进行类型转换,因此在该步骤中计算不需要人工干预。 (数组公式也不错,但并不令人惊讶)。
  • @JohnColeman:我还添加了分步说明以使其更加清晰。
  • 很好的解释。通过在文本和数字之间来回切换来控制计算的方式非常巧妙。
【解决方案2】:

是的,可以从算法中删除“初始化”变量。

我们来看一个例子。

我调用了 3 个单元格:numberfactorialiteration

首先,在使用迭代计算时,您应该记住的主要内容是检查 0 的 IF 语句,因为默认情况下空单元格为 0,并且,正如我们所知,乘法中的零(主要部分阶乘)是个坏主意。

iteration 单元格得到这个公式: =IF(i=0, number+1,i-1)

factorial 单元格得到这个公式: =IF(factorial=0, 1, IF(i=1, factorial, factorial*i))

因此,如果factorial 单元格中的iteration 单元格为0 或1(这取决于excel 的选择和月相),您应该得到正确的答案。

当然,通过更改number,您可以注意到iteration 单元格不是0 或1,而factorial 看起来不像预期的结果。试着按 F9 直到你听不懂为止。

【讨论】:

    【解决方案3】:

    假设我们想创建一个具有以下限制的阶乘公式:

    1. 不要使用内置的FACT()工作表函数
    2. 不要启用迭代计算
    3. 不要使用 VBA
    4. 不要使用手动输入的数组公式
    5. 不要使用“辅助”单元格

    使用 Excel 365,在单元格 A1 中输入一些值(比如 8)
    A2输入:

    =PRODUCT(SEQUENCE(A1))
    

    SEQUENCE() 函数给出的“历史”为 8,并允许我们模拟?递归。

    编辑#1:

    在我的 Win 7 机器上使用 Excel 2007,这个数组公式

    =PRODUCT(ROW(INDIRECT("1:" & A1)))
    

    提供等价物。我不得不违反限制 #4,因为 Excel 2007 中没有动态溢出。

    【讨论】:

    • 有趣。 SEQUENCE 在 Excel 2016 中不可用。如果没有 VBA 的 Excel 是 Turing complete,我提出这个问题的动机之一是好奇。您的解决方案似乎比递归更具迭代性,但足以表明 Excel 最近的电子表格函数可能足以使电子表格公式语言 Turing 完整,即使没有启用循环引用(但可能使用数组公式)。
    • @JohnColeman 如果我可以启动我的旧 WIN 7 笔记本电脑,我会尝试在旧版本的 Excel 上找到等效的。
    【解决方案4】:

    这就是我的评论的意思 - 每当“n”的值发生变化时,使用 VBA 来“选中/取消选中”初始化框。

    我用你在你的例子中的命名范围尝试了这个,它对我有用。可以推断用于您的其他目的;这里的关键是你仍然需要一个“初始化”单元格[真的你可以让“初始化”是一个命名范围,它只引用一个布尔值,而不是一个包含布尔值的单元格],但它至少会自动化。

    Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
    
    
    If Target.Address = Range("n").Address Then
    
        Range("initializing").Formula = True
        Range("initializing").Formula = False
    End If
    
    End Sub
    

    【讨论】:

    • 我赞成这一点,因为如果我们要使用 Excel 的 VBA 迭代计算,它是可能需要的,但我不会(还)接受它作为最终答案,因为我仍然想知道是否可以将纯非 VBA 版本降低到单个输入单元格。
    猜你喜欢
    • 1970-01-01
    • 2019-08-06
    • 1970-01-01
    • 2015-04-16
    • 1970-01-01
    • 2017-05-07
    • 2012-04-01
    • 2015-01-08
    • 1970-01-01
    相关资源
    最近更新 更多