我有:
testArray = [
{price: 540, volume: 12},
{price: 590, volume: 18},
{price: 630, volume: 50}
]
我想计算一定总量的平均值。假设有人想购买40
件,他希望这是最便宜的方式。这意味着平均价格
(540 * 12 + 590 * 18 + 630 * (40-18-12)) / 40 = 585
货币单位。
我已经问了问题here,并得到了答案。但是,在非常大的数组上,此方法很慢:
def self.calculateMiddlePrice(prices, amount)
@order = prices.flat_map{|item| [item[:price].to_i] * item[:volRemain].to_i }
unless amount > @order.count
@order[1..amount].reduce(:+) / amount
else
@order.reduce(:+) / @order.count
end
end
我尝试了懒惰的算子,但没有任何成功:
@order = prices.lazy.flat_map{|item| [item[:price].to_i] * item[:volRemain].to_i }.force
有没有更有效的方法来解决我的初始问题?
答案 0 :(得分:1)
这样可以在没有冗余数据复制的情况下一次性提高性能:
编辑:在OP的评论和orig的变化后更新答案。问题强>
testArray = [ {price: 540, volume: 12},
{price: 590, volume: 18},
{price: 630, volume: 50}]
to_buy = 40
wp, rem = testArray.inject({wp: 0, ct: to_buy}) do |m,h|
unless m[:ct].zero?
delta = [h[:volume], m[:ct]].min
m[:wp] += h[:price] * delta
m[:ct] -= delta
end
m
end.values
p wp / to_buy, rem # 585 0
使用Enumerable#inject Array
混合的数据结构折叠。
答案 1 :(得分:1)
上述评论者有正确的想法。这是一个简单的reduce
操作 - 无需构建长数组:
def calc_price(prices, volume)
volume_remaining = volume
total = prices.reduce(0) do |sum, item|
if item[:volume] > volume_remaining
break sum + item[:price] * volume_remaining
end
volume_remaining -= item[:volume]
sum + item[:price] * item[:volume]
end
total / volume
end
此方法使用prices
对Enumerable#reduce
中的项进行迭代,并保持正在运行的sum
。在每次迭代中,它会检查item[:volume]
是否大于volume_remaining
。如果是,则将item[:price] * volume_remaining
添加到正在运行的sum
并返回它。如果不是,则会从item[:volume]
中减去volume_remaining
,然后将item[:price] * item[:volume]
添加到正在运行的sum
,然后转到下一个项目。
它有效,甚至:
prices = [
{ price: 540, volume: 12 },
{ price: 590, volume: 18 },
{ price: 630, volume: 50 },
]
puts calc_price(prices, 40)
# => 585
P.S。您可能希望对此进行两项修改:
如果您需要小数结果(即如果平均值不是整数),请将total / volume
更改为total.to_f / volume
。
如果您想在无法满足音量时提出错误,那么这只是一个很小的改变:
def calc_price(prices, volume)
volume_remaining = volume
total = prices.reduce(0) do |sum, item|
if item[:volume] > volume_remaining
sum, volume_remaining = sum + item[:price] * volume_remaining, 0
break sum
end
volume_remaining -= item[:volume]
sum + item[:price] * item[:volume]
end
return total / value unless volume_remaining > 0
raise "Required volume of #{volume} could not be satisfied!"
end
然后:
puts calc_price(prices, 100)
# => RuntimeError: Required volume of 100 could not be satisfied!