Interpolazione polinomiale


12

Scrivi un programma che esegue l' interpolazione polinomiale usando numeri razionali di precisione arbitraria reale. L'input è simile al seguente:

f (1) = 2/3
f (2) = 4/5
f (3) = 6/7
...

Si può presumere che ci sia esattamente uno spazio bianco prima e dopo il =segno, tutti i numeri sono frazioni o numeri interi. Si può anche supporre che tutta la frazione nell'input sia già irriducibile.

Non è necessario il controllo degli errori, si può presumere che l'input sia valido e che nessuna x sia raddoppiata in f (x).

L'output dovrebbe essere in una forma compatibile LaTeX, il codice LaTeX emesso dovrebbe produrre la stessa rappresentazione grafica dell'output qui fornito.

f (x) = 123x ^ 2 + \ frac {45} {2} x + \ frac {7} {4}

La frazione deve essere ridotta il più possibile, ad es. qualcosa del genere \frac{2}{4} non è permesso. Se il numero è intero, non utilizzare una frazione.

Regole speciali:

Il tuo programma dovrebbe ...

  • lavorare per polinomi fino al grado 12
  • completare in meno di 1 minuto per un input ragionevole
  • non utilizzare alcuna funzione che esegua l'intero calcolo per te
  • emette il polinomio del più piccolo grado possibile

Casi test:

I test dati forniti sono solo per chiarimenti. Il tuo programma dovrebbe produrre risultati corretti per tutti gli input corretti.

Ingresso

f (1) = 2/3
f (2) = 4/5
f (3) = 6/7

Produzione

f (x) = - \ frac {4} {105} x ^ 2
       + \ frac {26} {105} x
       + \ frac {16} {35}

Ingresso

f (-12) = 13/2
f (5/3) = 3/5
f (13) = -6
f (1/5) = -3/4

Produzione

f (x) = - \ frac {2186133} {239455744} x ^ 3
       + \ frac {2741731} {149659840} x ^ 2
       + \ frac {26720517} {29201920} x
       - \ frac {279464297} {299319680}

Ingresso

f (4/3) = 617/81
f (2) = 20/3
f (-8/3) = 6749/81
f (-5) = 7367/12
f (0) = 23/3

Produzione

f (x) = \ frac {1} {2} x ^ 4
     - 2x ^ 3
     + \ frac {7} {4} x ^ 2
     + \ frac {23} {3}

Ingresso

f (0) = 5
f (1) = 7
f (2) = 9
f (3) = 11
f (4) = 13

Produzione

f (x) = 2x
     + 5

Ingresso

f (1/2) = -1/2
f (-25) = -1/2
f (-54/12) = -1/2

Produzione

f (x) = - \ frac {1} {2}

Perché stai parlando di numeri reali se tutto ciò che stai usando sono numeri razionali?
Joey,

Scusa. Il mio inglese è scarso. Sì, usa solo numeri razionali. I risultati devono essere esatti.
FUZxxl

Nel primo testcase, i punti ( ...) fanno davvero parte dell'input?
Eelvex,

@Eelvex: No. Fisso.
FUZxxl

L'output per il terzo testcase è errato. La risposta corretta è -\frac{37745}{14592}x^4 - \frac{853249}{43776}x^3 + \frac{57809}{7296}x^2 + \frac{225205}{2736}x + \frac{23}{3}. Ho il sospetto che l'input avrebbe dovuto essere qualcosa di diverso :)
Timwi

Risposte:


3

J + sh

J script:

