aboutsummaryrefslogtreecommitdiff
path: root/lib/knapsack_solver/solving_methods/fptas.rb
blob: 55ee52f9be0460cfd46efe58309b18ca3ac733e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
require 'knapsack_solver/solving_methods/dynamic_programming'

module KnapsackSolver
  # This class implements methods for solving 0/1 knapsack problem using Fully
  # Polynomial Time Approximation Scheme.
  class Fptas
    # Initializes 0/1 knapsack FPTAS solver.
    #
    # @param instance [Instance] Instance of a 0/1 knapsack problem.
    # @param epsilon [Instances] Maximum allowed relative error of the resulting price.
    def initialize(instance, epsilon)
      @instance = instance
      @epsilon = epsilon.to_f
      @orig_prices = @instance.things.map(&:price)
    end

    # Solve the instance of 0/1 knapsack problem using FPTAS.
    #
    # @return [Hash] resulting price and thing configuration (0 = thing is not in the knapsack, 1 = thing is there)
    def run
      modify_prices_for_epsilon!
      r = DynamicProgramming.new(@instance).run
      p = get_normal_price_from_fptas(r[:config])
      { price: p, config: r[:config] }
    end

    protected

    # Modifies prices of the things according to the supplied epsilon constant
    # to achieve max. allowed relative error.
    def modify_prices_for_epsilon!
      m = @instance.things.max_by(&:price).price
      k = (@epsilon * m) / @instance.things.size
      @instance.things.each { |t| t.price = (t.price.to_f / k).floor }
    end

    # Computes resulting price using original unmodified prices of things.
    #
    # @param presenve [Array] configuration variables vector
    # @return [Integer] total price of things in the knapsack
    def get_normal_price_from_fptas(presence)
      @instance.things.reduce(0) do |price, t|
        price + ((presence[t.index] != 0 ? @orig_prices[t.index] : 0))
      end
    end
  end
end