使用LAMBDA在Excel中生成所有排列

ifsvaxew  于 2023-05-30  发布在  其他
关注(0)|答案(5)|浏览(209)

这是一个经常被问到和回答的问题:如何在Excel中生成所有排列
201120162017 2017超级用户2018 2021
现在到了2022年,在它作为重复被关闭之前没有得到答案,这很不幸,因为LAMBDA真的改变了这个问题的答案。
我很少有同样的需求,并因不得不重新发明一个复杂的轮子而感到沮丧。所以,我会重新提出这个问题,并在下面给出我自己的答案。我不会将任何意见书标记为答案,但邀请好的想法。我相信我自己的方法可以改进。

重申2022年问题

我试图在Excel中创建一个仅包含公式的循环。我试图实现的目标如下所述。假设我有3列作为输入:㈠国家;(ii)可变的;及(iii)年。我想从这些输入展开,然后为这些参数赋值。

输入:

| 国家|变量|年份|
| - -----|- -----|- -----|
| GB|国内生产总值|2015年|
| DE|面积|2016年|
| CH|面积|2015年|

输出:

| 国家|变量|年份|
| - -----|- -----|- -----|
| GB|国内生产总值|2015年|
| GB|国内生产总值|2016年|
| GB|面积|2015年|
| GB|面积|2016年|
| DE|国内生产总值|2015年|
| DE|国内生产总值|2016年|
| DE|面积|2015年|
| DE|面积|2016年|
如何有效地使用Excel?

2018年问题展开

我有三个列,每个列都有不同类型的主数据,如下所示:

现在,我想要这三个细胞的所有可能的组合

aa kk jj
aa kk ff
aa ll jj
aa ll ff
aa mm jj
...

这可以通过公式来实现。我发现一个公式有2列,但我不能正确地将它扩展到3列
包含2列的公式:

=IF(ROW()-ROW($G$1)+1>COUNTA($A$2:$A$15)*COUNTA($B$2:$B$4),"",
INDEX($A$2:$A$15,INT((ROW()-ROW($G$1))/COUNTA($B$2:$B$4)+1))&
INDEX($B$2:$B$4,MOD(ROW()-ROW($G$1),COUNTA($B$2:$B$4))+1))

其中,G1是要放置结果值的像元

通用要求

它们的共同点是,它们都试图从一组有序的符号中创建一组有序的排列。他们都需要3个级别的符号,但2018年的问题是要求帮助从2级到3级,而2021年的问题是要求从3级到5级。2022年的问题只是要求3个级别,但输出需要是一个表。
如果我们像这样升到6级呢?
| L1| L2| L3| L4| L5| L6|
| - -----|- -----|- -----|- -----|- -----|- -----|
| 一个|F级|K|压力|U|一个|
| B| G级|L形|Q类|V| 2|
| C类|H型||R型|W| 3|
| D||||X| 4|
| E级||||||
这将产生1 '440个排列。
| L1| L2| L3| L4| L5| L6|
| - -----|- -----|- -----|- -----|- -----|- -----|
| 一个|F级|K|压力|U|一个|
| 一个|F级|K|压力|U| 2|
| 一个|F级|K|压力|U| 3|
| 一个|F级|K|压力|U| 4|
| 一个|F级|K|压力|V|一个|
| 一个|F级|K|压力|V| 2|
| 一个|F级|K|压力|V| 3|
| 一个|F级|K|压力|V| 4|
| 一个|F级|K|压力|W|一个|
| ......这是什么?|......这是什么?|......这是什么?|......这是什么?|......这是什么?|......这是什么?|

**制作一个包含任意数量的水平(列)的通用公式是很难的。只要浏览一下提供的答案-它们每个都需要一些火箭科学,直到现在,所有的解决方案都对符号的列数进行了硬编码限制。那么LAMBDA能给予我们一个通用的解决方案吗?

vdzxcuhz

vdzxcuhz1#

冷静的问题和脑筋急转弯;我只是对使用MAKEARRAY()的东西感到困惑:

选项一:

你所谓的“超级低效”是在计算行^列时创建一个排列列表。我认为下面的方法并不那么低效。让我们想象一下:

