Expansão Algébrica Básica

8

Problema

Eu tenho um GRANDE programa novo que mudará a maneira como pensamos em matemática na computação, absorvendo cadeias de funções algébricas e fazendo coisas INCRÍVEIS com elas! O único problema é que sou capaz de analisar álgebra específica, caso contrário o universo se dobra em si mesmo, o que é ruim. Felizmente, só preciso de algumas operações básicas na entrada deste incrível novo programa, mas ainda preciso expandi-la!

Regras

  • Uma resposta deve ser capaz de simplificar as seguintes expressões

    • 2+2 deve reduzir a 4
    • (5+x)+6 deve reduzir a x+11
    • (x+2)^2 deve reduzir a x^2+4*x+4
    • (x-5)*(3+7*x) deve reduzir a 7*x^2-32*x-15
    • 5*x+9*x deve reduzir a 14*x
    • (2*x^2)*x^3 deve reduzir a 2*x^5
  • As respostas devem poder COMPLETAMENTE remover parênteses, o que implica que toda a distribuição deve ocorrer.

  • As respostas devem poder lidar com todos os seguintes operadores e tokens padrão:

    • + (A função de adição)
    • - (A função de subtração)
    • * (A função de multiplicação)
    • ( (O parêntese esquerdo, usado para indicar um grupo)
    • ) (O parêntese direito, usado para indicar o final do último grupo iniciado)
    • x (A variável padrão)
    • [0-9]+ (números literais não negativos)
  • As respostas devem ser capazes de, pelo menos, quadrado, usando a notação expr ^ 2, incluindo (expr) ^ 2 recursivamente, pois (expr) é uma expressão;)

  • Uma solução deve estar em uma notação de infixo padrão, nenhum dos absurdos da RPN!

  • Nenhuma biblioteca funciona como a do Mathematica Simplifypara fazer isso por você.

  • A solução deve ser uma função que recebe um único argumento e retorna a versão expandida

Como se trata de código-golfe, a resposta com o menor número de pressionamentos (chave) vence, 1 semana a partir do OP.

Notas

Não há espaços neste mundo da matemática, é claro! Apenas parênteses.

Portanto, nenhuma divisão é necessária para salvar da fatoração

A ordem padrão de operações se aplica.

Estou ciente de que parte do que estou pedindo é simplificação (por exemplo 2+2=4), onde outras partes são realmente o oposto, como expandir (x+1)^2para ser x^2+2x+1. Isso é intencional. :)

-25 golpes para uma solução que pode fazer (expr) ^ n em vez de apenas (expr) ^ 2

-15 traçados para uma solução capaz de avaliar a multiplicação justaposta, como 4x+5x== 9xou 4(x+1)=4x+4

-5 traços para uma solução capaz de lidar com várias variáveis ​​(uma variável sendo exatamente um caractere do alfabeto em minúsculas)

-5 golpes para uma solução capaz de remover os 0s iniciais ( 007apenas 7[hoje não, Bond!] [Caramba, agora sinto que estou escrevendo o Lisp])

Christopher Wirt
fonte
Eu acredito que (x-5) * (3 + 7 * x) é (1 * x + -5) * (7 * x + 3) é 7 * x ^ 2 + (3-35) * x + - 15 ou 7 * x ^ 2-32 * x-15
Jeff Grigg
Como a entrada deve ser fornecida? Argumentos da linha de comando? Entrada padrão? Provavelmente deve ser especificado.
Frxstrem 9/07
@ JeffGrigg É por isso que preciso do programa;) Hehe, na verdade, adicionei o 7 * x para exibir mais complexidade e esqueci de atualizar a solução. Obrigado! E eu poderia jurar que incluiu que deve ser tomado como um argumento para uma função
Christopher Wirt
1
Eu quase tenho uma solução funcional para isso em J, só preciso analisar números negativos corretamente. É provavelmente a coisa mais feia e hacki que eu já escrevi, mas felizmente está quase no fim.
algorithmshark
1
@ssdecontrol sem divisão, estamos falando de polinômios. Adicionar a divisão e é uma coisa muito diferente
edc65

Respostas:

2

J - 350 char - 25 = 325 pts

Perdoe-me, Roger Hui, porque pequei.

