| # |
| # iExploder Combination Scanner Library (used for subtesting) |
| # |
| # Copyright 2010 Thomas Stromberg - All Rights Reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| |
| # This is a simple sequential combination creator with a constantly growing width |
| def seq_combo_creator(total_lines, width, offset) |
| # Offsets start at 0 to total_lines-1 |
| use_lines = [] |
| offset.upto(offset+width-1) do |line_number| |
| use_lines << line_number |
| end |
| |
| if use_lines[-1] == total_lines-1 |
| width += 1 |
| next_offset = 0 |
| else |
| next_offset = offset + 1 |
| end |
| return [width, next_offset, use_lines] |
| end |
| |
| # This tries all combinations, giving a small test-case, but requires lots of |
| # subtests. |
| def combine_combo_creator(total_lines, width, offsets) |
| # puts "Asked: Total Lines: #{total_lines} Line Count: #{width} Offsets: #{offsets.join(',')}" |
| if not offsets or offsets.length == 0 |
| # puts " Setting offsets to 0" |
| offsets = [0,] |
| end |
| if width < 1 |
| width = 1 |
| end |
| |
| index = 0 |
| lines = [] |
| new_offsets = [] |
| reset_next_offset = false |
| |
| # Reverse the order from fastest to slowest |
| offsets.each_with_index do |offset, index| |
| 0.upto(width-1) do |line_offset| |
| lines << (offset + line_offset) |
| end |
| if lines[-1] >= total_lines - 1 |
| # If we are the slowest, that means we are done with this iteration. |
| if index == offsets.length - 1 |
| new_offset_count = offsets.length + 1 |
| # Loosely follow the Fibonacci sequence when calculating width |
| width = (new_offset_count * 1.61803398).round |
| new_offsets = [] |
| # 0 to offsets.length creates one additional offset |
| 0.upto(offsets.length) do |new_offset_num| |
| new_offsets << (new_offset_num * width) |
| end |
| |
| # We need the lowest offset first. Oops. |
| new_offsets.reverse! |
| else |
| # Move to the next available slot.. next offset will take the one before. |
| new_offsets << offsets[index+1] + (width * 2) |
| reset_next_offset = true |
| end |
| elsif reset_next_offset |
| new_offsets << (offset + width) |
| reset_next_offset = false |
| # The first one always rotates |
| elsif index == 0 |
| new_offsets << (offset + width) |
| reset_next_offset = false |
| # The others stay still normally. |
| else |
| new_offsets << offset |
| reset_next_offset = false |
| end |
| end |
| |
| return [width, new_offsets, lines] |
| end |
| |
| def avg(list) |
| sum = list.inject(0.0) { |sum, el| sum += el } |
| return sum / list.length |
| end |
| |
| |
| # for testing ################################################################# |
| if $0 == __FILE__ |
| line_count = ARGV[0].to_i || 100 |
| try_count = ARGV[1].to_i || 10 |
| |
| seq_iterations = [] |
| combine_iterations = [] |
| seq_size = [] |
| combine_size = [] |
| |
| 1.upto(try_count) do |run| |
| puts "*" * 78 |
| puts "# RUN #{run} (line-count: #{line_count})" |
| find_lines = [] |
| 0.upto(rand(4)) do |count| |
| choice = rand(line_count).to_i |
| if ! find_lines.include?(choice) |
| find_lines << choice |
| end |
| end |
| |
| lines = [] |
| width = 1 |
| offset = 0 |
| attempts = 0 |
| puts "Find lines: #{find_lines.join(',')}" |
| while not find_lines.all? { |x| lines.include?(x) } |
| (width, offset, lines) = seq_combo_creator(line_count, width, offset) |
| attempts += 1 |
| end |
| puts "Sequential found #{find_lines.join(',')} in #{attempts} attempts with #{lines.length} total lines." |
| seq_iterations << attempts |
| seq_size << lines.length |
| |
| lines = [] |
| width = 1 |
| offsets = [] |
| attempts = 0 |
| while not find_lines.all? { |x| lines.include?(x) } |
| # puts "Looking for #{find_lines.join(',')}" |
| (width, offsets, lines) = combine_combo_creator(line_count, width, offsets) |
| attempts += 1 |
| end |
| puts "Combine found #{find_lines.join(',')} in #{attempts} attempts with #{lines.length} total lines." |
| combine_iterations << attempts |
| combine_size << lines.length |
| end |
| puts "-" * 78 |
| puts "Seq avg iterations=#{avg(seq_iterations).to_i} length=#{avg(seq_size).to_i}" |
| puts "combine avg iterations=#{avg(combine_iterations).to_i} length=#{avg(combine_size).to_i}" |
| diff_iter = (avg(combine_iterations) / avg(seq_iterations)) * 100 |
| diff_lines = (avg(combine_size) / avg(seq_size)) * 100 |
| puts "Diff iterations: #{diff_iter}%" |
| puts "Diff lines: #{diff_lines}%" |
| end |