E1中的公式:

=LET(A,A1:C3,B,ROWS(A),C,COLUMNS(A),D,B^C,E,UNIQUE(MAKEARRAY(D,C,LAMBDA(rw,cl,INDEX(IF(A="","",A),MOD(CEILING(rw/(D/(B^cl)),1)-1,B)+1,cl)))),FILTER(E,MMULT(--(E<>""),SEQUENCE(C,,,0))=C))

简而言之,它的作用是:

  • 变量A-D都是辅助变量。
  • 我们的想法是使用简单的INDEX() s来返回所有值。为此,我们需要为行和列都设置正确的索引。
  • MAKEARRAY()将使计算相对容易,因为lambda带来了递归功能。在这些函数中,它的基本数学运算为这些行和列返回正确的索引。事实上,不需要对列进行计算,因为我们简单地引用'cl',并且所有行索引的所有计算都通过MOD(CEILING(rw/(D/(B^cl)),1)-1,B)+1完成。
  • 将上面的结果放入UNIQUE()中,以使用非常少的资源来过滤掉任何潜在的重复将潜在的空行限制为仅一个空行。
  • FILTER()MMULT()可以很好地协同工作,过滤掉任何不需要的结果(读取;空)。

这是我能做的最紧凑最快的了。公式现在将适用于任何连续的单元格区域。单个单元格、单行、单列或任何二维范围。

选项二:

OP正确地提到,选项1可能会在开始时创建太多的元组,以至于后来才丢弃它们。这可能是低效的。为了解决这个问题(如果这不是你想要的),我们可以使用一个更大的公式。让我们想象以下数据:
| 一个|B| C类|
| - -----|- -----|- -----|
| a| d| f|
| B| e的|h的|
| | e的||
| c型||g|
| | | g|
我们看到有空单元格和重复的值。这就是选项1会创建太多元组的原因。为了解决这个问题,我提出了一个更长的公式:

=LET(A,A1:C5,B,ROWS(A),C,COLUMNS(A),D,IF(A="",NA(),A),E,MAKEARRAY(B,C,LAMBDA(rw,cl,INDEX(SORT(INDEX(D,0,cl)),rw))),F,BYCOL(E,LAMBDA(cl,COUNTA(FILTER(cl,NOT(ISERROR(cl)))))),G,MAKEARRAY(PRODUCT(F),C,LAMBDA(rw,cl,INDEX(E,MOD(CEILING(rw/IFERROR(PRODUCT(INDEX(F,SEQUENCE(C-cl,,cl+1))),1),1)-1,INDEX(F,cl))+1,cl))),UNIQUE(G))

要对此进行分解:

  • LET()-使用变量;
  • A-我们初始的全范围单元格(连续);
  • B--A的总行数;
  • C--A的列总数;
  • D-公式IF(A="",NA(),A)用于检查矩阵中的每个值是否为空(字符串)。如果是,则将其设为错误(这将在下一步中有意义)。
  • E-在此步骤中,公式MAKEARRAY(B,C,LAMBDA(rw,cl,INDEX(SORT(INDEX(D,0,cl)),rw)))对每列进行排序,因此值位于顶部,所有错误都被向下推:

| 一个|B| C类|
| - -----|- -----|- -----|
| a| d| f|
| B| e的|g|
| c型|e的|g|
| 大魔王|大魔王|h的|
| 大魔王|大魔王|大魔王|

  • F-此变量的公式BYCOL(E,LAMBDA(cl,COUNTA(FILTER(cl,NOT(ISERROR(cl))))))现在将计算每列的项目数量。这是以后使用和计数所有排列所需要的。在这个特定情况下,结果将是{3;3;4}
  • G-使用MAKEARRAY(PRODUCT(F),C,LAMBDA(rw,cl,INDEX(E,MOD(CEILING(rw/IFERROR(PRODUCT(INDEX(F,SEQUENCE(C-cl,,cl+1))),1),1)-1,INDEX(F,cl))+1,cl)))的最后一个变量(如果选择这样使用它)。它相当长,但每一步都有意义;得到乘积(所有可能的排列)来计算行的总量,列保持不变。在LAMBDA()中,我们引用F变量中当前column-indice之后的所有列。这是一个相当大的块消化,不幸的是,我不足以解释=)。
  • UNIQUE(G)-最后一步是过滤掉所有的双重排列,如果你也选择的话。

