Différenciation symbolique des polynômes


20

Différenciation symbolique 1: Gone Coefishin '

Tâche

Écrivez un programme qui prend un polynôme en x de stdin (1 <deg (p) <128) et le différencie. Le polynôme d'entrée sera une chaîne de la forme suivante:

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

où le coefficient de chaque terme est un entier (-128 <a <128). Chaque terme est séparé par un espace, un + et un autre espace; les termes linéaires et constants apparaissent comme ci-dessus (c.-à-d. non x^0ou x^1). Les termes apparaîtront par ordre croissant de degré et les puissances à coefficient nul seront omises. Tous les termes avec le coefficient 1 ou -1 affichent ce coefficient explicitement.

Votre sortie doit avoir exactement la même forme. Notez que les coefficients dans la sortie peuvent atteindre 127 * 127 == 16129.

Exemples

"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"

Notation

Votre score est la longueur de votre programme en octets, multipliée par trois si vous utilisez une bibliothèque intégrée ou une bibliothèque qui fait de l'algèbre symbolique.


Je ne peux pas croire que nous n'avons pas déjà eu ce défi ici!
flawr

5
@flawr Nous l'avons fait. (Bien que celle-ci nécessitait également d'autres fonctions et n'avait pas de règles aussi strictes sur le format de sortie.)
Martin Ender

@flawr Je pensais la même chose ... et pourtant je n'ai pas trouvé la recherche liée de Martin. Et bien.
hYPotenuser

Réponses:


15

Rétine , 53 43 42 41 40 35 octets

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

À des fins de comptage, chaque ligne va dans un fichier séparé, mais vous pouvez exécuter ce qui précède comme un fichier unique en appelant Retina avec l' -sindicateur.

Cela attend que les nombres dans la chaîne d'entrée soient donnés en unaire et produiront une sortie dans le même format. Par exemple

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

au lieu de

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

Explication

Le code décrit une seule substitution d'expression régulière, qui est essentiellement 4 substitutions compressées en une seule. Notez qu'une seule des branches remplira le groupe, $2donc si l'une des trois autres correspondances, la correspondance sera simplement supprimée de la chaîne. Nous pouvons donc examiner les quatre cas différents séparément:

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

S'il est possible d'atteindre un espace depuis le début de la chaîne sans en rencontrer un, xcela signifie que le premier terme est le terme constant et nous le supprimons. En raison de la gourmandise de +, cela correspondra également au plus et au deuxième espace après le terme constant. S'il n'y a pas de terme constant, cette partie ne correspondra tout simplement jamais.

x(?= )
<empty>

Ceci correspond à un xqui est suivi d'un espace, c'est-à-dire le xdu terme linéaire (s'il existe), et le supprime. Nous pouvons être sûrs qu'il y a un espace après, car le degré du polynôme est toujours d'au moins 2.

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

Ceci effectue la multiplication du coefficient par l'exposant. Cela correspond à un seul 1dans le coefficient et le remplace par l'exposant correspondant entier via la tête de lecture.

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

Cela réduit tous les exposants restants en faisant correspondre le dernier 1(assuré par l'anticipation). S'il est possible de faire correspondre^11 (et une limite de mot), nous le supprimons à la place, ce qui prend soin d'afficher correctement le terme linéaire.

Pour la compression, on remarque que la plupart des conditions ne s'influencent pas. (\^1)?ne correspondra pas si l'anticipation dans le troisième cas est vraie, nous pouvons donc mettre ces deux ensemble comme

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

Maintenant, nous avons déjà l'anticipation nécessaire pour le deuxième cas et les autres ne peuvent jamais être vrais lors de la correspondance x, nous pouvons donc simplement généraliser le 1à a \w:

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

Le premier cas n'a vraiment rien de commun avec les autres, nous le séparons donc.


9

CJam, 43 41 octets

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

Merci à @ jimmy23013 d'avoir signalé un bug et d'avoir joué deux octets au golf!

Essayez-le en ligne dans l' interpréteur CJam .

Comment ça fonctionne

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 octets

Code 62b + 1 ligne de commande (-p)

Pas étonnant pour le moment, mais je vais continuer d'essayer de le raccourcir.

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

Exemple d'utilisation:

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

Merci Denis pour -1b


5

Julia, 220 octets

Pas d'expressions régulières!

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," + "))

Cela crée une fonction lambda qui accepte une chaîne et renvoie une chaîne. Les entrailles imitent ce qui se passe lorsque le code Julia est évalué: une chaîne est analysée en symboles, expressions et appels. Je pourrais essayer d'écrire une fonction de différenciation symbolique complète de Julia et de la proposer comme faisant partie de Julia.

Non golfé + explication:

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 octets

#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;}

Analyser fondamentalement chaque terme et imprimer le terme différencié en séquence. Assez simple.


2

JavaScript ES6, 108 octets

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

Extrait 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 octets

Garçon, cela semble plus long qu'il ne devrait l'être.

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 fonction dprend un terme non constant tet renvoie sa dérivée. La raison pour laquelle je defla fonction au lieu d'utiliser un lambda est pour que je puisse affecter l'exposant moins 1 à e, qui est ensuite utilisé quatre fois. La principale chose ennuyeuse est de devoir faire des allers-retours entre les chaînes et les entiers, bien que l'opérateur de backtick de Python 2 y contribue.

Nous divisons ensuite l'entrée en termes et faisons appel dà chacun qui en contient "x", éliminant ainsi le terme constant. Les résultats sont réunis et imprimés.


2

CJam, 62 57 55 49 octets

Eh bien, Dennis a fait honte avant même que je remarque que le site était de retour. Mais voici ma création quand même:

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

La dernière version enregistre quelques octets avec les raccourcis suggérés par @Dennis (utilisez des variables et divisez-les dans l'espace au lieu de +).

Essayez-le en ligne


1
L'enregistrement dans une variable est plus court que l'insertion dans le bloc else. Par exemple, _({'^a\}{;}?1 octet de plus que :T({T'^a\}&.
Dennis

1
Si vous vous séparez dans des espaces au lieu de signes plus, vous n'avez pas besoin du ~dans le bloc else restant et pouvez également éliminer celui-ci.
Dennis

@Dennis Cela fonctionne, merci. À l'origine, je voulais éliminer les signes plus, mais ils disparaissent quand même lorsque je teste la présence de x. J'ai trouvé quelques améliorations supplémentaires pendant que j'y étais. Surtout, parce que j'ai maintenant les valeurs dans les variables, je peux les rappeler là où j'en ai vraiment besoin, en économisant une certaine manipulation de la pile. J'ai également eu une erreur aqui aurait dû être supprimée lorsque j'ai optimisé la génération de sortie plus tôt.
Reto Koradi

1

Pyth, 62 octets

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

Solution assez moche, utilisant certaines substitutions d'expression régulière.


1

Python 3, 176 octets

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]))

En effet, le principal inconvénient est de devoir convertir entre les chaînes et les entiers. De plus, si un terme constant était requis, le code ne serait que de 153 octets.


La première réponse, c'était de tirer pour avoir battu DLosc, je n'y suis pas tout à fait arrivé.
El'endia Starman

0

Python 2, 229 octets

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 octets

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]])

Malheureusement, l'astuce de DLosc pour renommer la méthode de partage et effectuer la différenciation dans une fonction spécifique ne raccourcit pas mon code ...

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.