非递归实现不重复序列的全排列(三)

笔者曾利用进制转换实现不重复序列全排列( http://blog.csdn.net/northwolves/archive/2004/07/21/47400.aspx ),但从0 循环到n^(n-1)-1,效率实在不高,经过仔细分析,发现一个另人激动的规律,详情见下表:

A

|

B A

|

C BA

|

D CBA

---|---|---|---

C D BA

CB D A

CBA D

B C A

|

D BCA

B D CA

BC D A

BCA D

BA C

|

D BAC

B D AC

BA D C

BAC D

A B

|

C AB

|

D CAB

C D AB

CA D B

CAB D

A C B

|

D ACB

A D CB

AC D B

ACB D

AB C

|

D ABC

A D BC

AB D C

ABC D

从上面表格可以看出,对于“ABCD”,假如先放好A(只有一种放法),再放B时,可以有BA,AB两种放法;再放C时,则针对BA,AB 各有3种放法(BA前,BA中,BA后),再放D时,各有4种放法。所以第一个元素排好后,第2个元素的位置可以用0,1 表示,第3个元素的位置可以用0,1 ,2表示,第n个元素的位置可以用0,1 ,2,3,...n-1表示,因而使用混合进制(笔者起的名字)可以实现数组元素的全排列。

代码如下:

Sub pailie3(ParamArray x())
Dim starttime As Single, endtime As Single
Dim i As Integer, j As Integer, Num As Long, n As Integer
Dim ALL As New Collection, TEMP1 As Long, TEMP2 As Long
n = UBound(x) + 1 '元素个数
starttime = Timer '开始计时
Num = 1
For i = 1 To n
Num = Num * i '递归计算n!
Next
For i = 1 To Num
Set ALL = Nothing '初始化集合all
ALL.Add x(0)
TEMP1 = i
For j = 2 To n
TEMP2 = TEMP1 Mod j
TEMP1 = TEMP1 \ j
If TEMP2 = 0 Then
ALL.Add x(j - 1) 'temp2为 0则放在最后
Else
ALL.Add x(j - 1), , BEFORE:=TEMP2 'temp2不为 0 则置于第temp2个元素前
End If
Next
For j = 1 To n
Debug.Print ALL(j) & " "; '输出
Next
Debug.Print
Next
endtime = Timer
Debug.Print "共 " & Num & " 种排列!用时 " & endtime - starttime & " 秒!"
End Sub

Private Sub Command1_Click()
pailie3 "a", "b", "c", "d", "e", "f", "g"
End Sub

由于集合属于VARIANT类型,运算速度慢,换成数组进行同样的转换,发现确实快了很多:

Sub pailie4(ParamArray x())
Dim starttime As Single, endtime As Single
Dim i As Integer, j As Integer, k As Integer, Num As Long, n As Integer
Dim ALL(), TEMP1 As Long, TEMP2 As Long
n = UBound(x) + 1 '元素个数

starttime = Timer '开始计时
Num = 1
For i = 1 To n
Num = Num * i '递归计算n!
Next
For i = 1 To Num
ReDim ALL(1 To n) '初始化数组all
ALL(1) = x(0)
TEMP1 = i
For j = 2 To n
TEMP2 = TEMP1 Mod j
TEMP1 = TEMP1 \ j
If TEMP2 = 0 Then
ALL(j) = x(j - 1) 'temp2为 0则放在最后
Else
For k = j To TEMP2 + 1 Step -1
ALL(k) = ALL(k - 1) ' temp2之后的元素后移一位
Next
ALL(TEMP2) = x(j - 1) 'temp2不为 0 则置于第temp2个元素前
End If
Next
Debug.Print Join(ALL, " ") '输出
Next
endtime = Timer
Debug.Print "共 " & Num & " 种排列!用时 " & endtime - starttime & " 秒!"
End Sub
Private Sub Command1_Click()
pailie4 "a", "b", "c", "d", "e", "f", "g"
End Sub

如果用COPYMEMORY进行数组的移动,速度应该更快,大家有兴趣不妨一试。

Published At
Categories with Web编程
Tagged with
comments powered by Disqus