结果:

现在,即使选项1在可读性方面优于选项2,但第二个选项(在非常有限的测试中)只花了第一个选项的三分之一的时间来计算。所以从速度上来说,第二种选择是首选。
作为第二个选项的替代方案,我首先有:

=LET(A,A1:C5,B,ROWS(A),C,COLUMNS(A),D,MAKEARRAY(B,C,LAMBDA(rw,cl,IF(MATCH(INDEX(A,rw,cl),INDEX(A,0,cl),0)=rw,INDEX(A,rw,cl),NA()))),E,MAKEARRAY(B,C,LAMBDA(rw,cl,INDEX(SORT(INDEX(D,0,cl)),rw))),F,BYCOL(E,LAMBDA(cl,COUNTA(UNIQUE(FILTER(cl,NOT(ISERROR(cl))))))),G,MAKEARRAY(PRODUCT(F),C,LAMBDA(rw,cl,INDEX(E,MOD(CEILING(rw/IFERROR(PRODUCT(INDEX(F,SEQUENCE(C-cl,,cl+1))),1),1)-1,INDEX(F,cl))+1,cl))),G)

这将把D变量改为一个更长的公式,以预先删除每列中的重复项。这两种变化都能很好地工作。

xmq68pz9

xmq68pz92#

LAMBDA支持的Simple LET

对于2022年的问题,我使用了以下LET:

=LET( matrix, A2:E6,

   cC, COLUMNS( matrix ), cSeq, SEQUENCE( 1, cC ),
   rC, ROWS( matrix ), rSeq, SEQUENCE( rC ),
   eC, rC ^ cC, eSeq, SEQUENCE( eC,,0 ),
   unblank, IF( ISBLANK(matrix), "°|°", matrix ),
   m, UNIQUE( INDEX( unblank, MOD( INT( INT( SEQUENCE( eC, cC, 0 )/cC )/rC^SEQUENCE( 1, cC, cC-1, -1 ) ), rC ) + 1, cSeq ) ),
   FILTER( m, BYROW( IFERROR( FIND( "°|°", m ), 0 ), LAMBDA(x, SUM( x ) ) ) = 0 ) )

其中用于在A2:E6:

此公式插入“°|“°”代替空白,以避免将空白重铸为0。它通过应用UNIQUE来消除重复项。
这是***超级低效***,因为它生成每一个可能的排列,包括重复,然后过滤掉它们。对于一个小规模的矩阵,这不是什么大问题,但是想象一下,一个100行乘3列的矩阵将生成1'000'000行,然后将它们过滤到一个小的子集。
但是,有一个小的好处,*注意黄色单元格中的 f,它被困在列的中间,与它的对等单元格不相邻。这个公式处理得很好。它只是将其集成到有效排列的输出中。这在下面的 * 有效 * 公式中很重要。

LAMBDA支持的高效通用LET

对于一般的LAMBDA公式,我使用:

SYMBOLPERMUTATIONS =
LAMBDA( matrix,

LET(
       cC, COLUMNS( matrix ),
       cSeq, SEQUENCE( 1, cC ),
       symbolCounts, BYCOL( matrix, LAMBDA(x, SUM( --NOT( ISBLANK( x ) ) ) ) ),
       rSeq, SEQUENCE( MAX( symbolCounts )-1 ),
       permFactors, INDEX( SCAN( 1, INDEX( symbolCounts, , cC-cSeq+1), LAMBDA(a,b, a*b ) ),, cC-cSeq+1 ),
       permMods, IFERROR( INDEX( permFactors,, IF( cSeq + 1  > cC, -1, cSeq+1 ) ), 1 ),
       idx, INT( MOD( SEQUENCE( INDEX(permFactors, 1, 1),,0 ), permFactors )/permMods ) + 1,
       answer, INDEX( matrix, idx, cSeq ),
       er, OR( BYCOL( --ISBLANK(matrix), LAMBDA(x, SUM(--(INDEX(x,rSeq+1)<INDEX(x,rSeq))) ) ) ), // detect if there are stranded symbols
       IF( SUM(symbolCounts)=0, "no symbols",
             IF( er, "symbol columns must be contiguous",
                          answer ) ) )

)

