Differenziazione simbolica dei polinomi


20

Differenziazione simbolica 1: Gone Coefishin '

Compito

Scrivi un programma che accetta un polinomio in x da stdin (1 <deg (p) <128) e lo differenzia. Il polinomio di input sarà una stringa nella seguente forma:

"a + bx + cx^2 + dx^3 +" ...

dove il coefficiente di ciascun termine è un numero intero (-128 <a <128). Ogni termine è separato da uno spazio, un + e un altro spazio; termini lineari e costanti appaiono come sopra (cioè, no x^0o x^1). I termini appariranno in ordine crescente, e quei poteri con coefficiente zero sono omessi. Tutti i termini con coefficiente 1 o -1 visualizzano esplicitamente quel coefficiente.

L'output deve avere esattamente la stessa forma. Si noti che i coefficienti nell'output potrebbero essere grandi quanto 127 * 127 == 16129.

Esempi

"3 + 1x + 2x^2" ==> "1 + 4x"
"1 + 2x + -3x^2 + 17x^17 + -1x^107" ==> "2 + -6x + 289x^16 + -107x^106"
"17x + 1x^2" ==> "17 + 2x"

punteggio

Il tuo punteggio è la lunghezza del tuo programma in byte, moltiplicato per tre se usi un built-in o una libreria che fa un'algebra simbolica.


Non riesco a credere che non abbiamo già avuto questa sfida qui!
flawr

5
@flawr L' abbiamo fatto. (Anche se uno richiedeva anche altre funzioni e non aveva regole rigorose sul formato di output.)
Martin Ender

@flawr Ho pensato la stessa cosa ... eppure non ho trovato il post collegato alla ricerca di Martin. Ah bene.
hYPotenuser

Risposte:


15

Retina , 53 43 42 41 40 35 byte

^[^x]+ |(\^1)?\w(?=1*x.(1+)| |$)
$2

Ai fini del conteggio ogni riga va in un file separato, ma è possibile eseguire quanto sopra come un singolo file invocando Retina con il -sflag.

Questo si aspetta che i numeri nella stringa di input vengano forniti in modo unario e genererà output nello stesso formato. Per esempio

1 + 11x + -111x^11 + 11x^111 + -1x^11111
-->
11 + -111111x + 111111x^11 + -11111x^1111

invece di

1 + 2x + -3x^2 + 2x^3 + -1x^5
-->
2 + -6x + 6x^2 + -5x^4

Spiegazione

Il codice descrive una singola sostituzione regex, che è sostanzialmente 4 sostituzioni compresse in una. Nota che solo uno dei rami riempirà il gruppo, $2quindi se una qualsiasi delle altre tre corrisponde, la corrispondenza verrà semplicemente eliminata dalla stringa. Quindi possiamo esaminare i quattro diversi casi separatamente:

^[^x]+<space>
<empty>

Se è possibile raggiungere uno spazio dall'inizio della stringa senza incontrarne uno xsignifica che il primo termine è il termine costante e lo eliminiamo. A causa dell'avidità di +, questo corrisponderà anche al più e al secondo spazio dopo il termine costante. Se non esiste un termine costante, questa parte semplicemente non corrisponderà mai.

x(?= )
<empty>

Questo corrisponde a uno xche è seguito da uno spazio, ovvero il xtermine del termine lineare (se esiste) e lo rimuove. Possiamo essere sicuri che ci sia uno spazio dopo di esso, perché il grado del polinomio è sempre almeno 2.

1(?=1*x.(1+))
$1

Ciò esegue la moltiplicazione del coefficiente per l'esponente. Questo corrisponde a un singolo 1nel coefficiente e lo sostituisce con l'intero esponente corrispondente tramite il lookahead.

(\^1)?1(?= |$)
<empty>

Ciò riduce tutti gli esponenti rimanenti abbinando il trascinamento 1(garantito dal lookahead). Se è possibile abbinare ^11(e un limite di parole) lo rimuoviamo invece, il che si occupa di visualizzare correttamente il termine lineare.

Per la compressione, notiamo che la maggior parte delle condizioni non si influenzano a vicenda. (\^1)?non corrisponderà se il lookahead nel terzo caso è vero, quindi possiamo mettere insieme quei due come

(\^1)?1(?=1*x.(1+)| |$)
$2

Ora abbiamo già il lookahead necessario per il secondo caso e gli altri non possono mai essere veri durante la corrispondenza x, quindi possiamo semplicemente generalizzare il 1a \w:

(\^1)?\w(?=1*x.(1+)| |$)
$2

Il primo caso non ha davvero nulla in comune con gli altri, quindi lo teniamo separato.


9

CJam, 43 41 byte

