从映射到排列的数字,如何生成与先前的perm不同的其他排列。通过交换元素?

时间:2013-12-04 22:50:26

标签: c# algorithm math permutation factorial

使用Lehmer code,可以使用permutation对N个元素序列中的任何factorial number system进行编码并映射为十进制数。

示例:

Lehmer编码ABCD排列:

ABDC => 0010
CBAD => 2100
DCBA => 3210

可以使用阶乘法将这些inversions vectors转换为小数:

2100 => 2 x 3! + 1 x 2! + 0 x 1! + 0 x 0!
     => 2 x 6  + 1 x 2  + 0 x 1  + 0 x 1
     => 14

因此,CBAD排列可以直接映射到数字14

我的问题是:

从一个映射到置换的数字,是否有一种计算有效的方法通过交换序列中的两个元素来生成与先前的置换不同的其他排列数?


示例: 我们有4个(映射到ADBC),我们想要交换前两个元素。结果是18(或DABC)。

4    => 18
0200 => 3000
ADBC => DABC

方法将被声明为:

swap(4, 0, 1); //return 18

我想避免再次完成整个过程,但要反过来:

Number => Factorial => Rebuild original permutation and swap elements (costly) =>
  Factorial => Number

注意: 在维基百科上有关于Steinhaus–Johnson–Trotter algorithm的文章 但我不确定这会有所帮助。

1 个答案:

答案 0 :(得分:2)

(回答我自己的问题)

我终于发现了。我花了一段时间,但这是最终公式:

int swap(int value, int indexA, int indexB)
{
   int valueA = value % factorial(indexA+1) / factorial(indexA);
   int valueB = value % factorial(indexB+1) / factorial(indexB);

   int deltaA = valueB - valueA;
   if (valueB >= valueA) deltaA++;

   int deltaB = valueA - valueB;
   if (valueA > valueB) deltaB--;

   return value + (deltaA * factorial(indexA)) + (deltaB * factorial(indexB));
}

//note: indexes have to be given from right to left
//so to swap elements at 0, 1 for 4 : swap(4, 3, 2); 

一些解释:

举个例子,我们来14。阶乘表示是:

2 x 3! + 1 x 2! + 0 x 1! + 0 x 0!

如果我们要交换序列的前两个元素(CBAD => BCAD2100 => 1100),我们需要{因子中的{1}}和2项,并交换它们:

1

注意:由于阶乘表达式只是一个总和,我们不需要重新评估完整表达式,只是为了应用一些增量:

1 x 3! + 2 x 2! + 0 x 1! + 0 x 0!

因子项的提取在前两行代码中完成,delta应用于最后一行。

注意:我们需要注意,因为我们交换了元素,Lehmer代码中的索引可能已更改,并且可选地在factorial中添加1或删除1:

  1 x 3! + 2 x 2! + 0 x 1! + 0 x 0!

= 2 x 3! + 1 x 2! + 0 x 1! + 0 x 0! - ( 1 x 3!) + ( 1 x 2!)

= 14 - ( 1 x 3!) + ( 1 x 2!)

这最后一部分是在c#code

中的两个“if”条件下完成的