繁体   English   中英

如何列出所有排列而不重复?

[英]How to list all permutations without repetition?

当前帖子是此链接帖子的后续问题:

将一副 7 张假设的交易卡洗牌,并在 Google 表格中列出他们可以进来的订单

惊喜。 我的问题孩子实际上是为真女神转生 3 排列 - 为一个视频游戏。 我认为把它放在交易卡的术语中对外行来说更有意义。 对不起。

大排列......现在处理它!

好的。 我弄乱了一个包含 7 个独特怪物的数据集(在视频游戏早期很容易获得的初始集)。 它们不能在我的派对中复制,但可以组合成不同的。 一开始,视频游戏中只有 8 个可用于怪物的插槽。

该项目的重点是构建“融合链”的所有排列,试图将这些怪物排列成独特的顺序,以便稍后在该链中组合。

它从 A+B 开始,然后清理该列表以从初始配对中消除任何 B+A 场景(融合 A+B 或 B+A 会产生相同的结果)。 然后,融合只是将 C、D、E、F、G 和 H(当前已损坏)添加到先前融合的结果,直到不再存在可能的融合(我的队伍中只有一个怪物)。

问题是这样的:排列单元格中的查询或其他函数在尝试列出一次排序 8 个怪物的排列时抛出错误“结果数组太大”——甚至在融合发生之前。 我已将问题隔离到这个公式(有点长):

=iferror(if(counta($A$2:$A$13)>=2,arrayformula(query(query(split(flatten(flatten(flatten(flatten(flatten(flatten(
filter($F$2:$F,$F$2:$F<>"")&if(counta($A$2:$A$13)>=3,","&transpose(
filter($A$2:$A$13,$A$2:$A$13<>"")),""))&if(counta($A$2:$A$13)>=4,","&transpose(
filter($A$2:$A$13,$A$2:$A$13<>"")),""))&if(counta($A$2:$A$13)>=5,","&transpose(
filter($A$2:$A$13,$A$2:$A$13<>"")),""))&if(counta($A$2:$A$13)>=6,","&transpose(
filter($A$2:$A$13,$A$2:$A$13<>"")),""))&if(counta($A$2:$A$13)>=7,","&transpose(
filter($A$2:$A$13,$A$2:$A$13<>"")),""))&if(counta($A$2:$A$13)>=8,","&transpose(
filter($A$2:$A$13,$A$2:$A$13<>"")),"")),","),
"where Col1 <> Col2"&
if(counta($A$2:$A$13)>=3," and Col1 <> Col3 and Col2 <> Col3"&
if(counta($A$2:$A$13)>=4," and Col1 <> Col4 and Col2 <> Col4 and Col3 <> Col4"&
if(counta($A$2:$A$13)>=5," and Col1 <> Col5 and Col2 <> Col5 and Col3 <> Col5 and Col4 <> Col5"&
if(counta($A$2:$A$13)>=6," and Col1 <> Col6 and Col2 <> Col6 and Col3 <> Col6 and Col4 <> Col6 and Col5 <> Col6"&
if(counta($A$2:$A$13)>=7," and Col1 <> Col7 and Col2 <> Col7 and Col3 <> Col7 and Col4 <> Col7 and Col5 <> Col7 and Col6 <> Col7"&
if(counta($A$2:$A$13)>=8," and Col1 <> Col8 and Col2 <> Col8 and Col3 <> Col8 and Col4 <> Col8 and Col5 <> Col8 and Col6 <> Col8 and Col7 <> Col8",),),),),),),0),"where Col1 <>''",0)),"not enough data"),)

该公式查看的第一个范围是以前稳定的形式(F 列):

唯一的初始化对
小精灵,式神
儿玉,小精灵
花婆,小精灵
Datsue-Ba,Pixie
天使,小精灵
Fomorian,小精灵
儿玉式神
花坡,式神
达江场式神
天使,式神
佛摩人,式神
花宝,儿玉
达江场,儿玉
天使,儿玉
佛摩里安,儿玉
达采霸,华坡
天使,华宝
Fomorian,华坡
天使,达江霸
达江坝,佛摩里安
天使,佛摩人

它是由我制作的一种“更清洁”的公式提供的,但这不是问题。

