Ram*_*hum 13 python algorithm permutation combinatorics time-complexity
我已经工作了几个小时,但无法弄明白.
将排列度定义为创建它时需要组合的最小转置数.所以程度(0, 1, 2, 3)为0,程度(0, 1, 3, 2)为1,程度(1, 0, 3, 2)为2,等等.
将空间Snd看作n具有度数的长度序列的所有排列的空间d.
我想要两种算法.一个在该空间中进行置换并为其分配索引号的另一个,并且另一个取一个项的索引号Snd并检索其置换.索引号显然应该是连续的(即在范围内0 to len(Snd)-1,每个排列具有不同的索引号.)
我希望这样实现O(sane); 这意味着如果你要求排列数17,算法不应该遍历0到16之间的所有排列来检索你的排列.
不知道怎么解决这个问题?
(如果你要包含代码,我更喜欢Python,谢谢.)
更新:
我想要一个解决方案
我过去两天试过解决这个问题而且我没有成功.如果你能提供Python代码,那就是最好的.
这个答案不如我的另一个答案优雅/高效,但它描述了一种多项式时间算法,可以应对排列顺序的额外约束。我将描述一个子例程,给定一个 n 元素排列的前缀和一组度数,计算有多少个排列具有该前缀和属于该集合的度数。给定这个子例程,我们可以对指定子集中指定排名的排列进行 n 元搜索,一次扩展已知前缀一个元素。
我们可以将 n 元素排列 p 可视化为 n 顶点、n 弧有向图,其中对于每个顶点 v,都有一条从 v 到 p(v) 的弧。该有向图由顶点不相交循环的集合组成。例如,排列 31024 看起来像
_______
/ \
\->2->0->3
__ __
/ | / |
1<-/ 4<-/ .
Run Code Online (Sandbox Code Playgroud)
给定排列的前缀,我们可以可视化与该前缀对应的子图,它将是顶点不相交路径和循环的集合。例如,前缀 310 看起来像
2->0->3
__
/ |
1<-/ .
Run Code Online (Sandbox Code Playgroud)
我将描述(1)此前缀的扩展(即排列)和(2)相关元素集上的完整排列之间的双射。此双射最多可保留循环数(即元素数减去次数)的常数项。常数项是前缀中的循环数。
(2) 中提到的排列位于以下元素集上。从原始集合开始,删除前缀中完整的循环所涉及的所有元素,并为每个路径引入一个新元素。例如,如果前缀是310,那么我们删除完整的循环1,并为路径2->0->3引入一个新元素A,得到集合{4,A}。现在,给定集合(1)中的排列,我们通过删除已知循环并用新元素替换每个路径来获得集合(2)中的排列。例如,排列31024对应于排列4->4、A->A,排列31042对应于排列4->A、A->4。我声称(1)该映射是双射,并且(2)它保留了之前描述的度数。
第一类第 (n,k) 个斯特林数的定义或多或少,写为
[n]
[ ]
[k]
Run Code Online (Sandbox Code Playgroud)
(ASCII 艺术方括号)是 n - k 次的 n 元素排列的数量。要计算 n 元素排列的 r 元素前缀的扩展数,请计算 c,即前缀中的完整循环数。总和,对于指定集合中的每个度数 d,斯特林数
[ n - r ]
[ ]
[n - d - c]
Run Code Online (Sandbox Code Playgroud)
第一类,将“不可能”指数的项视为零(斯特林数的一些出于分析动机的定义在意想不到的地方是非零的)。
为了从排列中获得排名,我们再次进行 n 元搜索,只不过这次我们使用排列而不是排名来指导搜索。
下面是两者的一些 Python 代码(包括测试函数)。
import itertools
memostirling1 = {(0, 0): 1}
def stirling1(n, k):
ans = memostirling1.get((n, k))
if ans is None:
if not 1 <= k <= n: return 0
ans = (n - 1) * stirling1(n - 1, k) + stirling1(n - 1, k - 1)
memostirling1[(n, k)] = ans
return ans
def cyclecount(prefix):
c = 0
visited = [False] * len(prefix)
for (i, j) in enumerate(prefix):
while j < len(prefix) and not visited[j]:
visited[j] = True
if j == i:
c += 1
break
j = prefix[j]
return c
def extcount(n, dset, prefix):
c = cyclecount(prefix)
return sum(stirling1(n - len(prefix), n - d - c) for d in dset)
def unrank(n, dset, rnk):
assert rnk >= 0
choices = set(range(n))
prefix = []
while choices:
for i in sorted(choices):
prefix.append(i)
count = extcount(n, dset, prefix)
if rnk < count:
choices.remove(i)
break
del prefix[-1]
rnk -= count
else:
assert False
return tuple(prefix)
def rank(n, dset, perm):
assert n == len(perm)
rnk = 0
prefix = []
choices = set(range(n))
for j in perm:
choices.remove(j)
for i in sorted(choices):
if i < j:
prefix.append(i)
rnk += extcount(n, dset, prefix)
del prefix[-1]
prefix.append(j)
return rnk
def degree(perm):
return len(perm) - cyclecount(perm)
def test(n, dset):
for (rnk, perm) in enumerate(perm for perm in itertools.permutations(range(n)) if degree(perm) in dset):
assert unrank(n, dset, rnk) == perm
assert rank(n, dset, perm) == rnk
test(7, {2, 3, 5})
Run Code Online (Sandbox Code Playgroud)