这需要矩阵,就像上面的 (LET版本如下所示)。它提供了完全相同的结果,但有显着的差异:

  • 它是高效的。在这里所示的例子中,上面的简单公式将生成5^6 = 15625排列,以得到1440有效排列。这仅生成所需的内容,而不需要过滤。
  • 然而,它根本不处理那个搁浅的***f***。事实上,它会在***f***的位置生成一个0,这是一个拥有大量排列的用户甚至不会注意到的东西。

因此,存在错误检测和处理。变量er使用LAMBDA Helper检测矩阵中是否有任何与其上方的符号不按列连续的搁浅符号:

OR( BYCOL( --ISBLANK(matrix), LAMBDA(x, SUM(--(INDEX(x,rSeq+1)<INDEX(x,rSeq))) ) ) )

这是一个新的挑战,也可能是一个新的问题。在搁浅的***f***的情况下,有人能想出一种方法来合并它吗?

  • 另一个错误陷阱是检测列是否完全为空。*

LAMBDA Magic

LAMBDA带来的魔力来自这一行,它使两个公式都可以扩展到任何数量的列,而不必对它们进行硬编码:

permFactors, INDEX( SCAN( 1, INDEX( symbolCounts, , cC-cSeq+1), LAMBDA(a,b, a*b ) ),, cC-cSeq+1 )

我从JvdV's的答案中得到了这个答案,它专门针对试图解决这个问题。Scott CranerBosco Yip已经表明,如果没有LAMBDA,它基本上是无法解决的,JvdV展示了如何使用LAMBDA助手SCAN来完成。

有效公式的LET版本

=LET( matrix, A2:F6,
   cC, COLUMNS( matrix ),
   cSeq, SEQUENCE( 1, cC ),
   symbolCounts, BYCOL( matrix, LAMBDA(x, SUM( --NOT( ISBLANK( x ) ) ) ) ),
   rSeq, SEQUENCE( MAX( symbolCounts )-1 ),
   permFactors, INDEX( SCAN( 1, INDEX( symbolCounts, , cC-cSeq+1), LAMBDA(a,b, a*b ) ),, cC-cSeq+1 ),
   permMods, IFERROR( INDEX( permFactors,, IF( cSeq + 1  > cC, -1, cSeq+1 ) ), 1 ),
   idx, INT( MOD( SEQUENCE( INDEX(permFactors, 1, 1),,0 ), permFactors )/permMods ) + 1,
   answer, INDEX( matrix, idx, cSeq ),
   er, OR( BYCOL( --ISBLANK(matrix), LAMBDA(x, SUM(--(INDEX(x,rSeq+1)<INDEX(x,rSeq))) ) ) ),
   IF( SUM(symbolCounts)=0, "no symbols",
         IF( er, "symbol columns must be contiguous",
                      answer ) ) )
7uhlpewt

7uhlpewt3#

可能是 iterativeLAMBDA

MyLambda在名称管理器中定义为:

=LAMBDA(n,
    LET(
        Rng, Sheet1!$A$1:$D$3,
        α, COLUMNS(Rng),
        β, INDEX(Rng, , n),
        γ, FILTER(β, β <> ""),
        IF( n > α,
            "",
            LET(δ, γ & REPT(CHAR(32), 25) & TRANSPOSE(MyLambda(n + 1)),
                ε, COLUMNS(δ),
                ζ, SEQUENCE(ROWS(δ) * ε, , 0),
                INDEX(δ, 1 + QUOTIENT(ζ, ε), 1 + MOD(ζ, ε))
               )
          )
        )
    )