我正在测试的整体输入是这样的(在 A 列中),也是初始对的更清晰公式的输入:

可用的
小精灵
式神
儿玉
华坡
达江坝
天使
福莫里安
高精灵

而预期的 output... 真的很大。 这是获得想法的第一行示例(托管在原始工作表的 H2 中):

一个 C D F G H
小精灵 式神 儿玉 华坡 达江坝 天使 福莫里安 高精灵
小精灵 式神 儿玉 华坡 达江坝 福莫里安 天使 高精灵
小精灵 式神 儿玉 华坡 天使 达江坝 福莫里安 高精灵
小精灵 式神 儿玉 华坡 天使 福莫里安 达江坝 高精灵
小精灵 式神 儿玉 华坡 福莫里安 达江坝 天使 高精灵
小精灵 式神 儿玉 华坡 福莫里安 天使 达江坝 高精灵
小精灵 式神 儿玉 达江坝 华坡 天使 福莫里安 高精灵
等等...

我目前不知道如何解决这个问题。 我想在我的表格中放置至少 8 个起始怪物以供分析,如果不是完整的 12 个以供游戏结束。

可能有一种比我现有的方式更好、更紧凑的方式来生成这些排列。 我可能想启动 Excel 在我的超级系统上尝试这个,然后看看它在哪里断线。 然而,我想要更有效的公式来解决我在 Google 表格中的“数组太大”问题。 这是我工作得最好的地方,也是我有许多其他项目的地方。

PS:感谢 Erik 对完整工作表的帮助,并教我一些使用 LAMBDA 和命名函数的新技术。 它有助于最终解决我的问题。 Stack 的政策限制我共享完整的电子表格是有充分理由的(担心不良行为者等等)。 因此,我对这篇文章进行了编辑,以使其更好地包含在内。 再次,为混乱感到抱歉。

Stack 是一项免费的、由志愿者运营的服务,旨在提供“小帮助”(例如,一些共享知识、对现有公式或几乎正确的代码行的调整等)。 它的设计初衷不是让人们可以免费获得定制的、复杂的、耗时的解决方案。

也就是说,由于最近推出的新功能,我选择采用它,主要是为了展示其中一些新功能的一些好处。

我提供这个解决方案没有解释,因为解释比写公式要花更长的时间。

我已将以下公式放在链接电子表格中新工作表(“Erik 帮助”)的单元格 A1 中:

=ArrayFormula(IFERROR(TO_TEXT(VLOOKUP(SPLIT(REGEXREPLACE({LAMBDA(x,FILTER(x,NOT(REGEXMATCH(x&"","8|9|0")),x>=12,BYROW(1*(MID(x&LEFT("abc",5-LEN(x)),SEQUENCE(1,4),1)<MID(x&LEFT("abc",5-LEN(x)),SEQUENCE(1,4,2),1)),LAMBDA(row,SUM(row)))=4))(SEQUENCE(34567));REGEXREPLACE("1234567",SEQUENCE(7)&"","")*1;1234567}&"","(.)","$1~"),"~",1,1),{SEQUENCE(7),'chains-original':A2,A8},2,FALSE))))

注意新函数 LAMBDA 和 BYROW 的使用。

为了未来访问者的利益,如果链接的电子表格失效,这是公式中与电子表格无关的部分,将生成数字 1-7 的所有唯一组合,即 2 到 7 个数字的组合和在任何单个结果中不重复任何数字:

=ArrayFormula(SPLIT(REGEXREPLACE({LAMBDA(x,FILTER(x,NOT(REGEXMATCH(x&"","8|9|0")),x>=12,BYROW(1*(MID(x&LEFT("abc",5-LEN(x)),SEQUENCE(1,4),1)<MID(x&LEFT("abc",5-LEN(x)),SEQUENCE(1,4,2),1)),LAMBDA(row,SUM(row)))=4))(SEQUENCE(34567));REGEXREPLACE("1234567",SEQUENCE(7)&"","")*1;1234567}&"","(.)","$1~"),"~",1,1))

如果其他人对此公式的特定元素有具体问题,请在下面的评论中提出该问题,我将尽我所能在合理范围内回答。 但是,我建议首先使用公式(本段上方与电子表格无关的公式)并对其进行剖析以进行理解。 经验永远是最好的老师。

