Solving linear equations 

in "Pure c"


github @observerss

jingchaohu AT gmail DOT com

2014. Caigin100 Inc.

Problem


Logan's middle school computer contest problem:

how to solve these linear equations?

3*x + 4 = 6
(4+x*2)*5+3*2+5*x=1
(4-3)*(2+7)/2 = x - 5*4+3*x


x = ?





During 5.1 festival, I was thinking ...


I am now a more capable programmer than he was,

can I solve this problem?

EASY!


matlab, yacas, 
or sympy

 >>> import sympy
 >>> sympy.solve('2*x + 5 - (3*x-2) - x - 5', 'x')
 [1]




NO, that's cheating

RULES


  • no third party library
  • no fancy eval-like operation
  • no regular expressions
  • let's do it in pure c
    • except using python for better  illustration

How humans do it?

2*(x + 5) - (3*x-2)=x + 5
rule1: a = b => a - b = 0                                                                                                                      
2*(x +5)-(3*x-2)-(x+5) = 0
rule2: a*(b+c) = a*b+a*c                                                                                                                    
2*x+2*5-(3*x-2)-(x+5) = 0
rule3: a-(b+c) = a-b-c; a-(b-c) = a-b+c                                                                                           
2*x+10-3*x+2-x-5 = 0
rule4: a + b + c = a + c + b; a + b - c = a + b + (-c) = a + (-c) + b =  a - c +b                  
2*x-3*x-x+10+2-5 = 0
then we apply rule2 again (reversed)                                                                                                   
-2*x+7=0

IT's ok, but...



  • a lot of rules
  • a lot of parsers
  • a lot of code
  • seems too complicated to me

Another APProach


for f(x) = (any very complex linear expression) 
there is a simplified version, that is,
f(x)=ax + b

then:
f(1) = a + b
f(0)  = b

Our Situation

f(x) = 2*(x+5) - (3*x-2) - x - 5

f(1) = 2*(1+5)-(3*1-2)-1-5 = 5
f(0) = 2*(0+5)-(3*0-2)-0-5= 7

so:                                            
a+b = 5 = f(1)
b = 7 = f(0)
ax+b=0
=>                                       
x = -b/a = -f(0)/(f(1)-f(0))

Full Algorithm



  1. move the right of equation to left
  2. replace x with 1 and 0 respectively and evaluate them
  3. calculate the x according to step2's result

FULL ALGORITHM


  1. move the right of equation to left
    1. equation.replace('=', '-(') + ')'
  2. replace x with 1 and 0 respectively  and evaluate them
    1. equation.replace('x', 1)
    2. how to evaluate a mathematic expression?
  3. calculate the x according to step2's results

IMPLEmentation



        def solve(equation, sym='x'):
            expr = equation.replace('=', '-(') + ')'
            expr = expr.replace(' ', '')
            v1 = calc(expr.replace(sym, '1'))
            v2 = calc(expr.replace(sym, '2'))
            return 1.*(-v2)/(v1-v2)
    

How to implement "calc"?


def calc(expression):
    """ calculate the result of a numeral algebra expression """
    return eval(expression)

No, there's no "eval" in C

How?

DATA structure Textbook


  • infix expression -> postfix expression
    • shutting-yard algorithm
  • evaluate postfix expression 
  • or simply combine the above steps

Implementing calc

def calc(expr):
    """ calculate numeral expression """
    op_stack = []
    num_stack = []
    i = 0
    while i < len(expr):
        tok, i = next_token(expr, i)
        if tok in ops:  # + - * / ( )
            if len(op_stack) == 0:
                op_stack.append(tok)
            elif tok == '(':
                op_stack.append(tok)
            elif tok == ')': # evaluate to last paired '('
                while op_stack[-1] != '(':
                    evaluate_last()
                op_stack.pop()
            else:
                while should_evaluate_last(tok):
                    evaluate_last()
                op_stack.append(tok)
        else:
            num_stack.append(tok)
    while len(num_stack) > 1:
        evaluate_last()
    return num_stack[0]

3+5*6

  1. num: 3;  op: [] 
  2. num: 3;  op +
  3. num: 3 5;  op +
  4. num: 3 5;  op + *, * has higher priority than +, do nothing
  5. num: 3 5 6; op + *
  6. num: 3 30; op +
  7. num: 33; op []

7*(5+3)+2

  1. num: 7; op []
  2. num: 7; op *
  3. num: 7; op * (; do nothing
  4. num: 7 5; op * (
  5. num: 7 5; op * ( +; + has higher priority than (, do nothing
  6. num: 7 5 3; op * ( +
  7. num: 7 5 3; op * ( + ); should evaluate all between ()
  8. num: 7 15; op *
  9. num: 7 15; op * +, + has lower priority than *, should * first
  10. num: 105; op +
  11. num: 105 2; op +
  12. num: 107; op []

Wrap up


  • convert the original problem to an evaluation problem
  • use textbook data structures to implement a calculator 
  • We beat Logan(at his 15s), Yay!

  • https://github.com/observerss/linearequation




THANK you!

Solving linear equations

By jingchaohu

Solving linear equations

  • 1,676