i=:0".(1!:1)3
i=:((-:#i),2)$i
c=:|.(%.(x:((i.#i)^~])"0({."1 i)))(+/ .*)(|:{:"1 i)
(":(-.0=c)#(c,.i.-#c))(1!:2)2

sh script:

echo -n 'f(x) = '
tr -d 'f()=' | tr /\\n- r' '_  | ./polyint.ijs | sed -e 's/^/+/;s/_/-/;s/\([0-9]*\)r\([0-9]*\)/\\frac{\1}{\2}/;s/ \([0-9]*$\)/x^\1/;s/\^1//;s/x^0//;s/+\(.*-.*\)/\1/'

Esegui lo script sh:

./pol-int.sh
f(1/2) = -1/2
f(-25) = -1/2
f(-54/12) = -1/2

f(x) = -\frac{1}{2}

.

./pol-int.sh
f(4/3) = 617/8
f(2) = 20/3
f(-8/3) = 6749/81
f(-5) = 7367/12
f(0) = 23/3

f(x) = -\frac{37745}{14592}x^4
       -\frac{853249}{43776}x^3
     +  \frac{57809}{7296}x^2
     + \frac{225205}{2736}x
     +  \frac{23}{3}

Non è necessario creare esattamente la stessa formattazione del codice sorgente. Nell'uscita LaTeX. Dovrebbe produrre la stessa rappresentazione grafica dopo aver eseguito LaTeX. Sentiti libero di salvare alcuni caratteri.
FUZxxl

Non riesco a leggere J, ma dalla breve lunghezza prendo ciò significa che J ha una funzione integrata per la forma di echelon della matrice?
Timwi,

@Timwi: No, ma uso la "matrice inversa" incorporata. J è molto conciso: anche se dovessi implementare la "matrice inversa" sarebbe lunga alcuni caratteri.
Eelvex,

3

Perl (569 caratteri)

use Math'BigInt;sub r{($u,$v)=@_;$v?r($v,$u%$v):$u}sub c{new Math'BigInt$_[0]}$a=@c=<>;for(@c){m!(-?\d+)/?(\d*). = (-?\d+)/?(\d*)!;$j{$_,$i}=$1**c$_,$k{$_,$i|=0}=($2||1)**c$_ for 0..$a;$j{$a,$i}=c$3;$k{$a,$i++}=c$4||1}for$p(0..$a-1){for$y(0..$p-1,$p+1..$a-1){$n=$j{$p,$y}*$k{$p,$p};$o=$k{$p,$y}*$j{$p,$p};$j{$_,$y}=$j{$_,$y}*$k{$_,$p}*$o-$k{$_,$y}*$j{$_,$p}*$n,$k{$_,$y}*=$k{$_,$p}*$o for 0..$a}}print"f(x)=";for(1..$a){$s=r$t=$j{$a,$p=$a-$_}*$k{$p,$p},$w=$k{$a,$p}*$j{$p,$p};$u=abs$t,print$t>0?"$z":'-',($z='+',$w/=$s)-1?"\\frac{$u}{$w}":$u,$p>1?"x^$p":x x$p if$t/=$s}

Spiegazione dettagliata:

use Math'BigInt;

# Subroutine to calculate gcd of two numbers
sub r{($u,$v)=@_;$v?r($v,$u%$v):$u}

# Subroutine to create BigInts
sub c{new Math'BigInt$_[0]}

# Read input
# Throughout, $a is the number of equations.
$a=@c=<>;

# Initialises the $a+1 × $a matrix with all the values.
# $j{$x,$y} contains the numerator, $k{$x,$y} the denominator.
for(@c)
{
    m!(-?\d+)/?(\d*). = (-?\d+)/?(\d*)!;

    # Puzzle for the reader: why is $i|=0 in the second one,
    # not the first one? Answer at the bottom!
    $j{$_,$i}=$1**c$_,$k{$_,$i|=0}=($2||1)**c$_ for 0..$a;
    $j{$a,$i}=c$3;
    $k{$a,$i++}=c$4||1
}

# Generates the matrix echelon form.
# Basically, it works like this:
for$p(0..$a-1)
{
    # For each element along the diagonal {$p,$p}, set all the values above and
    # below it to 0 by adding a multiple of row $p to each of the other rows.
    for$y(0..$p-1,$p+1..$a-1)
    {
        # So we need to multiply the row $p by the value of {$p,$y}/{$p,$p}
        # (stored in $n/$o) and then subtract that from row $y.
        $n=$j{$p,$y}*$k{$p,$p};
        $o=$k{$p,$y}*$j{$p,$p};
            $j{$_,$y}=$j{$_,$y}*$k{$_,$p}*$o-$k{$_,$y}*$j{$_,$p}*$n,
            $k{$_,$y}*=$k{$_,$p}*$o
        for 0..$a
    }
}

# Outputs the result
print"f(x)=";
for(1..$a)
{
    # Note this sets $p = $a-$_. $p is the power of x.
    # We need to divide {$a,$p} by {$p,$p}. Store the result in $t/$w.
    # We also need to put the fraction in lowest terms, so calculate the gcd.
    $s=r$t=$j{$a,$p=$a-$_}*$k{$p,$p},$w=$k{$a,$p}*$j{$p,$p};

    # Output this term only if the numerator ($t) is non-zero.
    # Output a plus sign only if this isn’t the first term.
    # Output a fraction only if the denomator ($w) isn’t 1.
        $u=abs$t,print$t>0?"$z":'-',
        ($z='+',$w/=$s)-1?"\\frac{$u}{$w}":$u,$p>1?"x^$p":x x$p
    if$t/=$s
}

# Answer to the puzzle buried in the code above:
# It’s because the second part is passed as a second argument to c,
# hence it is evaluated before the first part.

Commenti

  • Sono sicuro che esiste un modulo per la manipolazione della matrice che fornisce una funzione per il modulo echelon. In particolare non l'ho usato (non ne ho nemmeno cercato uno) perché presumo sia il punto di questo concorso farlo da soli. È anche più interessante in questo modo. Ovviamente si potrebbe dire lo stesso di BigInt, ma sospetto che nessuno tenterà questa sfida ...

Le modifiche

  • (630 → 585) Realizzato che posso fare la forma di scaglione in un ciclo invece di due. Aggiungi spiegazione come commenti nel codice.

  • (585 → 583) Ho appena scoperto la sintassi del pacchetto che mi consente di utilizzare 'anziché ::.

  • (583 → 573) Ancora un po 'di microgolf

  • (573 → 569) Espressione regolare più breve per analizzare l'input


Continuo a ricevere errori di compilazione: ideone.com/LoB2T
FUZxxl

@FUZxxl: grazie per averlo sottolineato. C'era solo uno spazio mancante. Riparato ora.
Timwi

3

TI-Basic (83/84): 109 caratteri

Tecnicamente 109 caratteri, TI-Basic conta dim (, For (, ->, rref (, [A], e elenca come "un carattere").

L'ingresso è formattato in L1 e L2, in coppie (x, y) [ex L1 = (1,2,3,4), L2 = (2,3,5,7)].

{1,1}->dim([A]
{dim(L1),dim(L2)+1}->dim([A]
For(A,1,dim(L1)
For(B,dim(L1)-1,0,-1
L1(A)^B->[A](A,dim(L1)-B
End
L2(A->[A](A,dim(L1)+1
End
rref([A]->[A]
{0}->L3
For(A,1,dim(L1)
[A](A,dim(L1)+1->L3(A
End
Disp L3

1
Questo non usa razionali o forma LaTeX.
lirtosiast,

1

Metodo Lagrange, Python, 199 byte

Un po 'in ritardo, ma ...

def lagrange(dp):
l = lambda i: lambda x: (prod([(x - dp[j][0]) / (dp[i][0] - dp[j][0]) for j in range(len(dp)) if i != j]))
return lambda x: sum([l(i)(x) * dp[i][1] for i in range(len(dp))])

1
Probabilmente non hai bisogno di tutto quello spazio bianco attorno agli operatori, vero?

0
l=lambda D,i:lambda x:prod((x-Dj[0])/(D[i][0]-Dj[0])for j,Dj in enumerate(D)if i!=j)
L=lambda D:lambda x:sum(l(D,i)(x)*Di[1]for i,Di in enumerate(D))

Solo una versione abbreviata del codice di Fred Freys. Si noti che probabilmente si potrebbe saltare il passaggio da D a l, poiché può semplicemente estrarlo dall'ambito esterno. Come probabilmente puoi fare lo stesso con me qui, potremmo persino radere una lambda. Lo proverò un giorno.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.