有不同的算法来实现这一点。 请参阅计算中的排列

直接且最简单的方法是创建一个数字序列,其中BASE等于可供选择的项目数。 例如,如果有 7 个项目可供选择,请创建如下序列:

BASE 7(=数组公式(BASE(SEQUENCE(25),7,7)))
0000001
0000002
0000003
0000004
0000005
0000006
0000010
0000011
0000012
0000013
0000014
0000015
0000016
0000020
0000021
0000022
0000023
0000024
0000025
0000026
0000030
0000031
0000032
0000033
0000034
……

注意每个 position 有 7 个变量(0 到 6),有 7 个位置。 一旦我们得到了PERMUTATIONA(7,7)的所有数字,只需删除所有重复的数字就很简单了,其中每个 position 中的所有数字都是唯一的,即每个数字的COUNTUNIQUE = 7(例如:0124536)。 这是一个实现:

=ARRAYFORMULA(LAMBDA(n,QUERY(BYROW(SPLIT(REGEXREPLACE(TO_TEXT(BASE(SEQUENCE(PERMUTATIONA(n,n)-1),n,n)),"\B","."),"."),LAMBDA(r, IF(COUNTUNIQUE(r)<>n,"👀",JOIN(,r)))),"where not Col1='👀' ",0))(5))

不幸的是,谷歌任意限制执行时间少于几秒钟。 因此,此公式无法获得超过n=5的所有排列。

列表中的下一个是使用阶乘(Lehmer 的代码)来获取排列。 请参阅此处的排列 请注意数字序列与排列之间的直接关系。

十进制 因数 排列
0 0:0:0! (0,1,2)
1 0:1:0! (0,2,1)
2 1:0:0! (1,0,2)
3 1:1:0! (1,2,0)
4 2:0:0! (2,0,1)
5 2:1:0! (2,1,0)

表格来自https://wikipedia.org/wiki/Factorial_number_system在 CC-BY-SA 3.0 下获得许可

我实现了这个算法,并且在n=5处再次达到了 Google 的限制。 (此处未显示代码)。

接下来,我们有Lexicographic ordering 算法如下:

以下算法在给定排列之后按字典顺序生成下一个排列。 它就地改变了给定的排列。

找到满足 a[k] < a[k + 1] 的最大索引 k。 如果不存在这样的索引,则排列是最后一个排列。 找到大于 k 的最大索引 l,使得 a[k] < a[l]。 将 a[k] 的值与 a[l] 的值交换。 反转从 a[k + 1] 到最后一个元素 a[n] 的序列。

例如,给定序列 [1, 2, 3, 4](按升序排列),并且给定索引是从零开始的,步骤如下:

索引 k = 2,因为 3 被放置在满足仍然小于 a[k + 1] 即 4 的最大索引条件的索引处。索引 l = 3,因为 4 是序列中唯一的值大于 3 以满足条件 a[k] < a[l]。 a[2] 和 a[3] 的值被交换以形成新的序列 [1, 2, 4, 3]。 从 k-index a[2] 到最后一个元素的顺序是相反的。 因为只有一个值位于该索引(3)之后,所以在这种情况下序列保持不变。 因此,初始 state 的词典后继被置换:[1,2,4,3]。

引自https://en.wikipedia.org/wiki/Permutation根据 CC-BY-SA 3.0 许可

感谢 Google 对递归和命名函数的最新支持,我实现了这一点,并且能够在单个公式中获得多达n=6 (720 项),但我仍然达到了 Google 的递归限制,即n=7 (5040 项) . 话虽如此,仍然可以在没有数组公式的情况下一一获得所有 5k 排列(甚至可能n=8 (40320 项),具体取决于您的设备可以处理的内容)。

1.小精灵 2.式神 3.儿玉 4.花婆 5.达江坝 6.天使 7.佛摩里安
1.小精灵 2.式神 3.儿玉 4.花婆 5.达江坝 7.佛摩里安 6.天使
1.小精灵 2.式神 3.儿玉 4.花婆 6.天使 5.达江坝 7.佛摩里安
1.小精灵 2.式神 3.儿玉 4.花婆 6.天使 7.佛摩里安 5.达江坝
1.小精灵 2.式神 3.儿玉 4.花婆 7.佛摩里安 5.达江坝 6.天使
1.小精灵 2.式神 3.儿玉 4.花婆 7.佛摩里安 6.天使 5.达江坝
1.小精灵 2.式神 3.儿玉 5.达江坝 4.花婆 6.天使 7.佛摩里安
1.小精灵 2.式神 3.儿玉 5.达江坝 4.花婆 7.佛摩里安 6.天使
1.小精灵 2.式神 3.儿玉 5.达江坝 6.天使 4.花婆 7.佛摩里安
1.小精灵 2.式神 3.儿玉 5.达江坝 6.天使 7.佛摩里安 4.花婆
1.小精灵 2.式神 3.儿玉 5.达江坝 7.佛摩里安 4.花婆 6.天使
1.小精灵 2.式神 3.儿玉 5.达江坝 7.佛摩里安 6.天使 4.花婆
1.小精灵 2.式神 3.儿玉 6.天使 4.花婆 5.达江坝 7.佛摩里安
1.小精灵 2.式神 3.儿玉 6.天使 4.花婆 7.佛摩里安 5.达江坝
1.小精灵 2.式神 3.儿玉 6.天使 5.达江坝 4.花婆 7.佛摩里安
1.小精灵 2.式神 3.儿玉 6.天使 5.达江坝 7.佛摩里安 4.花婆
1.小精灵 2.式神 3.儿玉 6.天使 7.佛摩里安 4.花婆 5.达江坝
1.小精灵 2.式神 3.儿玉 6.天使 7.佛摩里安 5.达江坝 4.花婆
1.小精灵 2.式神 3.儿玉 7.佛摩里安 4.花婆 5.达江坝 6.天使
1.小精灵 2.式神 3.儿玉 7.佛摩里安 4.花婆 6.天使 5.达江坝
1.小精灵 2.式神 3.儿玉 7.佛摩里安 5.达江坝 4.花婆 6.天使
1.小精灵 2.式神 3.儿玉 7.佛摩里安 5.达江坝 6.天使 4.花婆
1.小精灵 2.式神 3.儿玉 7.佛摩里安 6.天使 4.花婆 5.达江坝
1.小精灵 2.式神 3.儿玉 7.佛摩里安 6.天使 5.达江坝 4.花婆

显示n=7的前几个排列。 要使公式起作用,请务必注意列表中必须存在固有的升序。 我在1.Pixie2.Shikigami ... 等中添加了前缀: 1.2.等,以强制执行升序 可以在公式本身中进行排序,但未实现。

  • A1:G1

    1.小精灵 2.式神 3.儿玉 4.花婆 5.达江坝 6.天使 7.佛摩里安
  • A2

     =GET_NEXT_LEX(A1:G1)

根据需要向下拖动填充或自动填充(40k 或 5k 行)。

命名函数:

创建这些函数

主要function:

  • GET_NEXT_LEX(arr)
=ARRAYFORMULA(
  TRANSPOSE(
    LAMBDA(arr,     
      LAMBDA(k,       
        LAMBDA(sarr,k,{SPLICE(sarr,k+1,2^999);REVERSE(SPLICE(sarr,1,k+1))})  
          (SWAP(arr,k,XMATCH(TRUE,INDEX(arr,k)<SPLICE(arr,1,k+1),,-1)+k),k)
      )(XMATCH(TRUE,POP(arr)<SHIFT(arr),,-1))
    )(TRANSPOSE(arr))
  )
)

辅助功能:

功能类似于

  • SPLICE(arr,i,j)
=FILTER(arr,LAMBDA(seq,(seq<i)+(seq>=j))(SEQUENCE(ROWS(arr))))
  • REVERSE(arr)
=POP(REDUCE(,arr,LAMBDA(a,c,{c;a})))
  • SWAP(arr,i,j)
=SORT(arr,LAMBDA(keys,SWITCH(keys,i,j,j,i,keys))(SEQUENCE(ROWS(arr))),1)
  • POP(arr)
=ARRAY_CONSTRAIN(arr,ROWS(arr)-1,1)
  • SHIFT(arr)
=FILTER(arr,{0;SEQUENCE(ROWS(arr)-1)})

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM