Python 按索引号获取具有指定度的置换
我花了好几个小时研究这个问题,但还是没弄明白 将置换度定义为创建置换所需的最小置换数。所以aPython 按索引号获取具有指定度的置换,python,algorithm,permutation,time-complexity,combinatorics,Python,Algorithm,Permutation,Time Complexity,Combinatorics,我花了好几个小时研究这个问题,但还是没弄明白 将置换度定义为创建置换所需的最小置换数。所以a(0,1,2,3)的度数是0,(0,1,3,2)的度数是1,(1,0,3,2)的度数是2,以此类推 将空间Snd视为具有度d的长度序列n的所有排列的空间 我想要两个算法。一个在该空间中获取排列并为其分配索引号,另一个在Snd中获取项目的索引号并检索其排列。索引编号显然应该是连续的(即在0到len(Snd)-1的范围内,每个排列都有一个不同的索引编号。) 我希望这在O(理智)中实现;这意味着,如果你要求17
(0,1,2,3)
的度数是0,(0,1,3,2)
的度数是1,(1,0,3,2)
的度数是2,以此类推
将空间Snd
视为具有度d
的长度序列n
的所有排列的空间
我想要两个算法。一个在该空间中获取排列并为其分配索引号,另一个在
Snd
中获取项目的索引号并检索其排列。索引编号显然应该是连续的(即在0到len(Snd)-1的范围内,每个排列都有一个不同的索引编号。)
我希望这在O(理智)
中实现;这意味着,如果你要求17号排列,算法不应该遍历0到16之间的所有排列来检索你的排列
你知道怎么解决这个问题吗
(如果要包含代码,我更喜欢Python,谢谢。)
更新:
我想要一个解决方案,其中
排列是根据它们的字典顺序排序的(不是通过手动排序,而是通过一种有效的算法,该算法首先给出它们的字典顺序),并且
我希望算法也能接受不同度数的序列,所以我可以说“我希望排列空间(5)中1、3或4度数的所有排列中有78个排列”。(基本上这个函数需要一个度元组。)这也会影响反向函数,该函数通过排列计算索引;根据度数的设置,指数会有所不同
在过去的两天里,我试图解决这个问题,但没有成功。如果您可以提供Python代码,那就最好了。我认为您正在寻找一种变体,用于测量两个字符串之间的编辑次数。有效的计算方法是采用一种称为动态规划(dynamic programming)的技术——链接文章中提供了一种“正常”Levenshtein距离的伪算法。您需要对此进行调整,以考虑这样一个事实:不添加、删除或替换字符,唯一允许的操作是在两个位置交换元素
关于你的第二个算法:在排列度和“a”结果排列之间不是1:1的关系,相反,可能的结果的数量随着交换的数量呈指数增长:对于k
元素序列,有k*(k-1)/2
可能的索引对可以交换。如果我们将该数字称为l
,在d
交换之后,您就有了l^d
可能的结果(即使其中一些可能是相同的,如先交换01然后交换23,或先交换23然后交换01).长度n和度数d的置换正是那些可以写成k=n-d循环组合的置换,这些循环将n个元素分割开来。这种排列的数量由n表示,写在方括号中k的顶部
第一类斯特林数满足递推关系
[n] [n - 1] [n - 1]
[ ] = (n - 1) [ ] + [ ]
[k] [ k ] [k - 1],
这意味着,直观地说,将n个元素划分为k个循环的方法是将n-1个非最大元素划分为k个循环,并以n-1种方法中的一种方法拼接到最大元素中,或者将最大元素放在其自己的循环中,并将n-1个非最大元素划分为k-1个循环。从一个重复值表开始工作,可以沿着这条线追踪决策
memostirling1 = {(0, 0): 1}
def stirling1(n, k):
if (n, k) not in memostirling1:
if not (1 <= k <= n): return 0
memostirling1[(n, k)] = (n - 1) * stirling1(n - 1, k) + stirling1(n - 1, k - 1)
return memostirling1[(n, k)]
def unrank(n, d, i):
k = n - d
assert 0 <= i <= stirling1(n, k)
if d == 0:
return list(range(n))
threshold = stirling1(n - 1, k - 1)
if i < threshold:
perm = unrank(n - 1, d, i)
perm.append(n - 1)
else:
(q, r) = divmod(i - threshold, stirling1(n - 1, k))
perm = unrank(n - 1, d - 1, r)
perm.append(perm[q])
perm[q] = n - 1
return perm
memostring1={(0,0):1}
def斯特林1(n,k):
如果(n,k)不在备忘录Stirling1中:
如果不是(1我写了这个stackoverflow回答了一个类似的问题:。它能帮助我吗
不同之处可能在于用于生成perm的交换位,但Python中给出了index to perm和perm to index函数
我后来继续创建了这个Rosetta代码任务,该任务通过引用和更多代码来充实
希望有帮助:-)这个答案不如我的另一个答案优雅/高效,但它描述了一个多项式时间算法,可以处理排列顺序的附加约束。我将描述一个子程序,给定一个n元素置换的前缀和一组度,计算有多少置换具有该前缀和属于该集合的度。给定此子例程,我们可以对指定子集中指定秩的排列进行n元搜索,每次将已知前缀扩展一个元素
我们可以将n元素置换p可视化为一个n顶点,n弧有向图,其中,对于每个顶点v,有一个从v到p(v)的弧。这个有向图由一组顶点不相交的圈组成。例如,排列31024看起来像
_______
/ \
\->2->0->3
__ __
/ | / |
1<-/ 4<-/ .
2->0->3
__
/ |
1<-/ .
(ASCII art方括号),是阶数为n-k的n元素置换数。要计算n元素置换的r元素前缀的扩展数,计数c,前缀中的完整循环数。对于指定集合中的每个度d,斯特林数之和
[ n - r ]
[ ]
[n - d - c]
第一类,将“不可能”指数的项设为零(一些受分析启发的斯特林数定义在意外的地方不为零)
为了从排列中获得秩,我们再次进行n元搜索,但这次我们使用排列而不是秩来指导搜索
下面是这两个方面的一些Python代码(包括一个测试函数)
导入itertools
记忆斯特林1={(0,0):1}
def斯特林1(n,k):
ans=记忆斯特林1.get((n,k))
如果ans为无:
如果不是1如果你完全在词汇方面工作,那么第一部分是直截了当的。给我答案
[ n - r ]
[ ]
[n - d - c]
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})
01234
31042
permutation cycles (0342)(1)
degree = (4-1) + (1-1) = 3
def cycles(prefix):
_cycles = []
i = j = 0
visited = set()
while j < len(prefix):
if prefix[i] == i:
_cycles.append({"is":[i],"incomplete": False})
j = j + 1
i = i + 1
elif not i in visited:
cycle = {"is":[],"incomplete": False}
cycleStart = -1
while True:
if i >= len(prefix):
for k in range(len(_cycles) - 1,-1,-1):
if any(i in cycle["is"] for i in _cycles[k]["is"]):
cycle["is"] = list(set(cycle["is"] + _cycles[k]["is"]))
del _cycles[k]
cycle["incomplete"] = True
_cycles.append(cycle)
break
elif cycleStart == i:
_cycles.append(cycle)
break
else:
if prefix[i] == j + 1:
j = j + 1
visited.add(i)
if cycleStart == -1:
cycleStart = i
cycle["is"].append(i)
i = prefix[i]
while j in visited:
j = j + 1
i = j
return _cycles
def degree(cycles):
d = 0
for i in cycles:
if i["incomplete"]:
d = d + len(i["is"])
else:
d = d + len(i["is"]) - 1
return d
number of permutations of n=5,d=3 that start with "0" = S(4,4-3) = 6
number of permutations of n=5,d=3 that start with "1" = S(4,4-2) = 11
[just in case you're wondering, I believe the ones starting with "1" are:
(01)(234)
(01)(243)
(201)(34)
(301)(24)
(401)(23)
(2301)(4)
(2401)(3)
(3401)(2)
(3201)(4)
(4201)(3)
(4301)(2) notice what's common to all of them?]
number of permutations of n=5,d=3 that start with "2" = S(4,4-2) = 11
01234
31024 ?
permutaiton cycles (032)(4)(1)
degree = (3-1) + (1-1) + (1-1) = 2
since its degree is different, we will not apply 31024 to our calculation
(031)(24)
(0321)(4)
(0341)(2)
count = 3
def next(prefix,target):
i = len(prefix) - 1
if prefix[i] < target[i]:
prefix[i] = prefix[i] + 1
elif prefix[i] == target[i]:
prefix.append(0)
i = i + 1
while prefix[i] in prefix[0:i]:
prefix[i] = prefix[i] + 1
return prefix
def index(perm,prefix,ix):
if prefix == perm:
print ix
else:
permD = degree(cycles(perm))
prefixD = degree(cycles(prefix))
n = len(perm) - len(prefix)
k = n - (permD - prefixD)
if prefix != perm[0:len(prefix)] and permD >= prefixD:
ix = ix + S[n][k]
index(perm,next(prefix,perm),ix)
S = [[1]
,[0,1]
,[0,1,1]
,[0,2,3,1]
,[0,6,11,6,1]
,[0,24,50,35,10,1]]
C:\pypy>pypy test.py REM print(index([3,1,0,4,2],[0],0))
31
C:\pypy>pypy davids_rank.py REM print(rank(5,{3},[3,1,0,2,4]))
31
using System;
using System.Collections.Generic;
namespace WpfPermutations
{
public class PermutationOuelletLexico3<T>
{
// ************************************************************************
private T[] _sortedValues;
private bool[] _valueUsed;
public readonly long MaxIndex; // long to support 20! or less
// ************************************************************************
public PermutationOuelletLexico3(T[] sortedValues)
{
if (sortedValues.Length <= 0)
{
throw new ArgumentException("sortedValues.Lenght should be greater than 0");
}
_sortedValues = sortedValues;
Result = new T[_sortedValues.Length];
_valueUsed = new bool[_sortedValues.Length];
MaxIndex = Factorial.GetFactorial(_sortedValues.Length);
}
// ************************************************************************
public T[] Result { get; private set; }
// ************************************************************************
/// <summary>
/// Return the permutation relative to the index received, according to
/// _sortedValues.
/// Sort Index is 0 based and should be less than MaxIndex. Otherwise you get an exception.
/// </summary>
/// <param name="sortIndex"></param>
/// <param name="result">Value is not used as inpu, only as output. Re-use buffer in order to save memory</param>
/// <returns></returns>
public void GetValuesForIndex(long sortIndex)
{
int size = _sortedValues.Length;
if (sortIndex < 0)
{
throw new ArgumentException("sortIndex should be greater or equal to 0.");
}
if (sortIndex >= MaxIndex)
{
throw new ArgumentException("sortIndex should be less than factorial(the lenght of items)");
}
for (int n = 0; n < _valueUsed.Length; n++)
{
_valueUsed[n] = false;
}
long factorielLower = MaxIndex;
for (int index = 0; index < size; index++)
{
long factorielBigger = factorielLower;
factorielLower = Factorial.GetFactorial(size - index - 1); // factorielBigger / inverseIndex;
int resultItemIndex = (int)(sortIndex % factorielBigger / factorielLower);
int correctedResultItemIndex = 0;
for(;;)
{
if (! _valueUsed[correctedResultItemIndex])
{
resultItemIndex--;
if (resultItemIndex < 0)
{
break;
}
}
correctedResultItemIndex++;
}
Result[index] = _sortedValues[correctedResultItemIndex];
_valueUsed[correctedResultItemIndex] = true;
}
}
// ************************************************************************
/// <summary>
/// Calc the index, relative to _sortedValues, of the permutation received
/// as argument. Returned index is 0 based.
/// </summary>
/// <param name="values"></param>
/// <returns></returns>
public long GetIndexOfValues(T[] values)
{
int size = _sortedValues.Length;
long valuesIndex = 0;
List<T> valuesLeft = new List<T>(_sortedValues);
for (int index = 0; index < size; index++)
{
long indexFactorial = Factorial.GetFactorial(size - 1 - index);
T value = values[index];
int indexCorrected = valuesLeft.IndexOf(value);
valuesIndex = valuesIndex + (indexCorrected * indexFactorial);
valuesLeft.Remove(value);
}
return valuesIndex;
}
// ************************************************************************
}
}