之后我们就可以调用
=LET(Rng,Sheet1!$A$1:$D$3,α,COLUMNS(Rng),TRIM(MID(MyLambda(1),25*SEQUENCE(,α,0)+1,25)))
在工作表中。
请注意,Rng 中的每一列必须至少包含一个条目
LAMBDA背后的逻辑是 * 迭代地 *(通过 MyLambda 中对MyLambda(n+1)的调用)连接范围内的连续列,基于两个 * 正交 * 数组的连接生成一个二维数组,该数组包含这两个数组元素的所有排列。
={"Dave";"Mike"}&TRANSPOSE({"Bob";"Steve";"Paul"})
例如,生成
{"DaveBob","DaveSteve","DavePaul";"MikeBob","MikeSteve","MikePaul"}
然后需要将其“重定尺寸”为一维水平阵列,即,
{"DaveBob";"DaveSteve";"DavePaul";"MikeBob";"MikeSteve";"MikePaul"}
使得它可以与该范围内的下一个(垂直的、正交的)列迭代地连接。等等。
我们有效地:

unpivoting:
  {"Bob","Steve","Paul";"Bob","Steve","Paul";"Bob","Steve","Paul"}
by:
  {"Dave";"Mike"}

因此,输入数组 n 是:
| 一个|B|
| - -----|- -----|
| 戴夫|鲍勃|
| 迈克|史蒂夫|
| | 保罗|
列B被转置和复制:
| 一个|B| C类|D|
| - -----|- -----|- -----|- -----|
| 戴夫|鲍勃|史蒂夫|保罗|
| 迈克|鲍勃|史蒂夫|保罗|
现在我们通过列A对列B、C和D进行UNPIVOT(一种展平):
| 一个|B|
| - -----|- -----|
| 戴夫|鲍勃|
| 戴夫|史蒂夫|
| 戴夫|保罗|
| 迈克|鲍勃|
| 迈克|史蒂夫|
| 迈克|保罗|

:此方法:

  • 高效地生成所有排列而不重复
  • 忽略空格,并且不要求符号位于相邻行

第二个函数在工作表中调用,然后将串联的数组解析为所需的行数/列数。

lb3vh1jj

lb3vh1jj4#

我以前的回答有问题(S):

Simple公式忽略空格,并且不要求符号位于同一列中的相邻行,但效率低下。如果给定一个数组,A列有100行,B列有3行,C列有2行,结果应该是100 x 3 x 2 = 600,但它会生成100 x 100 x 100 = 1'000'000,然后过滤它们。这需要2分钟以上的时间来计算!如果输入数组中的任何列完全为空,它也会爆炸。
Efficient General LET速度快,不会浪费计算,但它不能处理非相邻行上的符号输入( 搁浅的f
),并且如果任何列完全空白,也会爆炸。

来自Jos Woolley解决方案的见解

Jos Woolley的解决方案提供了两个见解,允许一个有效的解决方案,不存在我以前的任何一种方法的问题:

  • 目标实际上是分解每一列,并通过下一列连续取消透视
  • 这个想法可以通过LAMBDA递归实现
递归LAMBDA解决方案

有了这些见解,我能够提出一个有效的LAMBDA解决方案。以下内容已完全注解,以便您可以将其直接粘贴到高级公式编辑器中:

PERMUTATEARRAY = 
 /* This function recursively generates all unique permutations of an ordered array of symbols.
    It is efficient - i.e., it does not generate duplicate rows that require filtering.
    The arguments are:
        symbolArray - ia a required array input (unless the recursion is done).
            Is an array of ordered symbols where the left most column contains symbols that will be permutated
            by the permutation of the symbols in the next columns to the right. e.g.,
                with symbolArray of:
                    A   1
                    B   2
                A and B will be permutated by 1 and 2:
                    A   1
                    A   2
                    B   1
                    B   2
        byArray - optional array that will be used to permutate the next column of the symbolArray.
            This is passed between recursions and it not intended for use by the user but it can be used.
        cleaned - optional argument to indicate that the symbolArray has already been cleaned.
            This prevents the function from repeatedly cleaning the symbolArray that would otherwise require
            a repetition for each column of the symbolArray. It is passed between recursions and it not
            intended for use by the user but can be used.
    Example - With a symbolArray of:
        A   C   1
        B   D   2
        The output would be the following array:
        A   C   1
        A   C   2
        A   D   1
        A   D   2
        B   C   1
        B   C   2
        B   D   1
        B   D   2
    NOTES:
     -  Blanks will be ignored.
     -  errors will be ignored. (see comments below to change this)
     -  all rows of the resulting array will be unique
     -  blank columns in the symbol array are removed
     -  this function has no dependencies on external LAMBDA functions even though that would make it more
        readable.
 ------------------------------------  */

