求解一个变量中的线性方程
Posted
技术标签:
【中文标题】求解一个变量中的线性方程【英文标题】:Solving a linear equation in one variable 【发布时间】:2013-04-29 07:30:50 【问题描述】:在作为函数的字符串输入给定的一个变量中求解线性方程的最有效算法是什么?例如,对于输入字符串:
“x + 9 - 2 - 4 + x = - x + 5 - 1 + 3 - x”
输出应该是 1。
当我遇到字符串中的空格时,我正在考虑使用堆栈并将每个字符串标记推到它上面。如果输入是波兰表示法,那么从堆栈中弹出数字以获得结果会更容易,但我不确定在这里采取什么方法。
这是一道面试题。
【问题讨论】:
一种可能的方法是使用正则表达式来解析字符串并将其拆分。 然后你需要检查分割的块并确定它们是什么。首先找到变量然后找到数字,反之亦然,显然您还需要确定正负等。然后根据你的发现计算输出,这是简单的算术。 只要把 x'ses 放在左边,数字放在等式右边。将两个站点除以 X 的乘数,瞧;) 您可以将字符串(考虑以 x 开头的 x)标记为 Vector一旦您计算出方程a * x + b = 0
中的系数a
和b
,求解线性方程对您来说(我希望)非常容易。
因此,问题的难点在于解析表达式并“评估”它以找到系数。您的示例表达式非常简单,它仅使用运算符一元-
、二元-
、二元+
。还有=
,你可以专门处理。
从问题中不清楚解决方案是否还应处理涉及二进制*
和/
或括号的表达式。我想知道面试问题是否有意:
两者都是重要的技能:-)
这个问题甚至可能是有意的:
将那些有很多编写解析器经验的人(他们会以他们能写/打字的速度解决它)与那些没有编写解析器的人(他们可能在几分钟内很难解决它,至少没有一些提示)分开.无论如何,为了满足未来更复杂的要求,解析算术表达式有两种常用方法:递归下降法或 Dijkstra 的调车场算法。您可以查看这些,如果您只需要 1.0 版中的简单表达式,那么您可以使用 Dijkstra 算法的简化形式。然后,一旦解析了表达式,就需要对其求值:使用 x
中的线性表达式值并将 =
解释为具有最低可能优先级的运算符,即“减法”。结果是x
中的线性表达式,等于0
。
如果您不需要复杂的表达式,那么您可以直接从左到右评估这个简单的示例,一旦您对其进行标记[*]:
x
x + 9
// set the "we've found minus sign" bit to negate the first thing that follows
x + 7 // and clear the negative bit
x + 3
2 * x + 3
// set the "we've found the equals sign" bit to negate everything that follows
3 * x + 3
3 * x - 2
3 * x - 1
3 * x - 4
4 * x - 4
最后,将a * x + b = 0
解为x = - b/a
。
[*] 示例标记化代码,在 Python 中:
acc = None
for idx, ch in enumerate(input):
if ch in '1234567890':
if acc is None: acc = 0
acc = 10 * acc + int(ch)
continue
if acc != None:
yield acc
acc = None
if ch in '+-=x':
yield ch
elif ch == ' ':
pass
else:
raise ValueError('illegal character "%s" at %d' % (ch, idx))
替代示例标记化代码,也在 Python 中,假设标记之间总是有空格,如示例中所示。这将令牌验证留给解析器:
return input.split()
【讨论】:
我认为这是一个很好的答案。如果我为此采访您,我会担心您是否假设在示例中未使用乘法和除法时,我想您会问这个问题。我还希望有经验的程序员提及空格,因为它们可以在没有正则表达式等的情况下拆分方程。当然,询问他们是否可以依赖是有道理的。如果它只使用 +/-/= 并且空格是可靠的,那么正如你所说的,你可以从左到右工作,将所有常量加在一起并计算 xs。 @Will:确实,在做任何更复杂的事情之前我会问,但如果我不问,我有一半希望完成代码,然后被要求修改它。实际上,我可能不会对空格提出问题,因为无论有没有空格,都可以轻松地标记该示例。标记可以是数字序列(最大咀嚼)或单个字符。丢弃空间“令牌”,因为它们所做的一切都是分开的。在带有 Boost 的 Python 或 C++ 中,我可能想使用库函数通过在空格上拆分来进行标记,在这种情况下我会先问。我不会在这两种语言中使用正则表达式。【参考方案2】:好的,一些简单的伪代码可以用来解决这个问题
function(stinrgToParse)
arrayoftokens = stringToParse.match(RegexMatching);
foreach(arrayoftokens as token)
//now step through the tokens and determine what they are
//and store the neccesary information.
//Use the above information to do the arithmetic.
//count the number of times a variable appears positive and negative
//do the arithmetic.
//add up the numbers both positive and negative.
//return the result.
【讨论】:
【参考方案3】:首先是解析字符串,识别各种标记(数字、变量和运算符),这样就可以通过给运算符适当的优先级来形成表达式树。
正则表达式可以提供帮助,但这不是唯一的方法(诸如 boost::spirit 之类的语法解析器也很好,您甚至可以运行自己的:它都是“查找和追索”)。
然后可以对树进行操作,减少执行那些处理常量的操作的节点,并通过对变量相关的操作进行分组,相应地执行它们。
这会递归进行,直到您保留一个变量相关节点和一个常量节点。
此时解的计算很简单。
它们与产生解释器或编译器的原则基本相同。
【讨论】:
【参考方案4】:考虑:
from operator import add, sub
def ab(expr):
a, b, op = 0, 0, add
for t in expr.split():
if t == '+': op = add
elif t == '-': op = sub
elif t == 'x': a = op(a, 1)
else : b = op(b, int(t))
return a, b
给定一个像 1 + x - 2 - x...
这样的表达式,这会将其转换为规范形式 ax+b
并返回一对系数 (a,b)
。
现在,让我们从方程的两个部分获得系数:
le, ri = equation.split('=')
a1, b1 = ab(le)
a2, b2 = ab(ri)
最后解出微不足道的方程a1*x + b1 = a2*x + b2
:
x = (b2 - b1) / (a1 - a2)
当然,这只解决了这个特定的例子,没有运算符优先级或括号。为了支持后者,您需要一个解析器,大概是一个递归下降解析器,这对于手动编码来说会更简单。
【讨论】:
以上是关于求解一个变量中的线性方程的主要内容,如果未能解决你的问题,请参考以下文章