任何人都可以解释算法以在仅使用单个堆栈时生成可能的排列,并且推送和弹出是唯一允许的操作。搜索了很多,但没有明确的答案。这种排列的总数也由加泰罗尼亚数字给出。但我没有得到证明。如果可能的话,请解释一下。
谢谢!!
任何人都可以解释算法以在仅使用单个堆栈时生成可能的排列,并且推送和弹出是唯一允许的操作。搜索了很多,但没有明确的答案。这种排列的总数也由加泰罗尼亚数字给出。但我没有得到证明。如果可能的话,请解释一下。
谢谢!!
这个问题使用输入队列和输出队列以及堆栈。
这些操作是“将一个项目从输入队列推入堆栈”和“将一个项目从堆栈弹出到输出队列”。
1 2 3
output ______ ______ input
\ /
<--+ | +---
pop | | | push
| | v
stack
例如,使用 input 1 2 3
,您可以获得2 1 3
以下序列的输出:
但是,如果您尝试生成3 1 2
.
您如何通过这些操作生成所有可能的排列?好吧,递归执行很简单:在任何给定状态下(“状态”由输入队列、堆栈和输出队列的内容组成),您最多可以执行两种可能的操作(您可以推送如果输入队列中至少有一项;如果堆栈上至少有一项,则可以弹出),这将为您提供最多两种可能的新状态来探索。
有关此问题的更多详细信息以及与加泰罗尼亚数字的关系,请查找 Knuth 的“计算机编程的艺术”第 1 卷(第 3 版)的副本 - 在 §2.2.1 中进行了讨论;请参阅第 242-243 页上的练习 2 - 5(以及第 240 页上我的图表的更好版本!)。
我正在考虑同样的问题,最后编写了一个小的 Prolog 程序来生成排列,并“发现”了与加泰罗尼亚数字的关系,然后找到了你的问题。所以这不是你问题的真正答案,但这里是 Prolog 程序:
% Generate permutation counts
count_pushpop(N-K) :-
length(_, N),
findall(Seq, pushpop(N, Seq), Seqs),
length(Seqs, K).
% Create an integer sequence from 1 to N
% and permutate it using all possible push-pop
% operations starting with an empty stack.
pushpop(N, Seq) :-
numlist(1, N, List),
pushpop(List, [], Seq).
% Generate all the possible ways a list
% of items can be pushed into a stack
% and poped out of it.
pushpop([], [], []).
pushpop([H | List], Stack, Seq) :-
pushpop(List, [H | Stack], Seq).
pushpop(List, [H | Stack], [H | Seq]) :-
pushpop(List, Stack, Seq).
证明并非所有n!
排列都是可能的:
?- findall(Seq, pushpop(3, Seq), Seqs).
Seqs = [[3, 2, 1], [2, 3, 1], [2, 1, 3], [1, 3, 2], [1, 2, 3]].
证明它生成加泰罗尼亚数字(或者如果不是堆栈溢出,这将是一个证明;)):
?- count_pushpop(N-K).
N = K, K = 0 ;
N = K, K = 1 ;
N = K, K = 2 ;
N = 3,
K = 5 ;
N = 4,
K = 14 ;
N = 5,
K = 42 ;
N = 6,
K = 132 ;
N = 7,
K = 429 ;
N = 8,
K = 1430 ;
N = 9,
K = 4862 ;
N = 10,
K = 16796 ;
N = 11,
K = 58786 ;
N = 12,
K = 208012 ;
ERROR: Out of global stack
首先,在以下假设下,不可能为任意排列编写算法来执行此操作:
您只能按顺序从输入中读取。
写入输出类似地顺序发生,写入输出的数据一旦写入就无法读取。
除了一个堆栈之外,您只允许使用恒定数量的内存。(这意味着没有额外的递归或数据结构)。
这是上下文无关语言的抽引引理的结果:
维基:http ://en.wikipedia.org/wiki/Pumping_lemma_for_context-free_languages
(或者也可以查看:Michael Sipser (1997)。计算理论导论。我相信这是第 4 章中的练习之一。)
现在,您可以轻松地实现一种算法,通过打破这些假设中的任何一个来解决这个问题。例如,如果您可以任意读取输入,则不需要堆栈:
def permute(seq, permutation):
result = []
for i in permutation:
result.push(seq[i])
return result
或者,如果你修复了一个排列,问题就会变得有限,你同样不需要堆栈。您只需将常用算法展开为所有输入的特殊情况(即就像在编译器中进行部分评估一样)。这太可怕了,所以我不会费心写出所有细节,但它仍然有效,因为可能输入的总数是固定的(但很大!)常数。