2011-03-23 6 views
12

저는 PHP로 CAS (Computer Algebra System)를 만들었지 만 지금은 막혀 있습니다. this website을 사용 중입니다.컴퓨터 대수학 시스템 구축

이제 토크 나이저를 작성했습니다. 이것은 이런 식으로 변환한다 :

1+2x-3*(4-5*(3x)) 

이에 :

(그룹은 토큰의 또 다른 설정 됨)
NUMBER PLUS_OPERATOR NUMBER VAR[X] MINUS_OPERATOR NUMBER MULTIPLY_OPERATOR GROUP 

. 이 방정식을 어떻게 단순화 할 수 있습니까? 예, 당신이 할 수있는 일을 알고 있습니다 : X-vars를 추가하십시오, 그러나 그들은 하위 그룹에 있습니다. 그 토큰을 처리하는 데 사용할 수있는 가장 좋은 방법은 무엇입니까?

+0

귀하의 VAR 토큰은 관련 문자열이 있습니다. NUMBER 토큰에 연결된 숫자가없는 이유는 무엇입니까? –

+0

제약 조건에 따라 [OOP 해석기] (http://sourcemaking.com/design_patterns/interpreter)를 구성 해보십시오. 토큰을 다루는 것보다 쉬워야하며 나무가 그 자체를 표현해야합니다. – ircmaxell

+0

@David Heffernan : 표현식과 프로그래밍 언어를 다루는 PHP의 장점 중 하나는 sigil 변수입니다. 프로그래밍 언어의 키워드와의 충돌에 대해 걱정할 필요없이 변수'$ operator'와'$ var'의 이름을 지정할 수 있습니다. –

답변

18

정말 유용한 다음 단계는 파스 트리를 구성하는 것입니다 : 당신은 중위 파서를 작성하여 다음 중 하나를 만들 것

enter image description here

. 간단한 재귀 파서를 작성하거나 큰 총과 using a parser generator을 가져 와서이 작업을 수행 할 수 있습니다. 두 경우 모두, 그것은 형식적인 문법을 구성하는 데 도움이 문법은 2x 구문을 처리하지 않는

expression: additive 

additive: multiplicative ([+-] multiplicative)* 

multiplicative: primary ('*' primary)* 

primary: variable 
     | number 
     | '(' expression ')' 

참고하지만, 쉽게 추가 할 수 있어야합니다.

문법 규칙에서 재귀를 영리하게 사용합니다. primary은 변수, 숫자 및 괄호로 묶은 표현식 만 캡처하고 연산자로 실행되면 중지합니다. multiplicative* 기호로 구분 된 하나 이상의 primary 표현식을 구문 분석하지만 + 또는 - 기호로 실행되면 중지합니다. additive+-으로 구분 된 하나 이상의 multiplicative 수식을 구문 분석하지만 )으로 실행되면 중지합니다. 따라서 재귀 체계는 연산자 우선 순위를 결정합니다. 심지어 꽤 당신이 아름다운 파스 트리가 그래서 지금,

function parse() 
{ 
    global $tokens; 
    reset($tokens); 
    $ret = parseExpression(); 
    if (current($tokens) !== FALSE) 
     die("Stray token at end of expression\n"); 
    return $ret; 
} 

function popToken() 
{ 
    global $tokens; 
    $ret = current($tokens); 
    if ($ret !== FALSE) 
     next($tokens); 
    return $ret; 
} 

function parseExpression() 
{ 
    return parseAdditive(); 
} 

function parseAdditive() 
{ 
    global $tokens; 

    $expr = parseMultiplicative(); 

    for (;;) { 
     $next = current($tokens); 
     if ($next !== FALSE && $next->type == "operator" && 
      ($next->op == "+" || $next->op == "-")) 
     { 
      next($tokens); 
      $left = $expr; 
      $right = parseMultiplicative(); 
      $expr = mkOperatorExpr($next->op, $left, $right); 
     } else { 
      return $expr; 
     } 
    } 
} 

function parseMultiplicative() 
{ 
    global $tokens; 

    $expr = parsePrimary(); 

    for (;;) { 
     $next = current($tokens); 
     if ($next !== FALSE && $next->type == "operator" && 
      $next->op == "*") 
     { 
      next($tokens); 
      $left = $expr; 
      $right = parsePrimary(); 
      $expr = mkOperatorExpr($next->op, $left, $right); 
     } else { 
      return $expr; 
     } 
    } 
} 