Qleu'^/';*'+/{~:E[*'x['^E(]]E<}/]1>" + "*

Grazie a @ jimmy23013 per aver segnalato un bug e aver giocato a golf due byte!

Provalo online nell'interprete CJam .

Come funziona

Q           e# Leave an empty array on the bottom of the stack.
l           e# Read a line from STDIN.
eu'^/';*    e# Convert to uppercase and replace carets with semicolons.
'+/         e# Split at plus signs.

{           e# For each resulting chunk:
  ~         e#   Evaluate it. "X" pushes 1 and ";" discards.
            e#   For example, "3X" pushes (3 1) and "3X;2" (3 2).
   :E       e#   Save the rightmost integer (usually the exponent) in E.
   [        e#
     *      e#   Multiply both integers.
            e#   For a constant term c, this repeats the empty string (Q) c times.
     'x     e#   Push the character 'x'.
     ['^E(] e#   Push ['^' E-1].
   ]        e#
   E<       e#  Keep at most E elements of this array.
            e#  If E == 1, 'x' and ['^' E-1] are discarded.
            e#  If E == 2, ['^' E-1] is discarded.
            e#  If E >= 3, nothing is discarded.
}/          e#

]           e# Wrap the entire stack in an array.
1>          e# Discard its first element.
            e# If the first term was constant, this discards [""]. ["" 'x']
            e# or ["" 'x' ['^' E-1]], depending on the constant.
            e# In all other cases, it discards the untouched empty array (Q).
" + "*      e# Join all kept array elements, separating by " + ".

5

Perl, 64 63 byte

Codice 62b + 1 riga comandi (-p)

Non eccezionale al momento, ma continuerò a provare ad accorciarlo.

s/(\d+)x.(\d+)/$1*$2."x^".($2-1)/eg;s/\^1\b|^\d+ . |x(?!\^)//g

Esempio di utilizzo:

echo "1 + 2x + 3x^2" | perl -pe 's/(\d+)x.(\d+)/$1*$2."x^".($2-1)/eg;s/\^1\b|^\d+ . |x(?!\^)//g'

Grazie Denis per -1b


5

Julia, 220 byte

Nessuna espressione regolare!

y->(A=Any[];for i=parse(y).args[2:end] T=typeof(i);T<:Int&&continue;T==Symbol?push!(A,1):(a=i.args;c=a[2];typeof(a[3])!=Expr?push!(A,c):(x=a[3].args[3];push!(A,string(c*x,"x",x>2?string("^",ex-1):""))))end;join(A," + "))

Questo crea una funzione lambda che accetta una stringa e restituisce una stringa. Le viscere imitano ciò che accade quando viene valutato il codice Julia: una stringa viene analizzata in simboli, espressioni e chiamate. Potrei effettivamente provare a scrivere una funzione di differenziazione simbolica completa di Julia e inviarla come parte di Julia.

Ungolfed + spiegazione:

function polyderiv{T<:AbstractString}(y::T)

    # Start by parsing the string into an expression
    p = parse(y)

    # Define an output vector to hold each differentiated term
    A = Any[]

    # Loop through the elements of p, skipping the operand
    for i in p.args[2:end]

        T = typeof(i)

        # Each element will be an integer, symbol, or expression.
        # Integers are constants and thus integrate to 0. Symbols
        # represent x alone, i.e. "x" with no coefficient or
        # exponent, so they integrate to 1. The difficulty here
        # stems from parsing out the expressions.

        # Omit zero derivatives
        T <: Int && continue

        if T == Symbol
            # This term will integrate to 1
            push!(A, 1)
        else
            # Get the vector of parsed out expression components.
            # The first will be a symbol indicating the operand,
            # e.g. :+, :*, or :^. The second element is the
            # coefficient.
            a = i.args

            # Coefficient
            c = a[2]

            # If the third element is an expression, we have an
            # exponent, otherwise we simply have cx, where c is
            # the coefficient.
            if typeof(a[3]) != Expr
                push!(A, c)
            else
                # Exponent
                x = a[3].args[3]

                # String representation of the differentiated term
                s = string(c*x, "x", x > 2 ? string("^", x-1) : "")

                push!(A, s)
            end
        end
    end

    # Return the elements of A joined into a string
    join(A, " + ")
end

3

C, 204 162 byte

#define g getchar()^10
h,e;main(c){for(;!h&&scanf("%dx%n^%d",&c,&h,&e);h=g?g?e?printf(" + "):0,0:1:1)e=e?e:h?1:0,e?printf(e>2?"%dx^%d":e>1?"%dx":"%d",c*e,e-1):0;}

Fondamentalmente analizzare ogni termine e stampare il termine differenziato in sequenza. Abbastanza semplice.


2

JavaScript ES6, 108 byte

f=s=>s.replace(/([-\d]+)(x)?\^?(\d+)?( \+ )?/g,(m,c,x,e,p)=>x?(c*e||c)+(--e?x+(e>1?'^'+e:''):'')+(p||''):'')

Snippet ES5:

// ES5 version, the only difference is no use of arrow functions.
function f(s) {
  return s.replace(/([-\d]+)(x)?\^?(\d+)?( \+ )?/g, function(m,c,x,e,p) {
    return x ? (c*e||c) + (--e?x+(e>1?'^'+e:''):'') + (p||'') : '';
  });
}

[
  '3 + 1x + 2x^2',
  '1 + 2x + -3x^2 + 17x^17 + -1x^107',
  '17x + 1x^2'
].forEach(function(preset) {
  var presetOption = new Option(preset, preset);
  presetSelect.appendChild(presetOption);
});

function loadPreset() {
  var value = presetSelect.value;
  polynomialInput.value = value;
  polynomialInput.disabled = !!value;
  showDifferential();
}

function showDifferential() {
  var value = polynomialInput.value;
  output.innerHTML = value ? f(value) : '';
}
code {
  display: block;
  margin: 1em 0;
}
<label for="presetSelect">Preset:</label>
<select id="presetSelect" onChange="loadPreset()">
  <option value="">None</option>
</select>
<input type="text" id="polynomialInput"/>
<button id="go" onclick="showDifferential()">Differentiate!</button>
<hr />
<code id="output">
</code>


2

Python 2, 166 byte

Ragazzo, questo sembra più lungo di quanto dovrebbe essere.

S=str.split
def d(t):e="^"in t and int(S(t,"^")[1])-1;return`int(S(t,"x")[0])*(e+1)`+"x"[:e]+"^%d"%e*(e>1)
print" + ".join(d(t)for t in S(raw_input()," + ")if"x"in t)

La funzione dha un termine non costante te restituisce il suo derivato. Il motivo per cui ho defla funzione invece di usare un lambda è quindi posso assegnare l'esponente meno 1 a e, che poi viene usato altre quattro volte. La cosa più fastidiosa principale è dover eseguire il cast avanti e indietro tra stringhe e ints, anche se l'operatore backtick di Python 2 aiuta a farlo.

Dividiamo quindi l'input in termini e chiamiamo dciascuno che ha "x"in esso, eliminando così il termine costante. I risultati vengono riuniti e stampati.


2

CJam, 62 57 55 49 byte

Bene, Dennis lo ha fatto vergognare prima ancora di notare che il sito era di backup. Ma ecco comunque la mia creazione:

lS/{'x/:T,({T~1>:T{~T~*'xT~(:T({'^T}&}&" + "}&}/;

L'ultima versione salva alcuni byte con le scorciatoie suggerite da @Dennis (usa le variabili e dividi nello spazio anziché +).

Provalo online


1
Il salvataggio in una variabile è più breve della comparsa nel blocco else. Ad esempio, _({'^a\}{;}?è 1 byte più lungo di :T({T'^a\}&.
Dennis,

1
Se si divide in spazi anziché segni più, non è necessario il ~blocco rimanente in altro e si può eliminare anche quello.
Dennis,

@Dennis Funziona, grazie. Inizialmente volevo eliminare i segni più, ma si ritirano comunque quando provo la presenza di x. Ho trovato alcuni miglioramenti mentre ero lì. Principalmente, poiché ora ho i valori nelle variabili, posso ricordarli dove ne ho davvero bisogno, risparmiando un po 'di manipolazione dello stack. Ho anche avuto un randagio ache avrebbe dovuto essere eliminato quando ho ottimizzato la generazione dell'output in precedenza.
Reto Koradi,

1

Pyth, 62 byte

jJ" + "m::j"x^",*Fdted"x.1$"\x"x.0"kftTmvMcd"x^"c:z"x ""x^1 "J

Soluzione piuttosto brutta, usando alcune sostituzioni regex.


1

Python 3, 176 byte

s=input().split(' + ')
y='x'in s[0]
L=map(lambda x:map(int,x.split('x^')),s[2-y:])
print(' + '.join([s[1-y][:-1]]+['x^'.join(map(str,[a*b,b-1])).rstrip('^1')for a,b in L]))

In effetti, il fastidio principale è la conversione tra stringhe e ints. Inoltre, se fosse richiesto un termine costante, il codice sarebbe solo 153 byte.


La prima risposta, è stata sparare per aver battuto DLosc, non ci siamo ancora arrivati.
El'endia Starman,

0

Python 2, 229 byte

import os
print' + '.join([i,i[:-2]][i[-2:]=='^1'].replace('x^0','')for i in[`a*b`+'x^'+`b-1`for a,b in[map(int,a.split('x^'))for a in[[[i+'x^0',i+'^1'][i[-1]=='x'],i]['^'in i]for i in os.read(0,9**9).split(' + ')]]]if i[0]!='0')

0

Python 2, 174 byte

print' + '.join(['%d%s%s'%(b[0]*b[1],'x'*(b[1]>1),'^%d'%(b[1]-1)*(b[1]>2))for b in[map(int,a.split('x^')if 'x^'in a else[a[:-1],1])for a in input().split(' + ')if 'x'in a]])

Sfortunatamente, il trucco di DLosc per rinominare il metodo split ed eseguire la differenziazione in una funzione specifica non accorcia il mio codice ...

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.