【问题标题】:Counting threshold hits计数阈值命中
【发布时间】:2025-12-04 23:35:01
【问题描述】:

独自跑步,需要一些帮助。如果您有一系列数字,并且想要计算值跨越障碍的次数,您会怎么做?

series = [1, 6, 2, 4, 1, 9, 2]
series.hit_counts(upper=7, middle=5, lower=3) #returns 3

详情

1 -> 6 = +1 (Going from 1 to 6 hits the lower and middle threshold)
6 -> 2 = +0 (The high value, 6, never reached the upper threshold although it crosses the middle)
2 -> 4 = +0 (2 is below the lower threshold but has not hit the middle)
4 -> 1 = +0 (The series still has not reached the middle threshold)
1 -> 9 = +1 (The series hit the middle threshold)
9 -> 2 = +1 (The series has hit the high and middle thresholds)

hit_counts:计算值达到上限或下限然后超过中间阈值的次数。

上限:7

中限:5

下限:3

有问题的方法将返回3。任何想法,将不胜感激。

【问题讨论】:

  • 当您将更改计为阈值命中时并不完全清楚。我最初的猜测是,只有当a 超出upper..lower 范围并且a..b 包含middle 时,[a, b] 才算作命中。正确的?此外,您的详细信息中有一个额外的步骤9 -> 2,它不会出现在您的初始数组中。
  • 评论好像被截断了?有什么不清楚的请告诉我
  • 回车太早,现在编辑。
  • 谢谢。更新了问题。仅当先前的值达到上限或下限阈值时,计数才会增加。因此,该方法必须考虑的不仅仅是 [previous_value, current_value]。当我尝试自己实现它时,我最终使用了一个变量来保存状态(它是否达到了上限或下限)。
  • 所以实际上我们只需要计算我们超过两个阈值的对吗?

标签: ruby loops iterator logic time-series


【解决方案1】:

我采取了不同的方法来解决您的问题。

代码

INF = Float::INFINITY

def score(series, limits)
  last =
    case series.first
    when (-INF..limits[:LOWER]) then :LOWER
    when (limits[:UPPER]..INF)  then :UPPER
    else :MIDDLE
    end
  series[1..-1].count do |n|
    if limits[last] <= n
      score = (last == :LOWER && n >= limits[:MIDDLE])
      last =
        case n
        when (-INF..limits[:MIDDLE]-1) then :LOWER
        when (limits[:UPPER]..INF) then :UPPER
        else :MIDDLE
        end
    else
      score = (last == :UPPER && n <= limits[:MIDDLE])
      last =
        case n
        when (-INF..limits[:LOWER]) then :LOWER
        when (limits[:MIDDLE]+1..INF) then :UPPER
        else :MIDDLE
        end
    end
    score
  end
end

示例

limits = { LOWER: 3, MIDDLE: 5, UPPER: 7 }

series = [1, 6, 2, 4, 1, 9, 2]
p score(series,limits)  #=> 3

series = [1,5,12,8,10,4,13,3,1,4,6,4,6,9,1]
p score(series, limits) #=> 5

@BroiSatse 在这两个测试序列中得到相同的结果。

说明

我已将规则更改为我认为更简单但得分相同的问题:

  • levels = {LOWER: 3, MIDDLE: 5, UPPER: 7}

  • 玩家从三个级别之一开始,:LOWER:MIDDLE:UPPER。如果f = series.first,则由变量last 持有的那个级别是:

    • :LOWER 如果f &lt;= limits[:LOWER]
    • :UPPER 如果f &gt;= limits[:UPPER]
    • :MIDDLE 否则
      `
  • 玩家将在关卡之间移动(或保持在当前级别),当从:LOWER 移动到:MIDDLE:UPPER,或从:UPPER 移动到@987654339 时,得分1 @ 或 :LOWER

  • 对于每个i &gt; 0,让f = series[i]。播放器位于last。移动计分规则如下:

    • 如果limits[last] &lt;= f(移动到非递减值)玩家移动到(并且last变成):
      • :LOWER 如果f &lt; limits[:MIDDLE]
      • :UPPER 如果f &gt;= limits[:UPPER]
      • :MIDDLE 否则
      • 如果玩家从:LOWER 移动到:MIDDLE:UPPER,则得分。 `
    • 如果f &lt; limits[last](移动到一个递减值)玩家移动到(并且last变成):
      • :LOWER 如果f &lt;= limits[:LOWER]
      • :UPPER 如果f &gt; limits[:MIDDLE]
      • :MIDDLE 否则
      • 如果玩家从:UPPER 移动到:MIDDLE:LOWER,则得分。
  • 对序列中的每个后续元素重复该过程。

代码只是以直接的方式实现这些规则。

【讨论】:

    【解决方案2】:

    假设我明白你想要什么,你可以试试:

    class Array
      def hit_counts(upper=7, middle=5, lower=3)
        limit_broken = false
        each_cons(2).count do |a,b|
          limit_broken = true unless (lower..upper).cover? a
          current_range = Range.new(*[a,b].sort)
          if limit_broken && current_range.cover?(middle)
            limit_broken = false
            true
          else
            false
          end
        end
      end
    end
    

    【讨论】: