基于补集最大值思想的排列组合
Posted uu6crella
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于补集最大值思想的排列组合相关的知识,希望对你有一定的参考价值。
基于补集最大值思想的排列组合。
就组合数列CombList写了文档,感觉跟ruby写的代码本身差不多。
组合数添加:文档
假设为C(n, m)模式。当前列表为@list,有m个元素。
@model是1~n所有整数构成的数组
delta数组是@model减去@list的所有元素。
@list从后往前找到第一个元素A,其位置为ji,使得A<delta数组内的最大值B(应该是delta数组的最后一位)
若存在B,则找出delta数组内所有比A大的元素集合中的最小值C。
把@list的ji位置替换为C元素。
对于更新后的@list从位置0到ji的子集D,获取@model-D的数组中所有大于C的元素组成的集合E。
如果E为空,则@list从ji到(m-1)位置上所有的数是n、n-1、n-2 ...,此时@list符合条件。直接跳出流程
如果E不为空,则把@list从(ji+1)到(m-1)位置,依次设为E[0]、E[1]..。
若不存在B, 则继续从后往前找到对应的元素A
如果整个@list都找不到符合条件的A,则@list已是本次组合中的最大倒序排列: [n, n-1, n-2 ...]。直接跳出流程。
# 文档结束
调用代码:
1 require ‘./permlist.rb‘ 2 a = Time.now 3 # 排列数Anm, n=13, m=7,在13个元素中找出任意集合A,满足A的元素个数为7 4 #~ list = Sanla::PermList.new(13, 7) # => list.pos=8648640=13!/6!,时间应该在34秒以内 5 list = Sanla::PermList.new(8, 4) 6 # 组合数Cnm, n=7, m=3,在7个元素中找出任意非重复集合A,满足A的元素个数为3 7 #~ list = Sanla::CombList.new(7, 3) 8 loop do 9 r = list.get 10 11 if (r == false) 12 break 13 end 14 #~ puts r.inspect 15 #~ puts "获取结果:#{r}"#, pos=#{list.pos}" 16 #~ fio.puts "获取结果:#{r}" 17 end 18 #~ fio.close 19 b = Time.now 20 #~ puts ‘waiting..‘; gets 21 puts list.pos 22 puts "time: #{(b-a).to_i}"
生成数列的模块代码:
# ruby API说明 # (1..n).to_a 表示从1到n之间(含边界)所有整数组成的数组 # array.empty? 数组是否为空, 返回true或false # array.detect{|x| bool} 从数组中找出第一个满足bool为true的元素; 若不存在,返回nil # array[j, k]表示从array[j]位置开始取第1个数,往后一直取数直到取了k个,然后返回这k个元素组成的数组 module Sanla class PermList # 排列数 attr_reader :pos def initialize(n, m) if (m > n || m*n == 0) raise StandardError, "求排列数时参数错误: n=#{n}, m=#{m}" end @n = n; @m = m @list = (1..m).to_a @model = (1..n).to_a #~ @list[-1] -= 1 # 方便第一次输出get() @pos = 0 end def get if (@list == false) return false end if (@pos==0) @pos += 1 else self.add() if (@list != false) @pos += 1 end end return @list end def add ji = @m-1 while (ji>=0) # 获取数组开始到当前位的所有元素 cur = @list[ji] array2 = @list[0, (ji+1)] # 获取模型中减去array2剩下的元素 # 方式是把指定位置的元素设置为nil,再使用Array.compact delta = get_delta(array2) bigger = delta.detect{|x| x>cur} # 筛选出delta里比当前位大的元素 # 必须确保delta是按从小到大排列 if bigger.nil? # 当前位已是最大; 交由下一次循环操作 else # 设置当前位为bigger; @list[ji] = bigger array2[-1] = bigger delta = get_delta(array2) # @list剩下的位置依次填充delta的前几个元素 ((ji+1)..(@m-1)).each do |id| @list[id] = delta[id-(ji+1)] end # 跳出循环, 直接返回 return end #~ puts "检查ji=#{ji}, list=#{@list}" ji -= 1 end #~ puts "排列数组已是最大! #{@list}" @list = false return #~ puts "@list=#{@list}, waiting.."; gets #~ end end def get_delta(array2) delta = @model.clone array2.each do |x| delta[x-1] = nil end delta = delta.compact return delta end end class CombList # 组合数 attr_reader :pos def initialize(n, m) if (m > n || m*n == 0) raise StandardError, "求排列数时参数错误: n=#{n}, m=#{m}" end @n = n; @m = m @list = (1..m).to_a @model = (1..n).to_a @pos = 0 end def get if (@list == false) return false end if (@pos==0) @pos += 1 else self.add() if (@list != false) @pos += 1 end end return @list end =begin 组合数添加:文档 假设为C(n, m)模式。当前列表为@list,有m个元素。 @model是1~n所有整数构成的数组 delta数组是@model减去@list的所有元素。 @list从后往前找到第一个元素A,其位置为ji,使得A<delta数组内的最大值B(应该是delta数组的最后一位) 若存在B,则找出delta数组内所有比A大的元素集合中的最小值C。 把@list的ji位置替换为C元素。 对于更新后的@list从位置0到ji的子集D,获取@model-D的数组中所有大于C的元素组成的集合E。 如果E为空,则@list从ji到(m-1)位置上所有的数是n、n-1、n-2 ...,此时@list符合条件。直接跳出流程 如果E不为空,则把@list从(ji+1)到(m-1)位置,依次设为E[0]、E[1]..。 若不存在B, 则继续从后往前找到对应的元素A 如果整个@list都找不到符合条件的A,则@list已是本次组合中的最大倒序排列: [n, n-1, n-2 ...]。直接跳出流程 =end def add delta = get_delta(@list) #~ puts "add()开始时list=#{@list}, delta=#{delta}"; gets # 从@list的后往前比较,获取第一个位置A使得A<delta内的某个数 # 假设delta的最大值在[-1]位 ji = @m-1 while (ji>=0) cur = @list[ji] if (cur < delta[-1]) # 获取delta数组内大于cur的数中的最小的一个 delta.each do |x| if (x>cur) @list[ji] = x break end end # 再次获取delta数组 cur = @list[ji] #~ puts "中间 list=#{@list}" array2 = @list[0, ji] delta = get_delta(array2).select{|x| x>cur} if delta.empty? # 从ji到@list末尾构成的数组已经是组合数里的较大数的集合了 else ((ji+1)..(@m-1)).each do |x| #~ puts "delta=#{delta}, 获取id为#{x-(ji+1)}" @list[x] = delta[x-(ji+1)] end end #~ puts "ji=#{ji}, list=#{@list}"; #gets return end ji -= 1 end #~ puts "组合数组已是最大! #{@list}" @list = false return #~ puts "@list=#{@list}, waiting.."; gets #~ end end def get_delta(array2) # 返回@model数组减去array2的元素的其他元素构成的数组 delta = @model.clone begin array2.each do |x| delta[x-1] = nil end rescue NoMethodError => ser puts ser.message, "array2=#{array2}" exit end delta = delta.compact return delta end end end
以上是关于基于补集最大值思想的排列组合的主要内容,如果未能解决你的问题,请参考以下文章
Gym10081 A - Arcade Game -康托展开全排列组合数变成递推的思想