随笔 - 123  文章 - 24 评论 - 2701 阅读 - 101万

我们将使用分治法实现一个全排列算法。先来看一下算法实现后的效果:
['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)

注 本文并不是从教科书上抄下来的,而是我闭门造车的结果,所以很可能有不正之处,请不吝赐教。
posted on   1-2-3  阅读(3274)  评论(10编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示