LAMBDA( symbolArray, [byArray], [cleaned],
    IF( AND(ISOMITTED(symbolArray),ISOMITTED(byArray)), ERROR.TYPE(7), // DONE
        IF(ISOMITTED(symbolArray), byArray, // if there is no symbolArray, the function is DONE.
            LET(                
                clnSymArray, IF(ISOMITTED(cleaned), //If the symbol array has not been cleaned, then clean it.
                /* Only clean arrays can be permuated. They cannot contain blanks because those are interpreted as 0's.
                   The input also cannot contain entirely blank columns, so these are removed.
                   The input cells also cannot contain errors as this will cause the whole function to error. That,
                   however, is a design choice. They are filtered out inside of this function because the user cannot
                   easily filter them out before passing them as arguments - IFERROR(x,"") causes all blanks to become 0's.
                */
                            LET(
                                COMPRESSC, LAMBDA( a, 
                                        FILTER(a,BYCOL(a,LAMBDA(a,SUM(--(a<>""))>0)))
                                        ),
                                REPLBLANKS, LAMBDA(array, [with], 
                                        LET(w, IF(ISOMITTED(with), "", with),
                                            IF(array = "", w, array)
                                            )
                                        ),
                                REPLERRORS, LAMBDA(array, [with], 
                                        LET(w, IF(ISOMITTED(with), "", with),
                                            IFERROR(array, w)
                                            )
                                        ),
                                COMPRESSC( REPLERRORS( REPLBLANKS(symbolArray) ) )
                                // COMPRESSC( REPLBLANKS(symbolArray) )  //removes the REPLERRORS if the user wants errors to result in erroring the function.
                                    ),
                            symbolArray ), //otherwise, pass the symbolArray
                
                //Once cleaned, effectively execute PERMUTATEARRAY( clnSymArray, byArray, 1 )
                IF( AND(COLUMNS( clnSymArray ) = 1, ISOMITTED( byArray ) ),
                    UNIQUE(FILTER(clnSymArray,clnSymArray<>"")), /* if the user gives a single column, give it back clean even if it was already cleaned.
                                                                    there is no point in testing again whether Clean has been set. DONE */
                    /* Otherwise, we can recursively process the inputs in the following LET. */
                    LET(
                        // MUX is an internal LAMBDA function that permutates the left most column of the p array by the b (by) array.
                            MUX, LAMBDA( p, b, 
                                    LET(pR, ROWS( p ),
                                        byR, ROWS( b ),
                                        byC, COLUMNS( b ),
                                        byCseq, SEQUENCE(,byC+1), // forces this to look at only one column of p
                                        oRSeq, SEQUENCE( byR * pR,,0 ),
                                        IFERROR( INDEX( b, oRSeq/pR+1, byCseq),
                                                INDEX( p, MOD(oRSeq,pR )+1, byCseq-byC ) )
                                        )
                                    ),
                            pRSeq, SEQUENCE(ROWS(clnSymArray)),
                        // Decide when to apply MUX versus when to recurse. MUX is always the final output.
                            // if there are only two symbol columns with no byArray, filter & MUX the two columns - DONE
                            IF( AND(COLUMNS( clnSymArray ) = 2, ISOMITTED( ByArray ) ),
                                LET(pFin, INDEX( clnSymArray, pRSeq, 2),
                                    fpFin, UNIQUE(FILTER(pFin,pFin<>"")),
                                    bFin, INDEX( clnSymArray, pRSeq, 1),
                                    fbFin, UNIQUE(FILTER(bFin,bFin<>"")),
                                    MUX( fpFin, fbFin )
                                    ),
                                // if there are more than two symbol columns with no byArray, repartition the symbol and byArray and recurse 
                                IF( AND(COLUMNS( clnSymArray ) > 2, ISOMITTED( ByArray ) ), 
                                    LET(pC, COLUMNS(clnSymArray),
                                        pCSeq, SEQUENCE(,pC-2,3),
                                        pNext, INDEX( clnSymArray, pRSeq, pCSeq ),
                                        pFin, INDEX( clnSymArray, pRSeq, 2),
                                        fpFin, UNIQUE(FILTER(pFin,pFin<>"")),
                                        bFin, INDEX( clnSymArray, pRSeq, 1),
                                        fbFin, UNIQUE(FILTER(bFin,bFin<>"")),
                                        bNext, MUX( fpFin, fbFin ),
                                        PERMUTATEARRAY( pNext, bNext, 1 )
                                        ) ,
                                    // if there is more than one symbol column and a byArray, repartition the symbol and byArray and recurse
                                    IF( AND(COLUMNS( clnSymArray ) > 1, NOT( ISOMITTED( ByArray ) ) ),
                                        LET(pC, COLUMNS(clnSymArray),
                                            pCSeq, SEQUENCE(,pC-1,2),
                                            pNext, INDEX( clnSymArray, pRSeq, pCSeq ),
                                            pFin, INDEX( clnSymArray, pRSeq, 1),
                                            fpFin, UNIQUE(FILTER(pFin,pFin<>"")),
                                            bNext, MUX( fpFin, ByArray ),
                                            PERMUTATEARRAY( pNext, bNext, 1 )
                                                ),
                                        // if there is only one symbol column and a byArray, filter symbol column & MUX it with he byArray - DONE
                                        IF( AND(COLUMNS( clnSymArray ) = 1, NOT( ISOMITTED( ByArray ) ) ),
                                            LET(pFin, INDEX( clnSymArray, pRSeq, 1),
                                                fpFin, UNIQUE(FILTER(pFin,pFin<>"")),
                                                MUX( fpFin, ByArray )
                                                )
                                )
                            ) ) )
                        ) ) )
            )
        )

);

