我們將使用分治法實現一個全排列演算法。先來看一下演算法實現後的效果:['a','b','c'].permutation #=> [["a", "b", "c"],
# ["a", "c", "b"],
# ["b", "a", "c"],
# ["b", "c", "a"],
# ["c", "a", "b"],
# ["c", "b", "a"]]
演算法描述
分治法求解問題分為三個步驟:
- 分解:將問題分為若干個子問題。
- 解決:遞迴地求解每個子問題。
- 合并:將每個子問題的解合并成為整個問題的解。
現在我們需要求具有n個元素的數組A的全排列。例如:大小為3的數組A=[a,b,c] (為方便起見,我把引號全都省略了,其實應該是A=['a','b','c']。下同),它的全排列為:
[[a,b,c],
[a,c,b],
[b,a,c],
[b,c,a],
[c,a,b],
[c,b,a]]
這是一個大小為 n!*n 的二維數組。
使用分治演算法求解全排列的過程如下
- 分解:將數組分為子數組 A[1..k-1] 和一個元素 A[k]。 (1≤k≤n)
- 解決:遞迴地求解每個子數組 A[1..k-1] 的全排列,直至子數組A[1..k-1]為空白時結束遞迴。
- 合并:將上一步的結果---A[1..k-1]的全排列(一個二維數組)與元素A[k]合并,得出A[1..k]的全排列。例如:
[[]] 與 a 合并得到 [[a]]
[[a]] 與 b 合并得到 [[a,b], [b,a]]
[[a,b],[b,a]] 與 c 合并得到 [[a,b,c],[a,c,b],[c,a,b],[b,c,a],[c,a,b],[c,b,a]]
看下面的圖示會更直觀一些
1. 分解過程 [a,b,c]
/ \
[a,b] c
/ \
[a] b
/ \
[] a
2. 合并過程[] a
\ /
[[a]] b
\ /
[[a,b],[b,a]] c
\ /
[[a,b,c],
[a,c,b],
[c,a,b],
[b,a,c],
[b,c,a],
[c,b,a]]
原始碼
下面是ruby的原始碼(檔案名稱“permutation.rb”)class Array
# Returns permutation(a new two-dimentional array) of self.
# Example
# - [].permutation #=> [[]]
# - ['A'].permutation #=> [['A']]
# - ['A','B'].permutation #=> [['A','B'],['B','A']]
# - ['A','B','C'].permutation #=> [['A','B','C'],
# ['A','C','B'],
# ['B','A','C'],
# ['B','C','A'],
# ['C','A','B'],
# ['C','B','A']]
def permutation()
permutation_i(self.length-1).sort!
end
private
def permutation_i(i)
if i> 0
return Array.merge_permutation(permutation_i(i-1), self[i])
else
return Array.merge_permutation([[]], self[i])
end
end
# Example
# - Array.merge_permutation([[]], 'A') #=> [['A']]
# - Array.merge_permutation([['A']], 'B') #=> [['B','A'],['A','B']]
# - Array.merge_permutation([['B','A'],['A','B']], 'C') #=> [['C','B','A'],
# ['B','C','A'],
# ['B','A','C'],
# ['C','B','A'],
# ['A','C','B'],
# ['A','B','C']]
def Array.merge_permutation(src, item)
result = Array.new
src.each do |each_array|
result += Array.item_permutation(each_array, item)
end
return result
end
# Returns a new two-dimentional array. Inserts "item" in each pertential place in "src".
# Example
# - Array.item_permutation([], 'A') #=> [['A']]
# - Array.item_permutation(['A'], 'B') #=> [['B','A'], ['A','B']]
# - Array.item_permutation(['A','B'], 'C') #=> [['C','A','B'], ['A','C','B'], 'A','B','C']]
def Array.item_permutation(src, item)
result = Array.new
(0..src.length).each do |i|
new_array = src.collect
new_array.insert(i, item)
result << new_array
end
return result
end
end
require 'pp_extension'
pp(['a','b','c'].permutation)
原始碼用於輸出二維數組的“pp_extension”可以看我的這篇文章讓ruby以矩陣的樣式輸出二維數組
原始碼如下(檔案名稱"pp_extension.rb")require 'pp'
# Outputs two-dimension array like matrix
class Array
def pretty_print(q)
q.group(1, '[', ']') {
q.seplist(self) {|v|
q.current_group.break if v.is_a?(Array) && !q.current_group.first? # added by me
q.pp v
}
}
end
end
已耗用時間
演算法已耗用時間 T(n) = T(n-1) + Θ(n2) = Θ(n3)
注 本文並不是從教科書上抄下來的,而是我閉門造車的結果,所以很可能有不正之處,請不吝賜教。