x=:0 1
f=:(('+_';'-';'_';'-')rplc~' '-.~'+'}.@,@|.@,.s(}.@]`(,&":)@.t'*x'"_`('*x^',":)`(''"_)@.t=.*@<:@[)/"1@#~0~:0{|:@s=.,.i.@#)@p=:(0{::'()'+/@,:&,e'+'@((+/@,:&,-)~e'-')@(*/a e'*')@('^'(*/(a=.+//.@)/@#,:e=:2 :'(<@; ::({:u/&.>{.)/.~i.@#-i.@#(>+>:)(<,v){.@i.~])^:_'))@:(([^:(''-:])". ::])&.>)@([-.~;@]<@(p^:(1<#));.1~1,0=2*/\[:+/\(*0,}:@)1 _1 0{~i.)&;:])

A monstruosidade acima define um número de variáveis, das quais a variável fé uma função que satisfaz as restrições da pergunta acima. Eu reivindico o bônus "expr ^ n" por 25 pontos.

Aqui está o golfe em ação no J REPL.

   x=:0 1
   f=:(('+_';'-';'_';'-')rplc~' '-.~'+'}.@,@|.@,.s(}.@]`(,&":)@.t'*x'"_`('*x^',":)`(''"_)@.t=.*@<:@[)/"1@#~0~:0{|:@s=.,.i.@#)@p=:(0{::'()'+/@,:&,e'+'@((+/@,:&,-)~e'-')@(*/a e'*')@('^'(*/(a=.+//.@)/@#,:e=:2 :'(<@; ::({:u/&.>{.)/.~i.@#-i.@#(>+>:)(<,v){.@i.~])^:_'))@:(([^:(''-:])". ::])&.>)@([-.~;@]<@(p^:(1<#));.1~1,0=2*/\[:+/\(*0,}:@)1 _1 0{~i.)&;:])

   f '2+2'
4
   f '(5+x)+6'
x+11
   f '(x+2)^2'
x^2+4*x+4
   f '(x-5)*(3+7*x)'
7*x^2-32*x-15
   f '5*x+9*x'
14*x
   f '(2*x^2)*x^3'
2*x^5
   f '(2*x+3)^(2+3)*2^3'   NB. bonus
256*x^8+1920*x^7+5760*x^6+8640*x^5+6480*x^4+1944*x^3

Aqui está a essência do que está acontecendo.

  • '()'...@([-.~;@]<@(p^:(1<#));.1~1,0=2*/\[:+/\(*0,}:@)1 _1 0{~i.)&;:- Divida recursivamente a expressão com base em suas subexpressões entre parênteses e avalie-as (o ...bit a ser descrito abaixo) à medida que estiverem prontas. ;:executa a tokenização.
  • (([^:(''-:])". ::])&.>)- Avalie átomos. Se o átomo for numérico, ele será transformado em um número escalar. Se o átomo é um operador, ele é deixado como está. Se o átomo for a variável x, ele será transformado no vetor 0 1. (É por isso que definimos x=:0 1no início.) Em geral, armazenamos o polinômio a*x^n + b*x^(n-1) + ... + c*x + dcomo a n+1lista -item d c ... b a.
  • e=:2 :'(<@; ::({:u/&.>{.)/.~i.@#([->+>:){.@i.&(<,v))^:_'- Essa curiosa conjunção leva um verbo à esquerda e um caractere à direita. Ele encontra a primeira instância desse caractere em sua entrada tokenizada e sem parêntesis, avalia-o com os argumentos ao seu redor e repete até que não haja mais caracteres a serem encontrados. Assim, simplificamos iterativamente a expressão, com a ordem das operações controlada pela ordem de aplicação de e.
  • E então todo o material na frente é apenas uma impressão bonita. rplcé uma função de biblioteca padrão para substituição de substring. Podemos salvar outros três caracteres se for permitido colocar os termos de grau mais baixo primeiro, em vez dos mais altos, removendo o @|..

Sinceramente, não tenho certeza se posso extrair mais personagens deste golfe. Não consigo pensar em nenhuma outra abordagem que não exija uma quantidade semelhante de excesso de engenharia, mas isso não significa que não exista. De qualquer forma, tenho certeza de que espremi todos os personagens óbvios desta versão.

algoritmshark
fonte
Bravo! E você pode se livrar da correção do número negativo, pois o -token é anotado apenas como a função de subtração. :)
Christopher Wirt
@ChristopherWirt Não entendi isso. De qualquer maneira, pronto.
algorithmshark
(2*x+3)^(2+3)*2^3deve dar 256x^5+1920x^4+5760x^3+8640x^2+6480x+1944ou estou faltando alguma coisa?
edc65
2

JavaScript (EcmaScript 6) 698 (bônus 748-50) 725 780 1951

Para ser jogado golfe . Mas tenho orgulho de que funcione.
(Edit: correção de bug, problemas com colchetes e menos)
(Edit2: jogou mais, bug corrigido na saída)
(Edit3: jogou mais uma vez, última vez que prometo)

Comente

Basicamente, a função X é uma calculadora de infix que opera em polinômios literais.
Cada polinômio é armazenado como um objeto js, ​​sendo a chave o termo (x ^ 3y ^ 2) e o valor o coeficiente numérico. As funções A, M e E são soma, multiplicação e expoente.

Código de golfe

(Provavelmente não pode ser jogado golfe mais ...) Nota: sem contar novas linhas adicionadas para (ehm ...) legibilidade

K=x=>Object.keys(x).sort(),
A=(p,q,s,t,c)=>[(c=(s+1)*q[t]+~~p[t])?p[t]=c:delete(p[t])for(t in q)],
M=(p,q,t,c,o,u,f,r,v={})=>([A(v,(r={},[(c=p[f]*q[t])&&(r[o={},(u=f+t)&&(u.match(/[a-z]\^?\d*/ig).map(x=>o[x[0]]=~~o[x[0]]+(~~x.slice(2)||1)),K(o,u=k).map(i=>u+=o[i]>1?i+'^'+o[i]:i)),u]=c)for(f in p)],r),k)for(t in q)],v),
E=(p,n)=>--n?M(p,E(p,n)):p,
O=n=>{for(l=0;h(o=s.pop())>=h(n);)a=w.pop(b=w.pop()),o=='*'?a=M(a,b):o>d?a=E(a,b[k]):A(a,b,o),w.push(a);s.push(o,n)},
X=e=>(l=k='',w=[],s=[d='@'],h=o=>'@)+-*^('.indexOf(o),
(e+')').match(/\D|\d+/g).map(t=>(u=h(t))>5?(l&&O('*'),s.push(d)):u>1?O(t):~u?s.pop(s.pop(O(t)),l=1):(l&&O('*'),l=1,w.push(v={}),t<d?v[k]=t|0:v[t]=1)),
K(p=w.pop()).map(i=>(k+=(c=p[i])>1?s+c+i:c<-1?c+i:(c-1?'-':s)+(i||1),s='+')),k||0)

Teste no console do FireFox

['2+2','(5+x)+6','(x+2)^2','(a+b)(a-b)','(a+b)*(a-b)','(a-b)(a-b)', '(x-5)*(3+7*x)','5*x+9*x','(2*x^2)*x^3','(2*x+3)^(2+3)*2^3']
.map(x => x + ' => ' + X(x)).join('\n')

Resultado

2+2 => 4
(5+x)+6 => 11+x
(x+2)^2 => 4+4x+x^2
(a+b)(a-b) => a^2-b^2
(a+b)*(a-b) => a^2-b^2
(a-b)(a-b) => a^2-2ab+b^2
(x-5)*(3+7*x) => -15-32x+7x^2
5*x+9*x => 14x
(2*x^2)*x^3 => 2x^5
(2*x+3)^(2+3)*2^3 => 1944+6480x+8640x^2+5760x^3+1920x^4+256x^5

Bônus

25: 2*x+3)^(2+3)*2^3 => 1944+6480x+8640x^2+5760x^3+1920x^4+256x^5
15: 4x+5x => 9x
5: 007x+05x^(06+2) => 7x+5x^8
5: (a+b)(a-b) => a^2-b^2

Código Ungolfed

Show=(p)=>(
  Object.keys(p).sort().map(i=>(c=p[i])-1?c+1?c+i:'-'+i:i).join('+').replace(/\+-/g,'-')
)
AddMonoTo=(poly, term, coeff, c)=>(
  (c = coeff + ~~poly[term]) ? poly[term]= c : delete(poly[term])
)
AddPolyTo=(p1, p2, s, t)=>(
  [ AddMonoTo(p1, t, (s+1)*p2[t]) for (t in p2)]
)
MulTerm=(t1, t2, o={})=>(
  (t1+=t2)&&
  (t1.match(/[a-z](\^\d+)?/g).every(x=>o[x[0]]=(~~x.slice(2)||1)+(~~o[x[0]])),
  Object.keys(o,t1='').sort().every(i=>t1+=o[i]>1?i+'^'+o[i]:i)),
  t1  
)
MulMono=(poly, term, coeff, c, r={}, t)=>(
  [(c = poly[p]*coeff) && (r[MulTerm(p,term)] = c) for (p in poly) ],r
)  
MulPoly=(p1, p2, r={}, p)=>(
  [AddPolyTo(r,MulMono(p2, p, p1[p]),'') for (p in p1)],r
)
ExpPoly=(p,n,r=p)=>{
  for(;--n;)r=MulPoly(r,p);
  return r
}
Expand=ex=>
{
  var tokens = ex.match(/\D|\d+/g).push(')')
  var t,a,b,v,LastV=0
  var vstack=[]
  var ostack=['_']
  var op={ '+': 3, '-':3, '*':4, '^':6, '(':8, _: 1, ')':2}
  var OPush=o=>ostack.push(o)
  var OPop=_=>ostack.pop()
  var VPush=v=>vstack.push(v)
  var VPop=v=>vstack.pop()

  var Scan=i=>
  {
    LastV=0 ;
    for (; (t=tokens[i++]) && (t != ')'); )
    {
      if (t == '(')  
      {
        if (LastV) CalcOp('*')
        OPush('_'), i=Scan(i)
      }
      else if (op[t])
        LastV=0, CalcOp(t)
      else
      {
        if (LastV) CalcOp('*')
        LastV = 1
        VPush(v={})
        t < 'a' ? v[''] = t|0 : v[t] = 1
      }
    }
    CalcOp(t);
    OPop();
    OPop();
    LastV=1;
    return i;
  }
  var CalcOp=nop=>
  {
    for (; op[po = OPop()] >= op[nop];)
      b=VPop(), a=VPop(), CalcV(a,b,po);
    OPush(po), OPush(nop);
  }
  var CalcV=(a,b,o)=>
  {
    if (op[o] < 4) 
      AddPolyTo(a,b,o)
    else if (o == '*')
      a=MulPoly(a,b)
    else // ^
      a=ExpPoly(a,b['']) // only use constant term for exp
    VPush(a)
  }
  Scan(0)

  return Show(vstack.pop())
}
edc65
fonte
você usa ponto e vírgula apenas em alguns lugares? É por falta de quadro que você precisa deles? Eu não sou complacente muito EcmaScript com a minha JS;)
Christopher Wirt
Vírgulas separam a expressão, ponto e vírgula separam as instruções. É mais claro for(i1,i2,i3; c1,c2; s1,s2,s3). Eu tenho apenas um ponto e vírgula no campo golfed para fechar uma instrução dentro de um for (função O). Por que vírgulas? dois stamenents devem ser agrupados com {} enquanto duas ou mais expressões podem ser simplesmente listadas.
edc65
1

Matemática: 56 - 25 - 15 - 5 - 5 = 6

É claro que nenhuma biblioteca funciona como Expandou Distributeapenas substituição de padrões. O Mathematica faz quase tudo por nós (hax!), O que resta são apenas regras para distribuição e expansão de energia. Os únicos exemplos não triviais, portanto, são (x-5)*(3+7*x)e (x+2)^2.

f=#//.{x_ y_Plus:>(x #&/@y),x_Plus^n_:>(x #&/@x)^(n-1)}&

Exemplos

(x-5)*(3+7*x) // f
-15 - 32 x + 7 x^2

(x + 2)^2 // f
4 + 4 x + x^2
swish
fonte
Você tem alguns personagens muito estranhos nos dois exemplos. Você pode limpá-lo?
edc65
@ edc65 M10 agindo de forma estranha com copiar / colar, eu acho.
swish
Eu provavelmente deveria ter banido o mathematica todos os outros;) bom trabalho, no entanto #
Christopher Wirt