跳转到内容

模組:Combination

本页使用了标题或全文手工转换
维基百科,自由的百科全书

这是本页的一个历史版本,由A2569875留言 | 贡献2018年10月28日 (日) 18:33 (提供排列組合(不含排列)、子集合编辑。这可能和当前版本存在着巨大的差异。

(差异) ←上一修订 | 最后版本 (差异) | 下一修订→ (差异)
local p = {}

function p.getCombinationGenerator()
	CombinationGenerator = {
		list = {},
		nums = {},
		count = 0,
		number_count={},
		factors={},
		factor_index={}
	}
	function CombinationGenerator:init(prime_factors,fill_count)
		self.count=fill_count
		self.nums = {}
		for first,second in pairs(prime_factors) do
			if first ~= 'has_err' then 
				self.nums[#(self.nums) + 1] = first 
				self.number_count[first] = 0
				self.factors[first] = second
			end
		end
		table.sort(self.nums)
		for i = 1,#(self.nums) do
			self.factor_index[self.nums[i]]=i
		end
	end
	function CombinationGenerator:fill()
		if self.count <= 0 then return end
		local iterator = 1
		local now_number = self.nums[iterator]
		if #(self.list) > 0 then
			iterator = self.factor_index[self.list[#(self.list)]]
			now_number = self.nums[iterator]
		end
		while #(self.list) < self.count do
			--test a number 'now_number'
			if self.number_count[now_number] < self.factors[now_number] then
				--if number 'now_number' can be put
				self.list[#(self.list) + 1] = now_number
				self.number_count[now_number] = self.number_count[now_number] + 1
			else
				--if number 'now_number' can NOT be put
				if iterator + 1 > #(self.nums) then break end
				iterator = iterator + 1
				now_number = self.nums[iterator]
			end
		end
	end
	function CombinationGenerator:checkNumberCount()
		if self.count <= 0 then return end
		for i = 1,#(self.nums) do
			self.number_count[self.nums[i]] = 0
		end
		for i = 1,#(self.list) do
			self.number_count[self.list[i]] = self.number_count[self.list[i]] + 1
		end
	end
	function CombinationGenerator:set(input)
		local result = {}
		if xpcall(function()_=#input pairs(input) end,function(_)end) == true then
			for key,value in pairs(input) do
				if tostring(tonumber(tostring(key)) or 0) == tostring(key) then 
					result[key] = value
				end
			end
			self.list = result
		end
		self:checkNumberCount()
	end
	function CombinationGenerator:next()
		if #(self.list) > 0 then
			local iterator = self.factor_index[self.list[#(self.list)]]
			local now_number = self.nums[iterator]
			if iterator + 1 > #(self.nums) then return false end
			self.number_count[now_number] = self.number_count[now_number] - 1
			iterator = iterator + 1
			now_number = self.nums[iterator]
			self.number_count[now_number] = self.number_count[now_number] + 1
			self.list[#(self.list)] = now_number
			return true
		end
		return false
	end
	function CombinationGenerator:copyListTo(newlist)
		if xpcall(function()_=#newlist pairs(newlist) end,function(_)end) == true then
			for key,value in pairs(self.list) do
				if tostring(tonumber(tostring(key)) or 0) == tostring(key) then 
					newlist[key] = value
				end
			end
		end
	end
	function CombinationGenerator:findSubset(min_ele,max_ele)
		local outside_flag = true;
		local result = {}
		local end_val = tonumber(max_ele) or -1
		if (tonumber(min_ele) or -1) <= 0 then self.count = 0 else self.count = min_ele end
		if end_val >= 0 and self.count > end_val then return result end
		
		if #(self.nums) <= 0 then 
			if self.count <= 0 then result[#result + 1] = {} end
			return result 
		end
		
		self:set({nil})
		while outside_flag == true and (end_val < 0 or self.count <= end_val) do
			result, outside_flag = self:findCombination(nil, result, outside_flag)
			self.count = self.count + 1
		end
		return result
	end
	function CombinationGenerator:findCombination(item_count, input_array, outside_flag)
		local result = input_array or {}

		if item_count ~= nil then self.count = item_count end
		
		if #(self.nums) <= 0 then 
			if self.count <= 0 then result[#result + 1] = {} end
			return result 
		end
		
		if self.count <= 0 then
			self:set({nil})
			result[#result+1] = {}
			self:copyListTo(result[#result])
		else
			self:set({nil})
			self:fill()

			local middle_flag = true;
			while middle_flag == true do
				local inside_flag = true;
				while inside_flag == true do
					if (#(self.list) < self.count) then
						outside_flag = false
						break
					end
					result[#result+1] = {}

					self:copyListTo(result[#result])
					inside_flag = self:next()
				end

				local backtrack_flag = true;
				while backtrack_flag == true do

					local recursive_test = {value = tostring(table.concat(self.list,','))}
					local delete_num = self.list[#(self.list)]
					self.number_count[delete_num] = self.number_count[delete_num] - 1
					self.list[#(self.list)] = nil
					recursive_test.count = #(self.list)
					if #(self.list) <= 0 then break end
					middle_flag = self:next()

					self:fill()

					local in_check,_ = mw.ustring.sub( tostring(table.concat(self.list,',')), 1, #tostring(recursive_test.value))

					if ( 
						tostring(in_check) == recursive_test.value 
					) == true then
						while #(self.list) > recursive_test.count do
							self.list[#(self.list)] = nil
							self:checkNumberCount();
						end
					end

					backtrack_flag = (#(self.list) < self.count) or #(self.list) <= 0
				end
				if #(self.list) <= 0 then break end
			end
		end
		return result, outside_flag
	end
	return CombinationGenerator
end

return p