I've got an array of hashes (sorted), something like this:
testArray = [{price: 540, volume: 12},
{price: 590, volume: 18},
{price: 630, volume: 50}]
Now I want to calculate the mean value up to certain total volume. Let's say someone wants to buy 40 pieces and he wants it the cheapest way. It would mean an average price of (540*12+590*18+630*50)/40 money units.
My first attempt is following:
testArray.each do |priceHash|
#priceArray << priceHash.fetch(:price)
#volumeArray << priceHash.fetch(:volume)
end
def calculateMiddlePrice(priceArray, volumeArray, totalAmount)
result = 0
# Here some crazy wild magic happens
(0...volumeArray.count).inject(0) do |r, i|
if (volumeArray[0..i].inject(:+)) < totalAmount
r += volumeArray[i]*priceArray[i]
elsif volumeArray[0..i-1].inject(:+) < totalAmount && volumeArray[0..i].inject(:+) >= totalAmount
theRest = volumeArray[i] - (volumeArray[0..i].inject(:+) - totalAmount)
r += theRest * priceArray[i]
elsif volumeArray[0] > totalAmount
r = totalAmount * priceArray[0]
end
result = r
end
result
end
Right now I'm not even sure why it works, but it does. However this absolutely ridiculous code in my eyes.
My second thought was to cut my testArray when the total amount is achieved. The code looks better
testAmount = 31
def returnIndexForSlice(array, amount)
sum = 0
array.each_index do |index|
p sum += array[index][:volume]
if sum >= amount
return index+1
end
end
end
testArray.slice(0,returnIndexForSlice(testArray, testAmount))
Still, this just doesn't feel that right, "rubyish" if you could say so. I checked almost every method for array class, played around with bsearch, however I can't figure out a really elegant way of solving my problem.
What's crossing my mind is something like that:
amountToCheck = 31
array.some_method.with_index {|sum, index| return index if sum >= amountToCheck}
But is there such method or any other way?
Given your prices array of hashes:
prices = [ {price: 540, volume: 12},
{price: 590, volume: 18},
{price: 630, volume: 50}]
You can calculate your result in 2 steps.
def calc_price(prices, amount)
order = prices.flat_map{|item| [item[:price]] * item[:volume] } #step 1
order.first(amount).reduce(:+)/amount #step 2
end
Step 1: Create an array with each individual item in it (if the prices aren't sorted, you have to add a sort_by clause). In other words, expand the prices into a numeric array containing twelve 540's, 18 590's, etc. This uses Ruby's array repetition method: [n] * 3 = [n, n, n].
Step 2: Average the first n elements
Result:
calc_price(prices, 40)
=> 585
Related
I've spent a while on the following algorithm:
You are given coins of different denominations and a total amount of
money amount. Write a function to compute the fewest number of coins
that you need to make up that amount. If that amount of money cannot
be made up by any combination of the coins, return -1.
Example 1: coins = [1, 2, 5], amount = 11 return 3 (11 = 5 + 5 + 1)
Example 2: coins = [2], amount = 3 return -1.
Note: You may assume that you have an infinite number of each kind of
coin.
This is likely not the most efficient way to solve the problem, but I figured I can solve it by trying every coin and launching a new function per attempt, where the new function call has the updated amount. This would launch N function calls per coin... but I'll deal with that later.
Right now I'm dealing with the following issue: often when making recursive calls, I'm unable to properly code in a base case. For example, in this problem we have to return -1 if the amount of money cannot be made up by any combination of the coins. However, I also need to count up the fewest number of coins. So I figured I'd take a min variable and call 1 + new_func_call.
However, when this new_func_call ends up not working out, it passes up a -1 to the recursive call stack, which ends up making min zero instead. I'm not sure how to adjust this-- I've tried varying my code in different ways but perhaps I'm having a conceptual issue. I know why it's happening-- just don't know how to deal with it.
Sample input:
Coins: [2]
Amount: 3
My output: 0
Correct output: -1
Code:
def coin_change(coins, amount)
coin_count(coins, amount, coins.min)
end
def coin_count(coins, amount, min_coin)
with_coin = min = 1.0/0
return 0 if amount == 0
return -1 if amount < min_coin
i = 0
while i < coins.length
with_coin = 1 + coin_count(coins, amount - coins[i], min) if amount - coins[i] >= 0
min = [min, with_coin].min
i += 1
end
min
end
Right now I'm dealing with the following issue: often when making recursive calls, I'm unable to properly code in a base case. For example, in this problem we have to return -1 if the amount of money cannot be made up by any combination of the coins. However, I also need to count up the fewest number of coins.
Well you sort of have two base cases here
If the amount state variable is zero, we are done counting, so return the count
If we run out of coins (xs) to count with and still didn't reach a zero-amount, then return -1
Otherwise we have the two recursion cases
oops: amount is less than 0 meaning the last coin (x) subtracted was too big – rewind and recurse without using this coin
default case: add 1 to count, subtract the coin (x) from amount, and recurse using the same set of coins (xs)
The only requirement for this to work is that the coins are first sorted in descending order
OK, so all of this is easily encoded in Ruby using an auxiliary helper (aux) to hold our state variables. Remember to initialize with a count of 0 and ensure that xs is sorted in descending order. - Note the sorting only happens once – not once per recursion
def fewest_coins amount, xs
def aux count, amount, (x,*xs)
if amount.zero?
count
elsif x.nil?
-1
elsif amount < 0
aux (count - 1), (amount + x), xs
else
aux (count + 1), (amount - x), [x, *xs]
end
end
aux 0, amount, xs.sort { |a,b| b <=> a }
end
fewest_coins 11, [1, 5, 2] # => 3
fewest_coins 2, [3] # => -1
fewest_coins 100, [1, 25, 10, 5] # => 4
Check your understanding
As an exercise, modify fewest_coins to output an array of coins that makes up the answer
# for example
fewest_coins 199, [1, 5, 10, 25, 50]
# => [50, 50, 50, 25, 10, 10, 1, 1, 1, 1]
You could do that as follows.
def count_ways(cents, coins)
if coins.size == 1
return (cents % coins.first) == 0 ? [cents/coins.first] : nil
end
coin, *remaining_coins = coins
(0..cents/coin).each_with_object([]) { |n, arr|
count_ways(cents-n*coin, remaining_coins).each { |a| arr << [n, *a] } }
end
def fewest(cents, coins)
count_ways(cents, coins)&.map(&:sum)&.min
end
fewest(11, [5,2,1])
#=> 3
fewest(199, [25,10,5,1])
#=> 13 (Let me guess: 7 quarters, 2 dimes, 4 pennies)
fewest(2, [3])
#=> nil
require 'time'
t = Time.now
fewest(2835, [25,10,5,1])
#=> 114
Time.now - t
#=> 7.6961 (seconds)
I took count_ways from my answer here.
The two &'s followed by . are Ruby's safe navigation operator, which was introduced in Ruby v2.3.0. Array#sum (and Enumerable#sum) first appeared in Ruby v2.4.0.
This question already has a really good answer that shows you exactly how to solve your problem, but I wanted to point out what was actually happening with your algorithm, so you know why it wasn't working for you.
Your first problem is that
coin_count(coins, amount - coins[i], min)
should be
coin_count(coins, amount - coins[i], coins.min)
Instead of passing along the smallest coin, you were passing along your min value, which you had set to Infinity, which made this statement checking if the amount was smaller than the smallest coin:
return -1 if amount < min_coin
actually check if the amount was smaller than infinity, which meant your coin_count is always returning -1. Which leads to the second problem:
1 + coin_count(coins, amount - coins[i], min)
#1 + -1 = 0
The frustrating thing about using -1 as an error in recursive programming is that -1 is an otherwise valid number and frequently causes logic problems. I would avoid using it entirely, but if your prompt or spec forces you to return it, i'd use it only at the last second. try:
def coin_change(coins, amount)
result = coin_count(coins, amount, coins.min)
return -1 if result == 1/0.0
return result
end
def coin_count(coins, amount, min_coin)
with_coin = min = 1.0/0
return 0 if amount == 0
return 1/0.0 if amount < min_coin
i = 0
while i < coins.length
with_coin = 1 + coin_count(coins, amount - coins[i], coins.min) if amount - coins[i] >= 0
min = [min, with_coin].min
i += 1
end
min
end
I changed your error number from -1 to infinity, which essentially makes your algorithm ignore invalid permutations since they're always sorted out by your .min(). the only way your function would return infinity in this case is if it were the smallest number returned, which only happens when there are no valid permutations. Then in fewest_coins I set it to check for infinity and return -1 instead.
Oh and by the way, there's a much easier way to loop through things in ruby:
coins.each do |coin|
with_coin = 1 + coin_count(coins, amount - coin, coins.min) if amount - coin >= 0
min = [min, with_coin].min
end
This definitely won’t be a smartest approach, it’s not the best performant one, and it might be time consuming for huge amounts, but it’s how I would do it in ruby:
def get coins, amount
coins = coins.sort
max = amount / coins.first + 1
(1..max).detect do |i|
result = coins.repeated_permutation(i).detect do |e|
e.reduce(:+) == amount
end
break result if result
end || -1
end
get [1, 2, 5], 11
#⇒ [1, 5, 5]
get [2], 3
#⇒ -1
This question here does not seem to help: Calculating Percentiles (Ruby)
I would like to calculate 95th percentile (or, indeed, any other desired percentile) from an array of numbers. Ultimately, this will be applied in Rails to calculate distribution against a large number of records.
But, if I can determine how to accurately determine a given percentile from an array of numbers, I can take it from there.
Frankly, I am surprised that I haven't been able to find some sort of gem that would have such functions--I haven't found one yet.
Help is greatly appreciated.
If you want to replicate Excel's PERCENTILE function then try the following:
def percentile(values, percentile)
values_sorted = values.sort
k = (percentile*(values_sorted.length-1)+1).floor - 1
f = (percentile*(values_sorted.length-1)+1).modulo(1)
return values_sorted[k] + (f * (values_sorted[k+1] - values_sorted[k]))
end
values = [1, 2, 3, 4]
p = 0.95
puts percentile(values, p)
#=> 3.85
The formula is based on the QUARTILE method, which is really just a specific percentiles - https://support.microsoft.com/en-us/office/quartile-inc-function-1bbacc80-5075-42f1-aed6-47d735c4819d.
If your are interested in existing gem, then descriptive_statistics gem is best I found so far for percentile function.
IRB Session
> require 'descriptive_statistics'
=> true
irb(main):009:0> data = [1, 2, 3, 4]
=> [1, 2, 3, 4]
irb(main):010:0> data.percentile(95)
=> 3.8499999999999996
irb(main):011:0> data.percentile(95).round(2)
=> 3.85
Good part of gem is its elegant way of describing "I want 95 percentile of data".
Percentile based on count of items
a = [1,2,3,4,5,6,10,11,12,13,14,15,20,30,40,50,60,61,91,99,120]
def percentile_by_count(array,percentile)
count = (array.length * (1.0-percentile)).floor
array.sort[-count..-1]
end
# 80th percentile (21 items*80% == 16.8 items are below; pick the top 4)
p percentile_by_count(a,0.8) #=> [61, 91, 99, 120]
Percentile based on range of values
def percentile_by_value(array,percentile)
min, max = array.minmax
range = max - min
min_value = (max-min)*percentile + min
array.select{ |v| v >= min_value }
end
# 80th percentile (119 * 80% = 95.2; pick values above this)
p percentile_by_value(a,0.8) #=> [99, 120]
Interestingly, Excel's PERCENTILE function returns 60 as the first value for the 80th percentile. If you want this result—if you want an item falling on the cusp of the limit to be included— then change the .floor above to .ceil.
This is the method I developed in my own statistical library:
def quantiles(data, probs=[0.25, 0.50, 0.75])
values = data.sort
probs.map do |prob|
h = 1 + (values.count - 1) * prob
mod = h % 1
(1 - mod) * values[h.floor - 1] + (mod) * values[h.ceil - 1]
end
end
If you just want one quantile, then do quantiles(data, [0.95]).
Given an array like [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], I want to get a random value that takes into consideration the position.
I want the likelihood of 1 popping up to be way bigger than 10.
Is something like this possible?
For the sake of simplicity let's assume an array arr = [x, y, z] from which we will be sampling values. We'd like to see following relative frequencies of x, y and z:
frequencies = [5, 2, 1]
Preprocess these frequencies to calculate margins for our subsequent dice roll:
thresholds = frequencies.clone
1.upto(frequencies.count - 1).each { |i| thresholds[i] += thresholds[i - 1] }
Let's sum them up.
max = frequencies.reduce :+
Now choose a random number
roll = 1 + rand max
index = thresholds.find_index { |x| roll <= x }
Return arr[index] as a result. To sum up:
def sample arr, frequencies
# assert arr.count == frequencies.count
thresholds = frequencies.clone
1.upto(frequencies.count - 1).each { |i| thresholds[i] += thresholds[i - 1] }
max = frequencies.reduce :+
roll = 1 + rand(max)
index = thresholds.find_index { |x| roll <= x }
arr[index]
end
Let's see how it works.
data = 80_000.times.map { sample [:x, :y, :z], [5, 2, 1] }
A histogram for data shows that sample works as we've intended.
def coin_toss( arr )
arr.detect{ rand(2) == 0 } || arr.last
end
a = (1..10).to_a
10.times{ print coin_toss( a ), ' ' } #=> 1 1 1 9 1 5 4 1 1 3
This takes the first element of the array, flips a coin, returns the element and stops if the coinflip is 'tails'; the same with the next element otherwise. If it is 'heads' all the way, return the last element.
A simple way to implement this with an logarithmic probabilistic of being selected is to simulate coin flips. Generate a random integer 0 and 1, the index to that array to choose is the number of consecutive 1s you get. With this method, the chance of selecting 2 is 1/2 as likely as 1, 3 is 1/4th as likely, etc. You can vary the probability slightly say by generating random numbers between 0 and 5 and count the number of consecutive rounds above 1, which makes each number in the array 4/5th as likely to appear as the one before.
A better and more general way to solve this problem is to use the alias method. See the answer to this question for more information:
Data structure for loaded dice?
Let's say I have a min and a max number. max can be anything, but min will always be greater than zero.
I can get the range min..max and let's say I have a third number, count -- I want to divide the range by 10 (or some other number) to get a new scale. So, if the range is 1000, it would increment in values of 100, 200, 300, and find out where the count lies within the range, based on my new scale. So, if count is 235, it would return 2 because that's where it lies on the range scale.
Am I making any sense? I'm trying to create a heat map based on a range of values, basically ... so I need to create the scale based on the range and find out where the value I'm testing lies on that new scale.
I was working with something like this, but it didn't do it:
def heat_map(project, word_count, division)
unless word_count == 0
max = project.words.maximum('quantity')
min = project.words.minimum('quantity')
range = min..max
total = range.count
break_point = total / division
heat_index = total.to_f / word_count.to_f
heat_index.round
else
"freezing"
end
end
I figured there's probably an easier ruby way I'm missing.
Why not just use arithmetic and rounding? Assuming that number is between min and max and you want the range split into n_div divisions and x is the number you want to find the index of (according to above it looks like min = 0, max = 1000, n_div = 10, and x = 235):
def heat_index(x, min, max, n_div)
break_point = (max - min).to_f/n_div.to_f
heat_index = (((x - min).to_f)/break_point).to_i
end
Then heat_index(235, 0, 1000, 10) gives 2.
I'm just quickly brainstorming an idea, but would something like this help?
>> (1..100).each_slice(10).to_a.index { |subrange| subrange.include? 34 }
=> 3
>> (1..100).each_slice(5).to_a.index { |subrange| subrange.include? 34 }
=> 6
This tells you in which subrange (the subrange size is determined by the argument to each_slice) the value (the argument to subrange.include?) lies.
>> (1..1000).each_slice(100).to_a.index { |subrange| subrange.include? 235 }
=> 2
Note that the indices for the subranges start from 0, so you may want to add 1 to them depending on what you need. Also this isn't ready as is, but should be easy to wrap up in a method.
How's this? It makes an array of range boundaries and then checks if the number lies between them.
def find_range(min, max, query, increment)
values = []
(min..max).step(increment) { |value| values << value }
values.each_with_index do |value, index|
break if values[index + 1].nil?
if query > value && query < values[index + 1]
return index
end
end
end
EDIT: removed redundant variable
Given an array
[50,30,0,0,10,0,30,60,0]
I need to replace the zeroes with calculated values to create a 'curve', so for example, between 10 and 30, the zero could be replaced with 20.
I keep thinking there must be a cool ruby way of doing this, but I cant find one. Can anyone help? The solution needs to take into account multiple adjacent zeroes, and zeroes at the start and end of the range.
Anyone any ideas?
The term you seem to be unaware of is interpolation. The Wikipedia article is a good place to start - exactly what algoithm is best suited to you depends on the exact context of your problem so we can't give you the one true answer here.
a =[50,30,0,0,10,0,30,60,0]
a.each_index{|i| a[i] = a[i-1] - ((a[i-2] - a[i-1])/2).to_i if a[i] == 0 && i > 1 }
puts a.inspect # [50, 30, 20, 15, 10, 8, 30, 60, 75]
I can't work out why the last number might be 80 in your spec however? Plus it doesn't work for the first two items in the array.
If there could not be consecutive zeroes, this unintelligible one-liner would do the trick (list is the given list of numbers):
[0, *list, 0].each_cons(3).map { |p, x, n| x == 0 ? (p + n)/2 : x }
Ruby 1.9 only, I think.
def find_consecutive_values( array, value=nil )
raise "Need a value or block to find" unless value || block_given?
start = last = nil
ranges = []
indices = array.each_with_index do |o,i|
if ((block_given? && yield(o)) || o==value)
start = i unless start
last = i
else
ranges << (start..last) if start && last
start = last = nil
end
end
ranges << (start..last) if start && last
ranges
end
def interpolate_zeros( array, round=false )
result = array.dup
find_consecutive_values( array, 0 ).each do |range|
next unless range.first>0 && range.last<(array.length-1)
before = result[range.first - 1]
after = result[range.last + 1]
diff = after - before
size = (range.last - range.first + 2).to_f
range.each_with_index do |i,idx|
value = before + diff * (idx+1)/size
value = value.round if round
result[i] = value
end
end
result
end
p interpolate_zeros( [0,50,30,0,0,10,0,30,60,0], true )
#=> [0, 50, 30, 23, 17, 10, 20, 30, 60, 0]
Just stumbled across this question. There is a ruby gem "interpolator", which just does what you want and probably tons more:
http://interpolator.rubyforge.org.
Here is a short introduction:
http://fabianosoriani.wordpress.com/2010/02/23/ruby-interpolation-with-gem-interpolator/