非递归实现不重复序列的全排列(三)
?笔者曾利用进制转换实现不重复序列全排列(http://blog.csdn.net/northwolves/archive/2004/07/21/47400.aspx),但从0 循环到n^(n-1)-1,效率实在不高,经过仔细分析,发现一个另人激动的规律,详情见下表:
?
A | BA | CBA | DCBA |
CDBA | |||
CBDA | |||
CBAD | |||
BCA | DBCA | ||
BDCA | |||
BCDA | |||
BCAD | |||
BAC | DBAC | ||
BDAC | |||
BADC | |||
BACD | |||
AB | CAB | DCAB | |
CDAB | |||
CADB | |||
CABD | |||
ACB | DACB | ||
ADCB | |||
ACDB | |||
ACBD | |||
ABC | DABC | ||
ADBC | |||
ABDC | |||
ABCD |
从上面表格可以看出,对于“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进行数组的移动,速度应该更快,大家有兴趣不妨一试。