在上一次的文章中,我们介绍了一种可以查找任意多个数字,使其合计等于给定值x的方法。这个方法的本质就是将问题变为更小一些的问题:

原问题:从原来的50个数中查找n个元素,其合计等于x。

新问题:从49个数中查找n-1个元素,其合计等于x-a。

这个思路启发了我们,可以使用分而治之(或减而治之)的方法解决这个问题。

分支法


我们的问题是在一个数组arr中,找到n个元素,使得它们的合计等于x

excel 用公式查找排第三的数(Excel函数解决查找任意n个数字之和)(1)

假设存在这样一个函数的话,这个问题可以这么记:

=FIND_ANY_NUMBER(arr, n, x)

我们可以将这个数组分为左右两部分:

excel 用公式查找排第三的数(Excel函数解决查找任意n个数字之和)(2)

左半部分只有一个元素,右半部分有n-1个元素。

原问题可以分为两种情况。

  1. 所有的n个元素都在右半部分:

excel 用公式查找排第三的数(Excel函数解决查找任意n个数字之和)(3)

即:

FIND_ANY_NUMBER(arr, n, x) = FIND_ANY_NUMBER(arr_right, n, x)

2. 左半部分是其中的一个元素,其余n-1个元素在右半部分中,这时右边的n-1个元素合计为 :x - 左半部分元素

即:

FIND_ANY_NUMBER(arr, n, x) = HSTACK(arr_left, FIND_ANY_NUMBER(arr_right, n - 1, x arr_left)

这样,我们就可以使用递归了。

一直到在一个数组中查找1个元素,问题可以用MATCH函数简单解决,递归结束。

自定义函数


这样,就可以创建下面的自定义函数:

FIND_ANY_NUMBER = LAMBDA(arr, n, x, IF( n > COUNTA(arr), //如果要找的元素个数n大于数组元素个数,则无解 -1, IF( n = 1, //如果要找一个元素, LET( i, IFERROR(MATCH(x, arr, 1), -1), IF( i = -1, //没有找到 -1, IF( INDEX(arr, i) <> x, //找到的不是目标值 -1, INDEX(arr, i)//k i ) ) ), //否则的话,分为两种情况 LET( result, FIND_ANY_NUMBER(DROP(arr, 1), n, x), IF( SUM(--(result = -1)) = 0, //成功了 result, //HSTACK(k 1, FIND_ANY_NUMBER(DROP(arr, 1), n - 1, x - INDEX(arr, k 1), k 1)) HSTACK(INDEX(arr, 1), FIND_ANY_NUMBER(DROP(arr, 1), n - 1, x - INDEX(arr, 1))) ) ) ) ) );

解释一下:

函数的输入参数是一个数组arr,要查找的元素个数n,以及要查找的元素x。

函数使用IF语句进行条件分支:

如果要查找的元素个数n大于数组元素个数,则无解,返回-1。

如果要查找的元素个数n等于1,则查找一个元素。在这种情况下,如果找到了目标值,则返回其索引,否则返回-1。

如果要查找的元素个数n大于1,则查找多个元素。在这种情况下,函数会对数组进行递归调用,将数组中的第一个元素删除,并将查找数量减少1。如果递归调用返回-1,则表示没有找到目标值,函数将返回-1。否则,如果递归调用返回非-1的结果,则将其返回。如果递归调用返回多个结果,则将它们与数组中的第一个元素的索引组合成一个水平堆栈(HSTACK),并将其返回。

excel 用公式查找排第三的数(Excel函数解决查找任意n个数字之和)(4)

,