function parsePrimary() 
{ 
    $tok = popToken(); 
    if ($tok === FALSE) 
     die("Unexpected end of token list\n"); 
    if ($tok->type == "variable") 
     return mkVariableExpr($tok->name); 
    if ($tok->type == "number") 
     return mkNumberExpr($tok->value); 
    if ($tok->type == "operator" && $tok->op == "(") { 
     $ret = parseExpression(); 
     $tok = popToken(); 
     if ($tok->type == "operator" && $tok->op == ")") 
      return $ret; 
     else 
      die("Missing end parenthesis\n"); 
    } 

    die("Unexpected $tok->type token\n"); 
} 

이 좋아, 그리고 : 나는 (see full example at ideone.com) 아래에했던 것처럼

이는 predictive parser 손으로 구현하기 너무 몹시 어려운 일이 아니다 그것으로가는 그림. 이제 뭐?귀하의 목표는 (지금은) 간단히 용어를 결합하여 양식의 결과를 얻을 수 있습니다.

n1*a + n2*b + n3*c + n4*d + ... 

나는 그 부분을 남겨 둘 것입니다. 구문 분석 트리를 사용하면 훨씬 간단하게 작업을 수행 할 수 있습니다.

+0

와우 ... 저는 스피치입니다. 덕분에 D가 매우 도움이되었습니다. D! –

+0

와우. SO 응답에 완전히 날아간 문법과 파서. 당신은 놀랍습니다 @ 조이 아담스 –

+0

파스 트리를 만드는 것이 쉬운 부분입니다. 이제는 그 위에 대수 연산을 구현해야합니다. 이것이 실제로 CAS를 만드는 것입니다. –

3

PHP는 문자열, 숫자 및 배열을 잘 처리합니다. 그러나 상징적 인 수식 조작을 구현하기에는 가난한 언어입니다. 왜냐하면 실제로 나무를 원한다는 "상징적 표현"을 처리 할 고유의 기계가 없기 때문입니다. 예, 모든 기계류를 구현할 수 있습니다. 더 단단한 것은 에 대수적 조작을합니다.. 아주 정교한 무언가를 만들고 싶다면 꽤 많은 일을해야합니다. 변환을 직접적이고 쉽게 작성하는 데 도움이되는 기계가 가장 이상적입니다.

예를 들어 임의의 대수 규칙을 어떻게 구현합니까? 연합성과 commutativity? 용어 "거리를두고 일치하는"?

(3*a+b)-2(a-b)+a ==> 3a-b 

당신은 우리의 DMS program transformation system를 사용하는 방법 a simple CAS can be implemented 볼 수 있습니다. DMS는 commutativity 및 associativity와 같은 어려운 수학적 구조를 가지고 있으며 대수학 규칙을 명시 적으로 작성하여 기호식에 적용 할 수 있습니다.

1

서적 Computer Algebra and Symbolic Computation: Mathematical Methods by Joel S. Cohen 에는 대수 표현식의 자동 단순화 알고리즘이 설명되어 있습니다.

이 알고리즘은 C#의 Symbolism 컴퓨터 대수 라이브러리에서 사용됩니다. 당신의 예를 다음과 같은 C# 프로그램을 간다 :

var x = new Symbol("x"); 

(1 + 2 * x - 3 * (4 - 5 * (3 * x))) 
    .AlgebraicExpand() 
    .Disp(); 

표시 콘솔에서 다음

-11 + 47 * x 
+1

사실 CS의 모든 대학원생은 LISP에서 이들 중 하나를 손가락으로 구현하기 시작합니다 -운동. 구현 개념은 이러한 시스템 중 첫 번째가 LISP (MacSyma?)에 구현되었을 때 60 년대로 거슬러 올라갑니다. 핵심 아이디어는 "표현을 나무로 표현하는 것"(LISP에서는 본질적으로 사소한 것임)이며 대수 규칙을 실현하는 절차를 작성하는 것입니다. 더 정교한 체계에는 손으로 직접 작성하는 절차 대신 패턴 일치 자/다시 쓰기 규칙이 포함됩니다. 이것에 대한 토론이 포함되어 있습니다.) Mathematica는 이것을 바탕으로 한 고전적인 CAS입니다. 다른 답변을보십시오 –

+0

@IraBaxter [mpl] (https://github.com/dharmatech/mpl)은 많은 알고리즘을 구현하는 Scheme 라이브러리입니다 코헨 본문에서. – dharmatech

관련 문제