下面是 5 列 - 第 6 列包含所需的结果:5 列中的名称在每个可能的排列中。
两行只有两列有值 - 所以第 6 列中只有两个排列(由“;”分隔)。
一行有 4 个值,所以有 24 个排列。
对不起图片,我无法弄清楚如何将表格从 Excel 粘贴到 Stack 而不必重写它。
真实数据集中的行可以有 1 个值、5 个值或介于两者之间的任何值。
=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))
我用谷歌搜索过的大多数解决方案在 1 行 5 列的简单数组上似乎也不起作用(或者我无法使它们起作用)。我试着从头开始做,并且生成了一个仅包含数字 1-5 且没有重复的数字列表 -
=LET(firstperm,VALUE(CONCAT(SEQUENCE(1,COLUMNS(Tablestu[@[First Name]:[Preferred Last Name]])))),lastperm,VALUE(CONCAT(SORT(SEQUENCE(1,COLUMNS(Tablestu[@[First Name]:[Preferred Last Name]])),,-1,TRUE))),diff,(lastperm-firstperm)+1,list,SEQUENCE(diff,1,firstperm),wanted,(IF((ISNUMBER(SEARCH("1",list))*ISNUMBER(SEARCH("2",list))*ISNUMBER(SEARCH("3",list))*ISNUMBER(SEARCH("4",list))*ISNUMBER(SEARCH("5",list))),list,"")),FILTER(wanted,wanted<>"",""))
我想我可以以某种方式拆分这 5 位数字,并使用 INDEX 公式按该顺序返回单词。 IE。 31452 会返回第三个单词,然后是第一个,然后是第四个,依此类推......但我仍然需要一百万次键盘敲击才能将它变成我需要的东西,而且它会非常低效我什至到了那里。链接的答案似乎是正确的 - 我正在使用 Excel 365,我需要一个使用公式的解决方案,而不是 VBA 或 power query。
LAMDA 和 LET 都很好。
警告:不建议将其作为通用解决方案,但由于 OP 给出的条目数上限为 5,因此可能值得考虑。
=LET(
ζ,A2:E2,
ξ,COUNTA(ζ),
κ,SEQUENCE(,ξ),
λ,LEFT(12345,ξ),
γ,REPT(1+ξ,ξ)-λ,
δ,SEQUENCE(γ-λ+1,,λ),
α,INDEX(FILTER(ζ,ζ<>""),
MID(FILTER(δ,MMULT(N(ISERR(FIND(MID(λ,κ,1),δ))),TOCOL(κ))=0),κ,1)
),
TEXTJOIN(";",,BYROW(α,LAMBDA(β,CONCAT(β))))
)
=LET(range, A1:E1,
f, FILTER(range,range<>""),
c, COLUMNS(f),
s, c^c,
a, MAKEARRAY( s, c,
LAMBDA( rw, cl,
MOD(CEILING(rw/(s/(c^cl)),1)-1,c)+1)),
p, FILTER(a,
BYROW( a,
LAMBDA( x,
AND(MMULT(N(TRANSPOSE(x)=x),SEQUENCE(c)^0)=1)))),
TEXTJOIN(";",,UNIQUE(BYROW(p,LAMBDA(x,TEXTJOIN("",1,CHOOSECOLS(f,x)))))))
这也考虑到如果在范围内使用重复的名称,它不会产生重复的排列。
=LET(
ζ,A2:E2,
ξ,FILTER(ζ,ζ<>""),
TEXTJOIN(";",,
SORT(UNIQUE(BYROW(RANDARRAY(2^10,COLUMNS(ξ)),LAMBDA(γ,CONCAT(SORTBY(ξ,γ))))))
)
)
只要RANDARRAY
的第一个参数选择得足够大,结果数组不唯一的概率就会小到几乎可以保证正确的输出。在这里,
2^10
似乎足以容纳最多 5 个非空白条目。同样,此设置不容易扩展到超过 5 个非空白条目,但由于其简洁性和/或性能可能值得考虑。
Script Lab,它是一个 Microsoft Garage Project 和它的多平台我用 Excel 版本测试过,但您可以尝试使用 Excel Desktop。
关于用于排列的 javascript 算法,我从问题中改编了其中两个:JavaScript 中的排列?.
改编我的意思是:
arr
) 从二维数组转换为一维数组。 javascript 问题中的所有算法都采用一维数组,但是当我们调用接收 Excel 范围的 javascript 函数时,该范围表示为二维数组 (
[][]
),因此我们使用内置数组
flat()
在调用算法之前进行转换的方法。
/**
* Generates all permutation without repetition of the input argument
* @customfunction
* @param {any[][]} arr Array of input values
* @returns {any[][]} 2D array with all permutations.
*/
function permute(arr) {
if (Array.isArray(arr[0])) {
arr = arr.flat();
}
function recur(a) {
if (!a.length) return [[]];
return a.flatMap((x, i) => {
return recur(a.filter((v, j) => i !== j)).map((vs) => [x, ...vs]);
});
}
return recur(arr);
}
来自@le_m:更大,但最有效的
/**
* Generates all permutation without repeatiton of the input argument
* @customfunction
* @param {any[][]} arr Array of input values
* @returns {any[][]} 2D array with all permutations.
*/
function permute_eff(arr) {
if (Array.isArray(arr[0])) {
arr = arr.flat();
}
var length = arr.length,
result = [arr.slice()],
c = new Array(length).fill(0),
i = 1,
k,
p;
while (i < length) {
if (c[i] < i) {
k = i % 2 && c[i];
p = arr[i];
arr[i] = arr[k];
arr[k] = p;
++c[i];
i = 1;
result.push(arr.slice());
} else {
c[i] = 0;
++i;
}
}
return result;
}
有了 javascript 函数,下一步就是从 Excel 中调用它。感谢 Script Lab,这是一项简单的任务,我创建了一个新的 Snippet,我将其命名为lib
。然后只是调用它。例如:为行/列数组输入以及数字或文本数据类型定义的函数按预期工作。我测试了这两个函数以找到
SEQUENCE(8)
的所有排列,即超过
40K
排列并在不到2秒内返回结果(两个javascript公式)。现在回到最初的问题,它产生了一个非常简单的公式,然后将其向下拖动:
=TEXTJOIN(";",,BYROW(SCRIPTLAB.LIB.PERMUTE(FILTER(A2:E2,A2:E2<>"")),
LAMBDA(x, CONCAT(x))))
这里是输出:
此解决方案唯一的不便之处在于,您似乎无法在数组辅助函数(例如BYROW
)中调用自定义函数。例如以下不起作用:
=BYROW(A2:E4, LAMBDA(x, TEXTJOIN(";",,BYROW(SCRIPTLAB.LIB.PERMUTE(FILTER(x,x<>"")),
LAMBDA(y, CONCAT(y))))))
所以你不能生成一个会溢出整个结果的数组版本。您收到以下错误:
希望对您有所帮助,我认为在使用 excel 函数无法提供良好性能或冻结的情况下,这是一个很好的解决方法。