람다를 사용하여 Excel에서 모든 순열 생성
Excel에서 모든 순열을 생성하려면 어떻게 해야 하는지에 대한 자주 묻는 질문과 대답입니다.
2011 2016 2017 2017년 슈퍼유저 2018 2021
현재 2022년에는 중복으로 마감되기 전에 답변을 얻지 못했는데, 이는 유감스러운 일입니다. 왜냐하면 LAMD는 이 질문에 대한 답변 방식을 정말로 바꾸기 때문입니다.
저는 종종 같은 요구를 받아왔고 복잡한 바퀴를 다시 만들어야 해서 좌절했습니다.그래서 저는 질문을 다시 제기하고 아래에 제 자신의 답을 넣을 것입니다.어떤 제출물도 답으로 표시하지 않고 좋은 아이디어를 초대하겠습니다.저는 제 자신의 접근 방식이 개선될 수 있다고 확신합니다.
2022년 질문 다시 작성하기
저는 공식만으로 엑셀에서 루프를 만들려고 합니다.제가 이루고자 하는 것은 아래와 같습니다.입력으로 (i) 국가, (ii) 변수 및 (iii) 연도의 세 열이 있다고 가정합니다.이 입력값을 확장하여 이 매개변수에 값을 할당하고자 합니다.
입력:
나라 | 변수 | 연도 |
---|---|---|
GB | 국내총생산 | 2015 |
DE | 지역 | 2016 |
CH | 지역 | 2015 |
출력:
나라 | 변수 | 연도 |
---|---|---|
GB | 국내총생산 | 2015 |
GB | 국내총생산 | 2016 |
GB | 지역 | 2015 |
GB | 지역 | 2016 |
DE | 국내총생산 | 2015 |
DE | 국내총생산 | 2016 |
DE | 지역 | 2015 |
DE | 지역 | 2016 |
엑셀을 이용해서 어떻게 하면 효율적으로 할 수 있을까요?
2018 문제 확장하기
저는 3개의 열을 가지고 있는데, 각 열에는 아래와 같이 다른 종류의 마스터 데이터가 있습니다.
이제, 이 세 세포의 가능한 모든 조합을 갖고 싶습니다. 예를 들면,
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 |
---|---|---|---|---|---|
A | F | K | P | U | 1 |
B | G | L | Q | V | 2 |
C | H | R | W | 3 | |
D | X | 4 | |||
E |
1'440개의 순열이 발생합니다
L1 | L2 | L3 | L4 | L5 | L6 |
---|---|---|---|---|---|
A | F | K | P | U | 1 |
A | F | K | P | U | 2 |
A | F | K | P | U | 3 |
A | F | K | P | U | 4 |
A | F | K | P | V | 1 |
A | F | K | P | V | 2 |
A | F | K | P | V | 3 |
A | F | K | P | V | 4 |
A | F | K | P | W | 1 |
... | ... | ... | ... | ... | ... |
수준(열)의 수를 모두 포함하는 일반 공식을 만드는 것은 어렵습니다.제시된 답변을 검토해 보십시오. 각각의 로켓 과학이 필요했고 지금까지 모든 솔루션은 기호의 열 수에 대해 암호화된 제한이 있었습니다.그렇다면 람다가 일반적인 해결책을 제시할 수 있습니까?
를 했습니다; .MAKEARRAY()
:
옵션 1:
당신이 "초 비효율적"이라고 부르는 것은 행들을 계산할 때 순열 목록을 만드는 것입니다.저는 다음이 그렇게 비효율적이지 않다고 생각합니다.다음을 상상해 보겠습니다.
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()
모든 값을 반환합니다.그러기 위해서는 행과 열 모두에 적합한 지수가 필요합니다. MAKEARRAY()
람다가 가져온 재귀적 함수로 인해 계산이 비교적 쉬워집니다.이러한 함수 내부에서 기본 연산은 이러한 행과 열 모두에 대해 올바른 인덱스를 반환합니다. 'cl을 지칭하기 하지 않고 행 히 'cl'다를 .MOD(CEILING(rw/(D/(B^cl)),1)-1,B)+1
.- 를 .
UNIQUE()
잠재적인 중복을 걸러내고 잠재적인 빈 행을 하나의 빈 행으로만 제한하기 위해 매우 적은 리소스를 사용합니다. FILTER()
그리고.MMULT()
원하지 않는 결과(읽기, 비어 있음)를 걸러내기 위해 잘 협력합니다.
그것은 제가 이것을 얻을 수 있을 정도로 작고 빠릅니다.이제 공식은 연속된 범위의 셀에서 작동합니다.단일 셀, 단일 행, 단일 열 또는 임의의 2D 범위.
옵션 2:
OP는 옵션 1이 처음에 너무 많은 튜플을 만들 수 있고 나중에 폐기할 수도 있다고 정당하게 언급했습니다.이것은 비효율적일 수 있습니다.이 문제를 해결하기 위해서는 훨씬 더 큰 공식을 사용할 수 있습니다.다음 데이터를 상상해 보겠습니다.
A | 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
총 양C
A 총 입니다.D
--IF(A="",NA(),A)
행렬이 비어 있으면 행렬의 각 값을 확인합니다( 문자열).그렇다면 오류로 만듭니다(다음 단계에서 의미가 있음).E
--MAKEARRAY(B,C,LAMBDA(rw,cl,INDEX(SORT(INDEX(D,0,cl)),rw)))
각을 정렬합니다.합니다.
A | 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)))
가 있습니다.총 다소 길지만 각각의 단계는 의미가 있습니다. 제품(가능한 모든 순열)을 가져와 행의 총 양을 계산하고 열은 동일하게 유지합니다.eLAMBDA()
합니다의 열 뒤에 모든 합니다.F
변수. 큰 부족합니다=소화하기에는 꽤 큰 양이지만 안타깝게도 =)에 대한 설명이 부족합니다.UNIQUE(G)
는 한 할 경우 -입니다.
결과:
이제 옵션 1이 가독성에서 옵션 2를 제치긴 했지만, 두 번째 옵션은 (매우 제한적인 테스트로) 첫 번째 옵션을 계산하는 데 걸린 시간의 3분의 1에 불과했습니다.그래서 속도면에서는 이 두 번째 옵션이 더 좋을 것 같습니다.
두 번째 옵션에 대한 대안으로 처음 선택한 것은 다음과 같습니다.
=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
변수는 각 열의 중복을 미리 제거하기 위해 더 긴 공식으로 이동합니다.두 가지 변형 모두 잘 작동할 것입니다.
람다가 지원하는 간단한 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으로 재캐스팅하지 않도록 빈칸 대신 사용합니다.유니크를 적용하여 중복을 제거합니다.
이는 반복을 포함한 모든 가능한 순열을 생성한 다음 필터링하기 때문에 매우 비효율적입니다.소규모 행렬의 경우 큰 문제는 아니지만, 100 행 x 3 열 행렬이 1,000',000 행을 생성한 다음 작은 부분 집합으로 필터링한다고 상상해 보십시오.
그러나 노란색 셀의 f가 열 중간에 묶여 있고 다른 셀과 인접하지 않는 것에 주목하는 작은 이점이 하나 있습니다.이 공식은 그것을 챔피언처럼 처리합니다.그것은 단지 그것을 유효한 순열의 출력에 통합할 뿐입니다.그것은 아래의 효율적인 공식에서 중요할 것입니다.
람다가 지원하는 효율적인 일반 LET
일반적인 람다 공식은 다음을 사용했습니다.
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 대신 0을 생성할 것이고, 이것은 순열이 많은 사용자가 알아챌 수 있는 것이 아닙니다.
이러한 이유로 오류 감지 및 처리가 있습니다.변수는 이 람다 도우미를 사용하여 행렬에 열 방향으로 연속되지 않는 고립된 기호가 있는지 감지합니다.
OR( BYCOL( --ISBLANK(matrix), LAMBDA(x, SUM(--(INDEX(x,rSeq+1)<INDEX(x,rSeq))) ) ) )
하지만 이것은 새로운 도전을 만들고 어쩌면 완전히 새로운 질문을 만들어냅니다.좌초된 f의 경우, 누구라도 그것을 통합할 수 있는 방법을 생각해 낼 수 있습니까?
다른 오류 트랩은 열이 완전히 비어 있는지 탐지하는 것입니다.
람다 매직
LAMDA가 제공하는 마법은 두 공식을 하드 코드화할 필요 없이 모든 수의 열로 확장할 수 있도록 하는 이 한 줄에서 비롯됩니다.
permFactors, INDEX( SCAN( 1, INDEX( symbolCounts, , cC-cSeq+1), LAMBDA(a,b, a*b ) ),, cC-cSeq+1 )
저는 이 질문을 해결하기 위해 특별히 시도한 이 질문에 대한 JvdV의 답변에서 이를 얻었습니다.Scott Craner & Bosco Yip은 기본적으로 LAMD 없이는 해결할 수 없다는 것을 보여주었고, JvdV는 LAMD 도우미를 통해 해결할 수 있는 방법을 보여주었습니다.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 ) ) )
어쩌면 반복적으로 LAMBDA
:
My Lambda는 Name Manager에서 다음과 같이 정의했습니다.
=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(n+1)
MyLambda)는 두 직교 배열의 연결이 그 두 배열의 요소의 모든 순열을 포함하는 2차원 배열을 생성한다는 사실에 기초하여 범위 내의 연속적인 열을 연결합니다.
={"Dave";"Mike"}&TRANSPOSE({"Bob";"Steve";"Paul"})
예를 들어, 생성
{"DaveBob","DaveSteve","DavePaul";"MikeBob","MikeSteve","MikePaul"}
그런 다음 1차원 수평 배열로 재차원화해야 합니다.
{"DaveBob";"DaveSteve";"DavePaul";"MikeBob";"MikeSteve";"MikePaul"}
범위 내에서 다음(vertical, 직교) 열과 반복적으로 연결할 수 있도록 합니다.뭐 이런 거.
우리는 효과적으로 다음과 같습니다.
unpivoting: {"Bob","Steve","Paul";"Bob","Steve","Paul";"Bob","Steve","Paul"} by: {"Dave";"Mike"}
따라서 입력 배열 n은 다음과 같습니다.
A | B |
---|---|
데이브 | 밥. |
마이크 | 스티브 |
폴. |
열 B가 전치되어 복제됩니다.
A | B | C | D |
---|---|---|---|
데이브 | 밥. | 스티브 | 폴. |
마이크 | 밥. | 스티브 | 폴. |
이제 우리는 (일종의 평탄화) 열 B, C & D 열 A:
A | B |
---|---|
데이브 | 밥. |
데이브 | 스티브 |
데이브 | 폴. |
마이크 | 밥. |
마이크 | 스티브 |
마이크 | 폴. |
참고: 이 접근 방식:
- 모든 순열을 반복하지 않고 효율적으로 생성
- 공백을 무시하고 기호가 인접한 행에 있어야 할 필요가 없습니다.
워크시트 내에서 호출된 두 번째 함수는 연결된 배열을 필요한 수의 행/열로 구문 분석합니다.
- 단순 공식은 공백을 무시하고 기호가 같은 열의 인접한 행에 있어야 하는 것은 아니지만 비효율적입니다.열 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의 솔루션은 두 가지 통찰력을 제공하여 이전 접근 방식의 문제가 없는 효율적인 솔루션을 제공합니다.
- 목표는 실제로 각 열을 분할하고 다음 열까지 연속적으로 피벗을 해제하는 것입니다.
- 이 아이디어는 람다 재귀를 통해 구현될 수 있습니다.
재귀 람다 용액
이러한 통찰력을 통해 효율적인 람다 솔루션을 제안할 수 있습니다.다음은 Advanced Formula Editor에 직접 붙여넣을 수 있도록 완전히 설명되어 있습니다.
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 )
)
)
) ) )
) ) )
)
)
);
댓글을 없애도 코드가 많이 들어가는데, 오류를 방지하면서 속도를 극대화하기 위해 그렇게 설계됐습니다.또한 완전히 포함된 람다이므로 작동을 위해 다른 람다 기능을 로드할 필요가 없습니다.
동일한 열(연동된 f 문제)의 인접하지 않은 행의 기호, 완전히 빈 열 및 입력 배열의 오류를 처리할 수 있습니다.다음은 이러한 모든 문제를 가지고 있는 예입니다.
람다 마법 --> 튜링 완료
이전 답변에서, JvdV가 다음을 사용하여 실행 순열로 보여주었듯이, 람다 헬퍼를 사용한 덕분에 람다가 이 문제에 대한 확장 가능한 솔루션을 제공할 수 있다는 것을 알 수 있었습니다.SCAN(array,LAMBDA(a,b,a*b))
.
이제 람다는 이 특정 문제에 대해 훨씬 더 강력한 재귀와 루프를 허용합니다.Jos의 통찰력이 없었다면, 반복되는 재귀적 패턴이 있다는 것을 인지하지 못했을 것입니다.이 새로운 솔루션은 시간이 오래 걸릴 수도 있지만 스프레드시트가 불필요하게 지연되는 것을 방지하는 계산 효율적인 방법으로 문제를 해결합니다.
단점으로, 디버깅 재귀는 큰 고통입니다!
이러한 접근 방식은 어떻습니까?
=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))
첫 번째 열의 (고유) 값으로 시작하여 두 번째 열의 각 고유 값과 결합합니다.결과가 저장되고 이렇게 저장된 각 값은 다음 열의 각 고유 값과 결합됩니다.마지막으로, 쉼표로 구분된 셀 값을 다시 구분된 열로 가져오기 위해 텍스트를 분할합니다.
EDIT: 순열 목록의 공백 열을 처리하고 상위 문자와 하위 문자를 고유 문자로 처리하기 위해 다음을 사용할 수 있습니다.
=LET(a, A1:C5,
b, TAKE(a,,1),
c, SEQUENCE(ROWS(b)),
d, FILTER(b,MMULT((EXACT(TOROW(b),b))*(TOROW(c)<=c),c^0)=1),
r, REDUCE(d, SEQUENCE(1,COLUMNS(a)-1,2),
LAMBDA(x,y,
LET(e, INDEX(a,,y),
f, SEQUENCE(ROWS(e)),
g, FILTER(e,MMULT((EXACT(TOROW(e),e))*(TOROW(f)<=f),f^0)=1),
IFERROR(TOCOL(x & "," & TOROW(IF(1/(g<>""),g),3)),x)))),
DROP(REDUCE("",r,
LAMBDA(x,y,
VSTACK(x,
TEXTSPLIT(y,",")))),
1))
이는 기본적으로 이전 버전과 동일하게 수행되지만, 먼저 서로 닮지 않는 값/ 문자열의 배열을 생성하고(이 경우 상위/하위는 닮지 않음), 피벗된 새 배열을 시작/이전 결과 배열에 스택합니다.따라서 새 배열의 각 피벗된 문자열이 쉼표 구분 기호로 구분된 이전 결과에서 각 문자열에 추가됩니다. TCOL은 모든 결과가 쉼표로 구분된 순열을 나열하는 단일 열로 평탄화되도록 합니다.
마지막으로, TEASSPLIT이 range input과 올바르게 동작하지 않기 때문에, 이전 결과를 열로 분할하기 위해서는 람다가 하나 더 필요합니다.
우리는 또한 사용할 수 있습니다.TEXTSPLIT(TEXTJOIN(";",,r),",",";"))
줄이지 않고 순열이 많으면 문자 결합의 문자 한계에 쉽게 도달할 것 같습니다.
언급URL : https://stackoverflow.com/questions/71188880/generate-all-permutations-in-excel-using-lambda
'programing' 카테고리의 다른 글
'on clause'에 알 수 없는 열(열 별칭이 오류를 일으키는 것 같습니다.) (0) | 2023.10.01 |
---|---|
AngularJS : 필터를 비동기적으로 초기화합니다. (0) | 2023.10.01 |
오라클 11g & jdk 1.6과 함께 사용할 jdbc jar 및 db 자체에 연결하는 방법 (0) | 2023.10.01 |
각도가 올바르게 로드되었는지 확인하는 방법 (0) | 2023.10.01 |
C를 아는 것이 실제로 당신이 더 높은 수준의 언어로 쓰는 코드를 해칠 수 있습니까? (0) | 2023.10.01 |