下一个词法"permutation"算法

Next lexical "permutation" algorithm

本文关键字:算法 permutation 下一个 词法      更新时间:2023-10-16

我写了一个程序来解决24的通用版本(好奇者的链接(。也就是说,给定一组n数字,有没有办法对它们执行二进制操作,以便它们计算到目标数字。

为此,我将可能的表达式视为由 'v''o' 组成的 char 数组,其中 'v' 是值的占位符,'o' 是操作的占位符。请注意,如果存在n值,则必须有n-1操作。

该程序目前的工作方式是按字典顺序检查{'o','o',...,'v',v'...}的每个排列,并查看前缀表达式是否有效。例如,当n = 4 时,以下表达式被视为有效:

{‘o’,’o’,’o’,’v’,’v’,’v’,’v’} {‘o’, ‘v’, ‘o’, ‘v’, ‘o’, ‘v’, ‘v’}

以下表达式无效:

{‘v’,’o’,’v’,’o’,’o’,’v’,’v’}

{‘o’,’o’,’v’,’v’,’v’,’o’,’v’}

我的问题是,是否存在一种有效的算法来获得在某种排序中有效的下一个排列?目标是消除必须检查表达式是否对每个排列都有效。

此外,如果存在这样的算法,是否存在O(1)时间来计算第 k 个有效排列?

到目前为止我有什么

我假设长度2n-1的前缀表达式A被认为是有效的,当且仅当

每个A[i:2n-1) number of operations < number of values

其中0<=i<2n-1(从 i 开始,在 2n-1 结束(非包含(的子数组(

此外,这意味着C(n,k) n choose k的地方正好存在(1/n)C(2n-2,n-1)有效的排列。

下面介绍如何生成ov模式。以下代码背后的细节在高德纳第 4A 卷中(或至少提到;我可能做了其中一个练习(。您可以使用现有的排列机制在更改模式之前以各种方式排列值。

代码

#include <cstdio>
namespace {
void FirstTree(int f[], int n) {
  for (int i = n; i >= 0; i--) f[i] = 2 * i + 1;
}
bool NextTree(int f[], int n) {
  int i = 0;
  while (f[i] + 1 == f[i + 1]) i++;
  f[i]++;
  FirstTree(f, i - 1);
  return i + 1 < n;
}
void PrintTree(int f[], int n) {
  int i = 0;
  for (int j = 0; j < 2 * n; j++) {
    if (j == f[i]) {
      std::putchar('v');
      i++;
    } else {
      std::putchar('o');
    }
  }
  std::putchar('v');
  std::putchar('n');
}
}
int main() {
  constexpr int kN = 4;
  int f[1 + kN];
  FirstTree(f, kN);
  do {
    PrintTree(f, kN);
  } while (NextTree(f, kN));
}

生成输出

ovovovovv
oovvovovv
ovoovvovv
oovovvovv
ooovvvovv
ovovoovvv
oovvoovvv
ovoovovvv
oovovovvv
ooovvovvv
ovooovvvv
oovoovvvv
ooovovvvv
oooovvvvv

有一种方法可以得到第 k 棵树,但时间是 O(n( 而不是 O(1(。魔术词是无排名的二叉树。