处理任意不等式并检查哪些(如果有的话)满足

时间:2012-05-04 19:07:20

标签: ruby algorithm

给定非负整数n和用户定义的任意一组不等式(例如外部文本文件),我想确定n是否满足任何不等式,如果是,则确定哪一个是不等式。


这是一个积分表。

n = 0: 1
n < 5: 5
n = 5: 10

如果你绘制的数字n等于5,你得到10分 如果n小于5,则得到5分 如果n为0,则得1分。


冒号留下的东西是“条件”,而右边的东西是“价值” 所有参赛作品的格式均为:

n1 op n2: val

在这个系统中,平等优先于不平等,因此它们出现的顺序最终无关紧要。输入是非负整数,但中间和结果可能不是非负的。结果甚至可能不是数字(例如:可能是字符串)。我设计它只是为了接受最基本的不等式,以便更容易编写解析器(并查看这个想法是否可行)

我的程序有两个组成部分:

  1. 解析器将读取结构化输入并构建数据结构以存储条件及其相关结果。

  2. 一个接受参数(非负整数)并返回结果的函数(或者,如示例中所示,我收到的点数)

  3. 如果列表是硬编码的,那么这是一项简单的任务:只需使用case-when或if-else块,我就完成了。但问题并不那么容易。

    回想一下顶部的列表。它可以包含任意数量的(in)等式。也许只有3个像上面那样。也许没有,或者可能有10,20,50甚至1000000.基本上,你可以有m个不等式,因为m> = 0

    给定数字n和包含任意数量的条件和结果的数据结构,我希望能够确定它是否满足任何条件并返回相关值。因此,与上面的示例一样,如果我传入5,则函数将返回10.

    它们的条件/值对原始形式不是唯一的。您可能有多个相同(in)相等但具有不同值的实例。例如:

    n = 0: 10
    n = 0: 1000
    n > 0: n
    

    注意最后一个条目:如果n大于0,那么它就是你得到的任何东西。

    如果满足多个不等式(例如:n> 5,n> 6,n> 7),则应返回所有不等式。如果不能有效地做到这一点,我可以只返回满足它的第一个并忽略其余部分。但我希望能够检索整个列表。


    我一直在思考这个问题,我想我应该使用两个哈希表:第一个存储均衡,第二个存储不等式。

    平等很容易处理:只需将条件作为键获取并具有值列表。然后我可以快速检查n是否在哈希中并获取适当的值。

    但是,对于不平等,我不确定它是如何工作的。有没有人有任何想法如何在尽可能少的计算步骤中解决这个问题?很明显,我可以在O(n)时间内轻松完成此任务:只需逐个执行每个(in)相等即可。但如果这次检查是实时完成会发生什么? (例如:不断更新)

    例如,很明显,如果我有100个不等式,其中99个检查值&gt; 100,而另一个检查值&lt; = 100,当我通过47时,我不应该费心去检查这99个不等式。

    您可以使用任何数据结构来存储数据。解析器本身不包含在计算中,因为它将被预处理并且只需要执行一次,但如果解析数据需要太长时间则可能会出现问题。

    由于我使用的是Ruby,在涉及“弄乱”数据及其解释方式时,我可能会有更灵活的选择。

3 个答案:

答案 0 :(得分:2)

class RuleSet
  Rule = Struct.new(:op1,:op,:op2,:result) do
    def <=>(r2)
      # Op of "=" sorts before others
      [op=="=" ? 0 : 1, op2.to_i] <=> [r2.op=="=" ? 0 : 1, r2.op2.to_i]
    end
    def matches(n)
      @op2i ||= op2.to_i
      case op
        when "=" then n == @op2i
        when "<" then n  < @op2i
        when ">" then n  > @op2i
      end
    end
  end

  def initialize(text)
    @rules = text.each_line.map do |line|
      Rule.new *line.split(/[\s:]+/)
    end.sort
  end

  def value_for( n )
    if rule = @rules.find{ |r| r.matches(n) }
      rule.result=="n" ? n : rule.result.to_i
    end
  end
end

set = RuleSet.new( DATA.read )
-1.upto(8) do |n|
  puts "%2i => %s" % [ n, set.value_for(n).inspect ]
end

#=> -1 => 5
#=>  0 => 1
#=>  1 => 5
#=>  2 => 5
#=>  3 => 5
#=>  4 => 5
#=>  5 => 10
#=>  6 => nil
#=>  7 => 7
#=>  8 => nil

__END__
n = 0: 1
n < 5: 5
n = 5: 10
n = 7: n

答案 1 :(得分:1)

我不会花很多时间在你的问题上,但这是我的快速思考:

由于points list始终采用格式n1 op n2: val,因此我只是将点建模为哈希数组。

所以第一步是将输入点列表解析为数据结构,即哈希数组。 每个散列都有值n1,op,n2,value

然后,对于每个数据输入,您将遍历所有哈希值(所有点)并处理每个哈希值(确定它是否与输入数据匹配)。

交易的一些技巧

花费时间在解析器处理错误输入。例如

n < = 1000  # no colon
n < : 1000  # missing n2
x < 2 : 10  # n1, n2 and val are either number or "n"
n           # too short, missing :, n2, val
n < 1 : 10x # val is not a number and is not "n"  

还礼貌地处理非数字输入数据

已添加

回复:n1没关系。小心,这可能是一个技巧。为什么不

5 < n : 30

是有效的积分榜项目吗?

Re:多个哈希数组,每个运算符一个数组,每个点列表项一个哈希 - 确定没问题。由于每个操作都以特定的方式处理,因此逐个处理操作符很好。但....订购然后成为一个问题:

由于您希望从多个匹配点列表项返回多个结果,因此您需要维护它们的整体顺序。因此,我认为所有点列表中的一个数组将是最简单的方法。

答案 2 :(得分:1)

我会解析输入行并将它们分成谓词/结果对并构建可调用过程的哈希值(使用eval - 哦,不!)。 “check”函数可以迭代每个谓词,并在一个为真时返回相关结果:

class PointChecker
  def initialize(input)
    @predicates = Hash[input.split(/\r?\n/).map do |line|
      parts = line.split(/\s*:\s*/)
      [Proc.new {|n| eval(parts[0].sub(/=/,'=='))}, parts[1].to_i]
    end]
  end
  def check(n)
    @predicates.map { |p,r| [p.call(n) ? r : nil] }.compact
  end
end

以下是示例用法:

p = PointChecker.new <<__HERE__
  n = 0: 1
  n = 1: 2
  n < 5: 5
  n = 5: 10
__HERE__
p.check(0) # => [1, 5] 
p.check(1) # => [2, 5] 
p.check(2) # => [5] 
p.check(5) # => [10] 
p.check(6) # => []

当然,这种实现存在许多问题。我只是提供一个概念验证。根据应用程序的范围,您可能希望构建适当的解析器和运行时(而不是使用eval),更普遍/优雅地处理输入等。