【问题标题】:How to list all permutations without repetition?如何列出所有排列而不重复?
【发布时间】:2022-09-27 16:00:12
【问题描述】:

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

Shuffle a deck of 7 hypothetical trading cards and list out the orders they can come in a Google Sheet

惊喜!我的问题孩子实际上是为真女神转生 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 列):

unique init pairs
Pixie,Shikigami
Kodama,Pixie
Hua Po,Pixie
Datsue-Ba,Pixie
Angel,Pixie
Fomorian,Pixie
Kodama,Shikigami
Hua Po,Shikigami
Datsue-Ba,Shikigami
Angel,Shikigami
Fomorian,Shikigami
Hua Po,Kodama
Datsue-Ba,Kodama
Angel,Kodama
Fomorian,Kodama
Datsue-Ba,Hua Po
Angel,Hua Po
Fomorian,Hua Po
Angel,Datsue-Ba
Datsue-Ba,Fomorian
Angel,Fomorian

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

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

available
Pixie
Shikigami
Kodama
Hua Po
Datsue Ba
Angel
Fomorian
High Pixie

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

A B C D E F G H
Pixie Shikigami Kodama Hua Po Datsue Ba Angel Fomorian High Pixie
Pixie Shikigami Kodama Hua Po Datsue Ba Fomorian Angel High Pixie
Pixie Shikigami Kodama Hua Po Angel Datsue Ba Fomorian High Pixie
Pixie Shikigami Kodama Hua Po Angel Fomorian Datsue Ba High Pixie
Pixie Shikigami Kodama Hua Po Fomorian Datsue Ba Angel High Pixie
Pixie Shikigami Kodama Hua Po Fomorian Angel Datsue Ba High Pixie
Pixie Shikigami Kodama Datsue Ba Hua Po Angel Fomorian High Pixie
and so on...

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

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

  • 如果您将带有示例输入和输出的表格添加到问题中,您的问题可以得到很大改善。 Tables 是比电子表格更好的选择来显示您的数据结构。如果您共享电子表格,请确保还添加工作表的图像以避免问题结束,因为此处的问题必须是 self containedYour email address can also be accessed by the public,当您共享 Google 文件时。

标签: google-sheets google-sheets-formula permutation combinatorics lexicographic-ordering


【解决方案1】:

有不同的算法来实现这一点。见Permutation in computing

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

BASE 7(=ARRAYFORMULA(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
....

请注意,在每个位置,有 7 个变量(0 到 6)并且有 7 个位置。一旦我们获得了PERMUTATIONA(7,7) 的所有数字,只需删除所有重复的数字就很简单了,其中每个位置的所有数字都是唯一的,即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 的代码)来获取排列。见permutations here。请注意数字序列与排列之间的直接关系。

decimal factoradic permutation
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 再次达到了谷歌的限制。 (此处未显示代码)。

接下来,我们有字典顺序.算法如下:

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

找到满足 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)之后,所以在这种情况下序列保持不变。因此,初始状态的词典后继被置换:[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.Pixie 2.Shikigami 3.Kodama 4.Hua Po 5.Datsue Ba 6.Angel 7.Fomorian
1.Pixie 2.Shikigami 3.Kodama 4.Hua Po 5.Datsue Ba 7.Fomorian 6.Angel
1.Pixie 2.Shikigami 3.Kodama 4.Hua Po 6.Angel 5.Datsue Ba 7.Fomorian
1.Pixie 2.Shikigami 3.Kodama 4.Hua Po 6.Angel 7.Fomorian 5.Datsue Ba
1.Pixie 2.Shikigami 3.Kodama 4.Hua Po 7.Fomorian 5.Datsue Ba 6.Angel
1.Pixie 2.Shikigami 3.Kodama 4.Hua Po 7.Fomorian 6.Angel 5.Datsue Ba
1.Pixie 2.Shikigami 3.Kodama 5.Datsue Ba 4.Hua Po 6.Angel 7.Fomorian
1.Pixie 2.Shikigami 3.Kodama 5.Datsue Ba 4.Hua Po 7.Fomorian 6.Angel
1.Pixie 2.Shikigami 3.Kodama 5.Datsue Ba 6.Angel 4.Hua Po 7.Fomorian
1.Pixie 2.Shikigami 3.Kodama 5.Datsue Ba 6.Angel 7.Fomorian 4.Hua Po
1.Pixie 2.Shikigami 3.Kodama 5.Datsue Ba 7.Fomorian 4.Hua Po 6.Angel
1.Pixie 2.Shikigami 3.Kodama 5.Datsue Ba 7.Fomorian 6.Angel 4.Hua Po
1.Pixie 2.Shikigami 3.Kodama 6.Angel 4.Hua Po 5.Datsue Ba 7.Fomorian
1.Pixie 2.Shikigami 3.Kodama 6.Angel 4.Hua Po 7.Fomorian 5.Datsue Ba
1.Pixie 2.Shikigami 3.Kodama 6.Angel 5.Datsue Ba 4.Hua Po 7.Fomorian
1.Pixie 2.Shikigami 3.Kodama 6.Angel 5.Datsue Ba 7.Fomorian 4.Hua Po
1.Pixie 2.Shikigami 3.Kodama 6.Angel 7.Fomorian 4.Hua Po 5.Datsue Ba
1.Pixie 2.Shikigami 3.Kodama 6.Angel 7.Fomorian 5.Datsue Ba 4.Hua Po
1.Pixie 2.Shikigami 3.Kodama 7.Fomorian 4.Hua Po 5.Datsue Ba 6.Angel
1.Pixie 2.Shikigami 3.Kodama 7.Fomorian 4.Hua Po 6.Angel 5.Datsue Ba
1.Pixie 2.Shikigami 3.Kodama 7.Fomorian 5.Datsue Ba 4.Hua Po 6.Angel
1.Pixie 2.Shikigami 3.Kodama 7.Fomorian 5.Datsue Ba 6.Angel 4.Hua Po
1.Pixie 2.Shikigami 3.Kodama 7.Fomorian 6.Angel 4.Hua Po 5.Datsue Ba
1.Pixie 2.Shikigami 3.Kodama 7.Fomorian 6.Angel 5.Datsue Ba 4.Hua Po

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

  • A1:G1:

    1.Pixie 2.Shikigami 3.Kodama 4.Hua Po 5.Datsue Ba 6.Angel 7.Fomorian
  • A2:

    =GET_NEXT_LEX(A1:G1)
    

根据需要向下拖动填充或自动填充(40k 或 5k 行)。使用这种方法的好处是,您可以从中断的地方继续。如果您需要 200 万个排列,而 Google 表格无法处理超过 100 万个。您可以将第一个百万放入一个电子表格中,然后将下一个百万放入另一个电子表格中(您所需要的只是前一个电子表格中的最后一个排列)。

命名函数:

创建these functions

主功能:

  • 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)})

【讨论】:

  • 如果有人可以优化时间/空间复杂度的实现或实现堆算法,那么就有可能在单个数组公式函数中获取所有数据。
  • 如果 Google 将来取消任意时间/空间限制,创建单个数组公式会比一路拖动填充更容易。
猜你喜欢
  • 2019-06-03
  • 1970-01-01
  • 1970-01-01
  • 2013-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-17
相关资源
最近更新 更多