Reputation: 184
need algorithm to map weighted objects to their amount and need to make amount minimal for each object with keeping ratio between weights
example 1:
input: object1: 40, object2: 60, object3: 80
output: object1: 2, object2: 3, object3: 4
this can be solved by dividing object weight with gcd of weights of all objects
example 2:
input: object1: 3, object2: 15
output: object1: 1, object2: 5
example 3:
input: object1: 13, object2: 97, object3: 20
output: object1: 1, object2: 7, object3: 2
example 4:
input: object1: 1, object2: 17, object3: 97
output: object1: 0, object2: 1, object3: 5
gcd is not applicable for example 3 and 4, what's the algorithm can be used, is there any idea?
limitations: range of weights 0-99, maximum sum of all amounts is 32
Upvotes: 3
Views: 107
Reputation: 9065
As I mentioned in comments, dividing by the GCD is the best you can do if you need integers that exactly preserve the ratio.
For floats that are very close to the ratio, divide everything by the min.
Ruby example:
def f(weights)
min_wt = weights.min
ans = []
weights.each do |wt|
ans.append(wt.to_f/min_wt)
end
return ans
end
> f([40, 60, 80])
=> [1.0, 1.5, 2.0]
> f([13, 97, 20])
=> [1.0, 7.461538461538462, 1.5384615384615385]
Alternate approach to get integers: Check every scaling factor in your range (final sum 1-32). I'm taking 1 as the floor for each integer since dividing by 0 is undefined.
Ruby code (not beautifully written):
def f(unsorted_weights)
weights = unsorted_weights.sort!
orig_sum_of_wts = weights.sum
best_error = Float::INFINITY
best_sum_of_wts = 0
1.upto(32) do |new_sum_of_wts|
error = 0.0
new_wts = []
0.upto(weights.length - 1) do |i|
new_wts[i] = [1, weights[i] * new_sum_of_wts / orig_sum_of_wts].max
end
0.upto(weights.length - 2) do |i|
new_wt_i = weights[i] * new_sum_of_wts / orig_sum_of_wts
(i+1).upto(weights.length - 1) do |j|
new_wt_j = weights[j] * new_sum_of_wts / orig_sum_of_wts
error += (new_wts[j].to_f / [new_wts[i], 1.0].max - weights[j].to_f / [weights[i], 1.0].max).abs
end
if error < best_error
best_sum_of_wts = new_sum_of_wts
best_error = error
end
end
end
ans = []
0.upto(weights.length - 1) do |i|
ans.append([1, weights[i] * best_sum_of_wts / orig_sum_of_wts].max)
end
puts "#{ans.to_s}"
end
Results:
> f([40, 60, 80])
[2, 3, 4]
> f([40, 60])
[2, 3]
> f([13, 97, 20])
[2, 3, 15]
> f([1, 17, 97])
[1, 4, 26]
For 13, 20, 97, I get 2,3,15 vs your 1,2,7.
Ratios: 20/13 = 1.538, 3/2 = 1.500, 2/1 = 2.000
97/13 = 7.462, 15/2 = 7.500, 7/1 = 7.000
97/20 = 4.850, 15/3 = 5.000, 7/2 = 3.500
Cumulative error for 2,3,15: 0.038 + 0.038 + 0.150 = 0.226
Cumulative error for 1,2,7: 0.038 + 0.462 + 1.350 = 2.274
Upvotes: 2