即使您删除了注解,这里也有很多代码,但这样设计是为了在防止错误的同时最大限度地提高速度。它也是一个完全包含的LAMBDA,这意味着它不需要加载任何其他LAMBDA函数即可工作。
它能够处理来自同一列的非相邻行中的符号 (搁浅的f问题),完全空白的列和输入数组中的错误。下面是一个具有所有这些问题的示例:

LAMBDA Magic --> Turing Complete

在我之前的回答中,我可以看到LAMBDA允许我们现在有一个可扩展的解决方案来解决这个问题,这要归功于LAMBDA助手的使用,就像JvdV使用SCAN(array,LAMBDA(a,b,a*b))运行排列所展示的那样。
现在,LAMBDA允许递归和循环,这对于这个特定的问题来说甚至更强大。如果没有Jos的洞察力,我就不会认识到有一个重复的递归模式。这个新的解决方案可能很长,但它以一种计算效率更高的方式解决了这个问题,这将防止电子表格变得不必要的滞后。
缺点是,调试递归是一个巨大的痛苦!

w1e3prcc

w1e3prcc5#

这个方法怎么样:

=LET(a,  A1:E3,
     b,  UNIQUE(TAKE(a,,1)),
     r,  REDUCE(b,  SEQUENCE(1,COLUMNS(a)-1,2),
         LAMBDA(x,  y,
                TOCOL(x & "," & TOROW(UNIQUE(INDEX(a,,y)))))),
DROP(REDUCE("",r,
     LAMBDA(x,  y,
            VSTACK(x,
                   TEXTSPLIT(y,",")))),
     1))

它从第一列中的(唯一)值开始,将这些值中的每个值与第二列中的每个唯一值连接起来。结果被存储,并且该存储的值中的每一个与下一列的每个唯一值联接,等等。最后,这是textsplitted得到逗号分隔的单元格值,以分隔列再次。

相关问题