是否有算法可以查找一系列独特元素的所有可能排列,遵循此规则?
从给定的排列中,必须通过正好循环3个元素来找到下一个排列。它们可以是任何三个元素。
使用这样的3个周期,只能找到所有排列的子集,但是 应找到所有可能通过3个周期到达的那些,并且在找到所有可到达的排列之前,不应找到相同的排列两次。
以下是输入示例:
1,2,3,4,5
输出可能是:
3,1,2,4,5
2,3,1,4,5
4,2,1,3,5
3,4,1,2,5
1,3,4,2,5
4,1,3,2,5
2,4,3,1,5
......等等。
我尝试生成这样一个序列的众多算法之一如下(对于数组 a 和length n ):
print (a)
for i = 0 to n-1
for j = i+1 to n-1
for l = j+2 to n-1
for k = j+1 to l
cycle a[i],a[j],a[k]
print (a)
cycle a[i],a[j],a[k]
print (a)
这会生成上面打印的系列,但接着继续:
1,2,3,4,5
..这是已经输出的排列。到目前为止我找到的任何其他找到下一个3周期的算法都找不到所有可达排列。
答案 0 :(得分:7)
有一篇旧论文 V. L. Kompel'makher and V. A. Liskovets. Sequential generation of arrangements by a basis of transpositions.,它表明你可以通过简单的换位生成所有排列,并且每个换位都必须将置换的第一个元素与任何其他元素交换(所谓的星形基础) )。例如对于S(3),因为第一个元素(与元素1相对)在每一步都被交换。
123->213->312->132->231->321->[123, Hamiltonian cycle!]
还有一种类似的方法A `Hot Potato' Gray Code for Permutations,它不在付费墙后面。本文的一个重要见解是,即使必须交换每个转置元素1,仍然可以生成所有排列而不重复(每一步都交换元素1):
123->213->231->132->312->321->[123, Hamiltonian cycle!]
循环通过星形基础的所有排列的另一种算法是来自Knuths" The Art of computer programming",Chapter" Generating all permutations&#34 ;.算法被称为" Ehrlich的交换方法"。我没有声称理解那里发生了什么,它只是将算法转换为java。最有趣的部分就是这一行:
//swap values to get next permutation:
swap(per,0,b[k]);
在每个步骤中都有一个转置,并且在每个转置中交换元素[0]( - >星形基础)。
import java.util.Arrays;
public class EhrlichPermuter {
//Follows Knuths "The Art of computer programming", Chapter "Generating all permutations", "Ehrlich's swap method".
int n;
int[] c;
int[] b;
int[] per;
boolean done;
void initialize(){
c=new int[n];
b=new int[n];
per=new int[n];
for(int j=0;j<n;j++){
b[j]=j;
per[j]=j;
}
}
EhrlichPermuter(int n){
this.n=n;
initialize();
}
void swap(int[] a, int i, int j){
int h=a[i];a[i]=a[j];a[j]=h;
}
int[] getNextPermut(){
int[] result=Arrays.copyOf(per, per.length);//remember permutation
int k=1;
while(c[k]>=k){
c[k]=0;
k++;
if(k==n){
done=true;
initialize();//we got all permutations so far
return result;//return the last permutation
}
}
c[k]=c[k]+1;
//swap values to get next permutation:
swap(per,0,b[k]);
//flip:
int j=1; k--;
while(j<k){
swap(b,j,k);
j++;
k--;
}
return result;//return remembered permutation
}
}
现在已经完成了很多事情!
最后一步是:形式(1a)(1b)的任何两个连续转置可以写为3个元素周期(1ab)。因此,您只需跳过具有负奇偶校验的排列。对于Hot-Potato,这看起来如下
123 --(213)-->231--(132)-->312--(321)-->[123, Hamiltonian cycle!]
跳过()中的排列。
答案 1 :(得分:2)
在我之前对这个问题的回答中,我描述了一种方法来查找相同方向的3元素旋转序列,这些旋转将生成N个元素的所有(可到达的)排列。我在这个方法中找到的最简单的序列用于下面的实现。每个元素的旋转显示重复模式,仅对N的奇数/偶数值不同;这意味着可以很容易地为任意数量的元素生成旋转序列。
N=3: (0,1,2) (0,1,2)
N=4: (0,1,3) (1,2,3) (1,2,3)
N=5: (0,3,4) (0,3,4) (0,3,4) (0,3,4)
N=6: (0,1,5) (0,2,5) (0,2,5) (0,2,5) (0,2,5)
N=7: (0,5,6) (0,5,6) (0,5,6) (0,5,6) (0,5,6) (0,5,6)
N=8: (0,1,7) (0,2,7) (0,3,7) (0,4,7) (0,4,7) (0,4,7) (0,4,7)
...
从5个元素开始,你会发现这种反复出现的模式:
N=odd: (0,N-2,N-1) (0,N-2,N-1) (0,N-2,N-1) ...
N=even: (0,1,N-1) (0,2,N-1) (0,3,N-1) ... (0,N-4,N-1) (0,N-4,N-1) (0,N-4,N-1) (0,N-4,N-1)
或以图形方式,<
表示旋转元素的位置:
N=3 N=4 N=5 N=6 N=7 N=8 N=9 N=10 N=11 N=12
<<< <<=< <==<< <<===< <====<< <<=====< <======<< <<=======< <========<< <<=========<
<<< =<<< <==<< <=<==< <====<< <=<====< <======<< <=<======< <========<< <=<========<
=<<< <==<< <=<==< <====<< <==<===< <======<< <==<=====< <========<< <==<=======<
<==<< <=<==< <====<< <===<==< <======<< <===<====< <========<< <===<======<
<=<==< <====<< <===<==< <======<< <====<===< <========<< <====<=====<
<====<< <===<==< <======<< <=====<==< <========<< <=====<====<
<===<==< <======<< <=====<==< <========<< <======<===<
<======<< <=====<==< <========<< <=======<==<
<=====<==< <========<< <=======<==<
<========<< <=======<==<
<=======<==<
然后通过按此顺序运行旋转序列来完成生成排列,其中每个第n个n 被 n + 1 替换:
3,3,4,3,3,4,3,3,4,3,3, 5 中,3,3,4,3,3,4,3,3, 4,3,3, 5 下,3,3,4,3,3,4,3,3,4,3,3, 5 中,3,3, 4,3,3,4,3,3,4,3,3, 5 下,3,3,4,3,3,4,3,3,4,3,3,< b> 6 ,...
以便旋转:
(0,1,2),(0,1,2),(0,1,3),(0,1,2),(0,1,2),(1,2,3) ,(0,1,2),(0,1,2),(1,2,3),(0,1,2),(0,1,2),(0,3,4) )中,
(0,1,2),(0,1,2),(0,1,3),(0,1,2),(0,1,2),(1,2,3),(0 ,1,2),(0,1,2),(1,2,3),(0,1,2),(0,1,2),(0,3,4)下,
(0,1,2),(0,1,2),(0,1,3),(0,1,2),(0,1,2),(1,2,3),(0 ,1,2),(0,1,2),(1,2,3),(0,1,2),(0,1,2),(0,3,4)下,
(0,1,2),(0,1,2),(0,1,3),(0,1,2),(0,1,2),(1,2,3),(0 ,1,2),(0,1,2),(1,2,3),(0,1,2),(0,1,2),(0,3,4)下,
(0,1,2),(0,1,2),(0,1,3),(0,1,2),(0,1,2),(1,2,3),(0 ,1,2),(0,1,2),(1,2,3),(0,1,2),(0,1,2),(0,1,5),...
下面的代码示例生成最多到所请求的元素数的旋转序列,然后执行旋转并输出结果排列。
function rotate3permute(elems) {
// GENERATE ROTATION SEQUENCES
var pos = [,,,[[0,1],[0,1]],[[0,1],[1,2],[1,2]]];
for (var i = 5; i <= elems; i++) {
pos[i] = [];
for (var j = 1; j < i; j++) pos[i].push([0, i % 2 ? i - 2 : j < i - 4 ? j : i - 4])
}
// PREPARE INITIAL PERMUTATION AND STEP COUNTER
var perm = [0,1], step = [,,,], seq = 3;
for (var i = 2; i < elems; i++) {
perm.push(i);
step.push(0);
}
document.write(perm + "<BR>");
// EXECUTE ROTATIONS
while (seq <= elems) {
rotate(pos[seq][step[seq]++], seq - 1);
document.write(perm + "<BR>");
seq = 3;
while (step[seq] == seq - 1) step[seq++] = 0; // seq = 3,3,4,3,3,4,3,3,4,3,3,5...
}
function rotate(pair, third) {
var temp = perm[pair[0]];
perm[pair[0]] = perm[pair[1]];
perm[pair[1]] = perm[third];
perm[third] = temp;
}
}
rotate3permute(8);
注意:用更严格的语言将while (step[seq] == seq - 1)
替换为while (seq <= elems && step[seq] == seq - 1)
,以避免数组越界错误。
如上所述,要生成所有排列,而不仅仅是通过3个元素旋转可以达到的一半,输出每个排列两次,一次是原样,一次是切换前两个元素。
答案 2 :(得分:1)
我很确定我没有得到这个问题,因为听起来你已经拥有了实现它所需要的所有部分,但是这里有。请评论这听起来是否正确。
我选择了递归方法。循环3个元素的每个组合,然后递归处理新组合。只处理独特的组合。
以下是LINQPad中作为C#程序实现的代码:
var Category = Parse.Object.extend("Category");
var queryCategory = new Parse.Query(Category);
queryCategory.equalTo("objectId", $stateParams.categoryId);
queryCategory.first({
success: function(category) {
var SubCategory = Parse.Object.extend("SubCategory");
var query = new Parse.Query(SubCategory);
query.equalTo("isActive", true);
query.equalTo("categoryId", category);
query.ascending("name");
query.find(..);
}
});
输出:
12345,23145,31245,14235,42135,21435,13425,34125,41325,15324,53124,31524,12534,25134,51234,13254,32154,21335,14352,43152,31452,15432,54132,41532 ,13542,35142,51342,45312,53412,34512,42513,25413,54213,41253,12453,24153,45123,51423,14523,43521,35421,54321,42351,23451,34251,45231,52431,24531,32541 ,25341,53241,43215,32415,24315,52314,23514,35214,15243,52143,21543
答案 3 :(得分:1)
要通过在相同方向上重复旋转3个元素来生成N个元素的排列,您可以从3个元素的旋转构建4个元素的排列,然后从4个元素的5个元素的排列构建 - 元素置换,依此类推,直到达到N个元素。
E.g。 3元素的轮换如下:
012
<<< 120
<<< 201
4元素置换重复使用3元素旋转,通过第4个元素旋转的旋转交替:
0123
<<< 1203
<<< 2013
<<=< 0312
<<< 3102
<<< 1032
<<=< 0231
<<< 2301
<<< 3021
=<<< 3210
<<< 2130
<<< 1320
(其中<
是旋转元素的位置,=
是保留在原位的元素的位置。)
5元素permutaton重复4元素排列,通过第5个元素旋转的旋转交替:
01234 <=<=< 23401 <=<=< 40123 <=<=< 12340 <=<=< 34012
<<< 12034 <<< 34201 <<< 01423 <<< 23140 <<< 40312
<<< 20134 <<< 42301 <<< 14023 <<< 31240 <<< 03412
<<=< 03124 <<=< 20341 <<=< 42013 <<=< 14230 <<=< 31402
<<< 31024 <<< 03241 <<< 20413 <<< 42130 <<< 14302
<<< 10324 <<< 32041 <<< 04213 <<< 21430 <<< 43102
<<=< 02314 <<=< 24031 <<=< 41203 <<=< 13420 <<=< 30142
<<< 23014 <<< 40231 <<< 12403 <<< 34120 <<< 01342
<<< 30214 <<< 02431 <<< 24103 <<< 41320 <<< 13042
=<<< 32104 =<<< 04321 =<<< 21043 =<<< 43210 =<<< 10432
<<< 21304 <<< 43021 <<< 10243 <<< 32410 <<< 04132
<<< 13204 <<< 30421 <<< 02143 <<< 24310 <<< 41032
对于从N-1到N元素的每个步骤,有几个选项,每个选项都有自己的结束状态。因此,每个步骤由N-1个旋转定义,并且它们一起定义N元素置换;例如对于上面的例子:
3 ELEMENTS
rotations: <<< <<<
complete permutation: 012 -> 201
4 ELEMENTS
rotations: <<=< <<=< =<<<
complete permutation: 0123 -> 1320
5 ELEMENTS
rotations: <=<=< <=<=< <=<=< <=<=<
complete permutation: 01234 -> 41032
您将在上面的示例中看到,4元素排列不使用3个相同的旋转,而是<<=<
,再次<<=<
,最后=<<<
;这是因为并非所有可能的旋转组合都会产生正确的排列
要找出可用于N元素置换的旋转,请查看N-1元素置换的结束状态,并查看它包含的周期,例如:
排列0123 -> 1320
有两个周期:[031]
和[2]
,因为位置0移动到位置3,位置3移动到位置1,位置1移动到位置0,而位置2留在原地。
排列0123 -> 3210
有两个周期:[03]
和[12]
,因为0和3切换位置,以及1和2个切换位置。
案例:多个周期
要从N-1个元素置换创建N元素置换,您需要使用第N个元素从前N-1个元素旋转两个元素。检查N-1个元素排列具有哪些循环,并选择位置0处的旋转点和第二个循环中的位置。重复此旋转次数与第二个循环中的元素一样多次,然后将第二个点移动到第三个循环中的位置(如果有的话),并重复此次数与第三个循环中的元素一样多次,并且等等。使用完所有循环后,根据需要重复最后一次循环。
一个例子可以说明这一点:
N-1-permutation: 012345 -> 254301
cycles: [042], [15], [3]
0123456 ... 2543016
<<====< 5643012 ... 4103562 (positions 0 and 1, for cycle [15])
<<====< 1203564 ... 0653124 (repeat for second element in [15])
<==<==< 3654120 ... 5214360 (positions 0 and 3, for cycle [3])
<==<==< 4210365 ... 1630425 (done; repeat last rotation till end)
<==<==< 0635421 ... 3245061
<==<==< 5241063 ... 4601523
正如您将注意到的,在2个循环的情况下,旋转始终保持不变:
N-1-permutation: 012345 -> 354201
cycles: [0423], [15]
0123456 ... 3542016
<<====< 5642013 ... 2104563 (positions 0 and 1, for cycle [15])
<<====< 1304562 ... 4650132 (repeat for second element in [15])
<<====< 6250134 ... 0315624 (done; repeat last rotation till end)
<<====< 3415620 ... 5261340
<<====< 2061345 ... 1436205
<<====< 4536201 ... 6023451
案例:一个周期
找到两个位置X和Y,其中X位于Y的左侧,N-1排列将X移动到Y,而Y不是N-1排列中的最右侧位置。然后重复旋转位置X和Y与第N个元素,最后一步旋转Y和最右侧位置。
再一次,一个例子会让这个更清楚:
N-1-permutation: 012345 -> 153024
cycles: [032451]
positions X and Y: e.g. 0 and 3, because 0 moves to 3 and 3 < 5 (other option: 2 and 4)
0123456 ... 1530246
<==<==< 0536241 ... 5460321 (positions X and Y)
<==<==< 0461325 ... 4210635 (repeat until penultimate rotation)
<==<==< 0215634 ... 2350164
<==<==< 0354162 ... 3640512
<==<==< 0642513 ... 6120453
===<=<< 6125430 ... 1356240 (last rotation: position Y and right-most)
当N-1排列由与旋转相同方向的1位置移位组成时存在边缘情况;在这种情况下,在位置0和1以及位置1和2之间交替:
N-1-permutation: 012345 -> 123450
cycles: [054321]
0123456 ... 1234506
<<====< 2634501 ... 6345021
=<<===< 6415023 ... 4150263
<<====< 1350264 ... 3502614
=<<===< 3042615 ... 0426135
<<====< 4526130 ... 5261340
=<<===< 5601342 ... 6013452
以下是最多10个元素的排列示例;有很多选项,但是这个选项显示了一个有趣的重复模式,从7个元素开始(比较7和9元素以及8和10元素的旋转,以及7和9元素的结束状态):
THREE ELEMENTS (3 permutations)
012
<<< 120
<<< 201 = 1 cycle: [012] X=0, Y=1
FOUR ELEMENTS (12 permutations)
0123 ... 2013
<<=< 0312 ... 1032
<<=< 0231 ... 3021
=<<< 3210 ... 1320 = 2 cycles: [031][2] X=0, Y=2
FIVE ELEMENTS (60 permutations)
01234 ... 13204
<=<=< 23401 ... 30421
<=<=< 40123 ... 02143
<=<=< 12340 ... 24310
<=<=< 34012 ... 41032 = 3 cycles: [024][1][3] X=0, Y=1,3
SIX ELEMENTS (360 permutations)
012345 ... 410325
<<===< 150324 ... 251304
<==<=< 351402 ... 053412
<==<=< 453210 ... 154230
<==<=< 254031 ... 352041
<==<=< 052143 ... 450123 = 2 cycles: [024][135] X=0, Y=1
SEVEN ELEMENTS (2,520 permutations)
0123456 ... 4501236
<<====< 5601234 ... 2356014
<<====< 3456012 ... 0134562
<<====< 1234560 ... 5612340
<<====< 6012345 ... 3460125
<<====< 4560123 ... 1245603
<<====< 2345601 ... 6023451 = 5 cycles: [016][2][3][4][5]; X=0, Y=2,3,4,5
EIGHT ELEMENTS (20,160 permutations)
01234567 ... 60234517
<=<====< 20734516 ... 12734506
<==<===< 32764501 ... 03764521
<===<==< 43761520 ... 24761530
<====<=< 54761032 ... 35761042
<====<=< 05761243 ... 40761253
<====<=< 20761354 ... 52761304
<====<=< 32761405 ... 03761425 = 2 cycles: [0][1457263]; X=0, Y=1
NINE ELEMENTS (181,440 permutations)
012345678 ... 037614258
<<======< 387614250 ... 365281740
<<======< 605281743 ... 624708513
<<======< 234708516 ... 271530486
<<======< 761530482 ... 758463102
<<======< 528463107 ... 540126837
<<======< 470126835 ... 413872065
<<======< 153872064 ... 186057324
<<======< 846057321 ... 802345671 = 7 cycles: [018][2][3][4][5][6][7]; X=0, Y=2,3,4,5,6,7
TEN ELEMENTS (1,814,400 permutations)
0123456789 ... 8023456719
<=<======< 2093456718 ... 1293456708
<==<=====< 3298456701 ... 0398456721
<===<====< 4398156720 ... 2498156730
<====<===< 5498106732 ... 3598106742
<=====<==< 6598102743 ... 4698102753
<======<=< 7698102354 ... 5798102364
<======<=< 3798102465 ... 6398102475
<======<=< 4398102576 ... 7498102536
<======<=< 5498102637 ... 3598102647 = 2 cycles: [051483][2679]; X=0, Y=2
通过首先选择要使用的旋转,然后使用以下顺序执行旋转来完成生成排列:其中每第三个3由4替换,每四个4替换为5,每五个替换5一个6,依此类推:
3,3,4,3,3,4,3,3,4,3,3,5,3,3,4,3,3,4,3,3,4,3,3,5 ,3,3,4,3,3,4,3,3,4,3,3,5,3,3,4,3,3,4,3,3,4,3,3,5,3 ,3,4,3,3,4,3,3,4,3,3,6 ...
类似于Ehrlich的序列,因为你可以使用前2个旋转来生成3个元素的3个排列,或者前4个用于4个元素的12个排列,或者前50个用于5个元素的60个排列,或者通常:N!/ 2-1旋转以产生N!/ 2个排列。
(更新:有关实施,请参阅我对此问题的其他答案。)
正如问题和评论中所提到的,只有一半的可能排列可以使用3元素旋转生成。使用上述方法生成的每个排列都有一个不可达的伴随排列,可以通过切换前两个元素来创建,例如:
0123 (1023)
<<< 1203 (2103)
<<< 2013 (0213)
<<=< 0312 (3012)
<<< 3102 (1302)
<<< 1032 (0132)
<<=< 0231 (2031)
<<< 2301 (3201)
<<< 3021 (0321)
=<<< 3210 (2310)
<<< 2130 (1230)
<<< 1320 (3120)