【问题标题】:How to set default of ruby reduce method to calculation of first array rather than first array value如何将ruby reduce方法的默认值设置为计算第一个数组而不是第一个数组值
【发布时间】:2021-11-13 00:30:03
【问题描述】:

我正在解决选股问题,我尝试在给定一系列股票价格的情况下找到最佳买卖日期。我用下面的代码解决了这个问题,它有效。

def stock_picker(price_list)
  """Returns the largest profit possible given the array of prices""" 
  #should not be able to purchase on the last date
  avalable_purchase_dates = price_list[0..-2]
  maximized_profit = avalable_purchase_dates.reduce(-(Float::INFINITY)) do |profit, buy_price|
    available_sell_prices = price_list[price_list.index(buy_price)+1, price_list.length] 
    max_profit = (available_sell_prices.map {|sell_price| sell_price-buy_price}).max
    profit = [profit, max_profit].max 
  end
  return maximized_profit #, buy ,sell
end

b = stock_picker([137,3,6,9,15,8,6,1,10,19,-4]) #returns 18
print (b)

代码的逻辑是它查看每个日期的价格并计算如果以这个价格购买可能的最大利润,如果潜在利润大于聚合器,它将聚合器设置为等于潜在利润.

我想知道是否有办法避免将 reduce 方法聚合器的默认值设置为负无穷大。我将默认值设置为负无穷大,以便第一个潜在利润更大,因此聚合器将设置为该值。我希望能够完全避免这种情况,并让 ruby​​ 为每个数组值执行回调,并将默认值设置为第一个计算而不是特定值。我当前的解决方案很容易混淆并错误地编写逻辑,例如,如果我将默认设置为零,那么我的解决方案将不适用于价格递减的系列。

谢谢!

【问题讨论】:

  • 你能解释一下(用文字)为什么给定数组的结果是 18 吗?

标签: ruby reduce


【解决方案1】:

这对你有用吗?它绕过了inject 的所有复杂性。

def stock_picker(price_list)
  price_list.combination(2).max_by{|buy_price, sell_price| sell_price - buy_price}
end

p stock_picker( [137,3,6,9,15,8,6,1,10,19,-4]) # =>[1, 19]

【讨论】:

  • 虽然看起来不错,但我看不出它在哪里保证了每个组合的顺序。官方文档说“组合的顺序是不确定的”,这并没有真正澄清它是否谈论组合的枚举顺序或每个组合中元素的顺序。在后一种情况下,您并不真正知道第一个元素是否是买入价。见docs.ruby-lang.org/en/master/Array.html#method-i-combination
【解决方案2】:

您将希望通过数组进行一次传递。我假设在调用该方法之前,您已经检查了价格数组是否包含至少两个元素。

def stock_picker(prices)
  best_buy_period = prices[1] < prices[0] ? 1 : 0
  (2..prices.size-1).reduce(buy:0, sell: 1, profit:prices[1]-prices[0]) do |best,i|
    candidate = prices[i]-prices[best_buy_period]
    best = { buy:best_buy_period, sell:i, profit:candidate } if
      candidate > best[:profit]
    best_buy_period = i if prices[i] < prices[best_buy_period]
    best
  end
end
price_list = [137,3,6,9,15,8,6,1,10,19,-4]
  #=>{:buy=>7, :sell=>9, :profit=>18}

我们可以通过添加一些puts 语句来跟踪正在发生的事情。

def stock_picker(prices)
  best_buy_period = prices[1] < prices[0] ? 1 : 0
  (2..prices.size-1).reduce(buy:0, sell:1, profit:prices[1]-prices[0]) do |best,i|
    puts "i = #{i}, best_buy_period = #{best_buy_period}, best = #{best}"
    candidate = prices[i]-prices[best_buy_period]
    puts "best = #{best}, candidate = #{candidate}"        
    best = { buy:best_buy_period, sell:i, profit:candidate } if
      candidate > best[:profit]
    best_buy_period = i if prices[i] < prices[best_buy_period]
    best
  end
end
stock_picker(prices)
  #=>{:buy=>7, :sell=>9, :profit=>18}
i = 2, best_buy_period = 1, best = {:buy=>0, :sell=>1, :profit=>-134}
best = {:buy=>0, :sell=>1, :profit=>-134}, candidate = -1
i = 3, best_buy_period = 2, best = {:buy=>1, :sell=>2, :profit=>-1}
best = {:buy=>1, :sell=>2, :profit=>-1}, candidate = 7
i = 4, best_buy_period = 2, best = {:buy=>2, :sell=>3, :profit=>7}
best = {:buy=>2, :sell=>3, :profit=>7}, candidate = 13
i = 5, best_buy_period = 2, best = {:buy=>2, :sell=>4, :profit=>13}
best = {:buy=>2, :sell=>4, :profit=>13}, candidate = 6
i = 6, best_buy_period = 2, best = {:buy=>2, :sell=>4, :profit=>13}
best = {:buy=>2, :sell=>4, :profit=>13}, candidate = 4
i = 7, best_buy_period = 2, best = {:buy=>2, :sell=>4, :profit=>13}
best = {:buy=>2, :sell=>4, :profit=>13}, candidate = -1
i = 8, best_buy_period = 7, best = {:buy=>2, :sell=>4, :profit=>13}
best = {:buy=>2, :sell=>4, :profit=>13}, candidate = 9
i = 9, best_buy_period = 7, best = {:buy=>2, :sell=>4, :profit=>13}
best = {:buy=>2, :sell=>4, :profit=>13}, candidate = 18
i = 10, best_buy_period = 7, best = {:buy=>7, :sell=>9, :profit=>18}
best = {:buy=>7, :sell=>9, :profit=>18}, candidate = -5

请注意,...reduce(buy: 0, sell: 1, profit: prices[1]-prices[0]) do......reduce({ buy: 0, sell: 1, profit: prices[1]-prices[0] }) do... 的简写。


方法也可以写成如下。

def stock_picker(prices)
  best_buy_period = prices[1] < prices[0] ? 1 : 0
  (2..prices.size-1).each_with_object(buy:0, sell:1, profit:prices[1]-prices[0]) do |i,best|
    candidate = prices[i]-prices[best_buy_period]
    best.replace(buy:best_buy_period, sell:i, profit:candidate) if
      candidate > best[:profit]
    best_buy_period = i if prices[i] < prices[best_buy_period]
  end
end

【讨论】:

    【解决方案3】:

    作为中间立场,您可以迭代索引(深受@steenslag 解决方案的启发)

    # Returns the largest profit possible given the array of prices
    def stock_picker(price_list)
      length = price_list.size
    
      (0...length - 1).map do |buy_index|
        (buy_index + 1...length).map do |sell_index|
          # This would actuall return more info:
          # [price_list[sell_index] - price_list[buy_index], buy_index, sell_index]
          price_list[sell_index] - price_list[buy_index]
        end.max
      end.max
    end
    

    关于您对不将累加器传递给reduce 的疑问:是的,您可以在调用reduce 时避免累加器的初始值,但随后它将作为第一个累加器的第一个值可枚举。所以要使用它,你必须映射,并且只在实际减少步骤中使用 reduce:

      maximized_profit = 
        avalable_purchase_dates
          .map do |buy_price|
             # ...
          end.reduce do |memo, internal_max|
            [memo, internal_max].max
          end
    

    您可以看到大部分计算是如何在map 块中完成的,因此第一个结果已经可以用作初始值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-11
      • 1970-01-01
      • 1970-01-01
      • 2018-07-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多