给定非负整数n和用户定义的任意一组不等式(例如外部文本文件),我想确定n是否满足任何不等式,如果是,则确定哪一个是不等式。
这是一个积分表。
n = 0: 1
n < 5: 5
n = 5: 10
如果你绘制的数字n等于5,你得到10分 如果n小于5,则得到5分 如果n为0,则得1分。
冒号留下的东西是“条件”,而右边的东西是“价值” 所有参赛作品的格式均为:
n1 op n2: val
在这个系统中,平等优先于不平等,因此它们出现的顺序最终无关紧要。输入是非负整数,但中间和结果可能不是非负的。结果甚至可能不是数字(例如:可能是字符串)。我设计它只是为了接受最基本的不等式,以便更容易编写解析器(并查看这个想法是否可行)
我的程序有两个组成部分:
解析器将读取结构化输入并构建数据结构以存储条件及其相关结果。
一个接受参数(非负整数)并返回结果的函数(或者,如示例中所示,我收到的点数)
如果列表是硬编码的,那么这是一项简单的任务:只需使用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,在涉及“弄乱”数据及其解释方式时,我可能会有更灵活的选择。
答案 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
),更普遍/优雅地处理输入等。