Help me with differential calculus!

52

3

I love programming and know every language, but I suck at math. Unfortunately, my school requires that computers students must take a year of calculus. There's a test next week, and I don't know any of the formulas for derivatives!

Please help me find the formulas. I need a cheat sheet - a program (as short as possible so my teacher won't notice it) that takes an expression (like 4*x^3-2) as input and outputs the derivative. (I don't care if the input and output uses command line arguments, STDIN, STDOUT, or whatever, since I'm doing all of the calculation in my head anyway.)

The test covers the following types of functions:

  • Constants, like -3 or 8.5
  • Power functions, like x^0.5 or x^-7
  • Exponential functions, like 0.5^x or 7^x (the base is always positive)
  • A constant multiplied by a function, like 3*x^5 or -0.1*0.3^x
  • The sum and difference of multiple functions, like -5*x^2+10-3^x

My teacher always formats his questions in the exact same way, as shown above. He also doesn't use any fractions, numbers like pi or e, or really big numbers (bigger than 1,000). He never uses parentheses, and always shows multiplication using an asterisk (*). The only variable used is always x.

On the other hand, my teacher is pretty lenient about answers. They don't need to be simplified at all or formatted exactly as shown above, as long as it's clear what the answer is saying.

While I can use any language, remember that I can't figure out derivatives by myself. So if the program uses built-in functions to deal with equations or calculate derivatives, I won't be able to use it.

During the test, I won't have access to the Internet or any files other than the program on the cheat sheet.

Note: This scenario is entirely fictional. In real life, cheating and helping others cheat is wrong and should never be done.

Ypnypn

Posted 2014-06-17T13:32:40.747

Reputation: 10 485

3Can we expect that x is always the variable to differentiate? – Kyle Kanos – 2014-06-17T13:39:13.027

2Must the answer be simplified? Do we need to add like terms? – Rainbolt – 2014-06-17T13:40:46.110

Will any numbers be in scientific notation? – Martin Ender – 2014-06-17T14:02:21.097

Will there be any product rule? – scrblnrd3 – 2014-06-17T14:28:58.247

1

I guess it's time for my calculus project at http://scrblnrd3.github.io/Javascript-CAS/ to shine if I can actually golf it

– scrblnrd3 – 2014-06-17T14:33:02.647

1Should we assume there are no parens? – Not that Charles – 2014-06-17T14:59:57.700

2

I've answered most of these questions in my edit. There is no scientific notation or product rule.

– Ypnypn – 2014-06-17T17:08:10.320

1Oh. So no chain rule. – Cruncher – 2014-06-18T18:13:12.317

I'm sure a math language such as Maple would make short work of this problem. – MBraedley – 2014-06-18T19:20:46.347

@MBraedley However: http://meta.codegolf.stackexchange.com/a/1078/9498

– Justin – 2014-06-19T06:39:34.373

Can x^x appear? – celtschk – 2014-06-19T12:56:28.257

Also, is -2^x to be interpreted as -(2^x) or as (-2)^x? – celtschk – 2014-06-19T12:58:00.087

@celtschk x^x will not appear (since it requires using techniques other than the ones listed). -2^x means -(2^x) (since, as mentioned, the base will never be negative). – Ypnypn – 2014-06-19T13:19:46.577

1

--Views hot network questions: Help me with differential calculus! "I know differential calculus, maybe I can help this guy." --Reads question --Reviews answers "WTH, these answers are all terrible." On a serious note, I think it's an indication of a good question setup that two separate posters were convinced your situation was real.

– primo – 2014-06-19T17:46:12.707

Answers

8

Wolfram 136 134 109[Thanks to Calle for his comment below]

Limited support for product and chain rules.

n=n_?NumberQ;d[v_Plus]:=d/@v;d[v_]:=v/.{x_^n:>x^(n-1)d[x]n,n^x_:>Log[n]d[x]n^x,x_*y__:>d[x]y+d[y]x,n:>0,x:>1}

Example:

d[3^(x^2)*(x^3+2*x)^2]
>> 2*3^x^2*(2+3*x^2)*(2*x+x^3) + 2*3^x^2*x*(2*x+x^3)^2*Log[3]

Note that this does not use any "built-in functions to deal with equations or calculate derivatives": only pattern-matching is involved*.

[*Well... technically the interpreter also parses and builds a sort of AST from the input too]


Ungolfed:

d[expr_Plus] := d /@ expr;
d[expr_] := expr /. {
   Power[x_, n_?NumberQ] :> n Power[x, n - 1] d[x],
   Power[n_?NumberQ, x_] :> Log[n] Power[n, x] d[x],
   Times[x_, y__] :> d[x] y + d[y] x,
   n_?NumberQ :> 0,
   x :> 1
}

Saran

Posted 2014-06-17T13:32:40.747

Reputation: 226

This is another version. You don't have to write Power, Times etc. IDK how much that will improve your golfed version though, but you have at least one Times in there so you can def. save some characters. Also note that in your ungolfed version it says d[expr_]:= v/.... – None – 2014-06-19T11:07:14.233

1@Calle "IDK how much that will improve your golfed version" -- 25 bytes! Cheers! – Saran – 2014-06-19T11:22:48.017

26

Perl - 121 122

(+2 for -p)

s/(?<![-\d.*^])-?[\d.]+(?![*^\d.])/0/g;s/(?<!\^)x(?!\^)/1/g;s/x\^(-?[\d.]+)/"$1*x^".($1-1)/ge;s/([\d.]+)\^x/ln($1)*$&/g

Test:

$ perl -p diff.pl << EOF
> -3
> 8.5
> x^0.5
> x^-7
> 0.5^x
> 7^x
> 3*x^5
> -0.1*0.3^x
> -5*x^2+10-3^x
> EOF
0
0
0.5*x^-0.5
-7*x^-8
ln(0.5)*0.5^x
ln(7)*7^x
3*5*x^4
-0.1*ln(0.3)*0.3^x
-5*2*x^1+0-ln(3)*3^x

mniip

Posted 2014-06-17T13:32:40.747

Reputation: 9 396

Yet another reason for me to learn regex... – Kyle Kanos – 2014-06-17T15:27:00.983

3@KyleKanos Don't. Regex is bad, regex is terrific. – mniip – 2014-06-17T15:35:39.287

Meh, beat me to it. Not bad! (PS: regex is beautiful) – Martin Ender – 2014-06-17T15:36:59.283

8I have no idea what's going on here. +1 – qwr – 2014-06-18T06:02:08.243

4Explanation: Constant --> 0, x --> 1, x^n --> nx^(n-1), a^x --> ln(a)a^x – n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ – 2014-06-18T09:56:44.747

I'm pretty sure that -p can count as one character since it's a flag and flags can be combined like -alFe – None – 2014-06-18T13:57:44.230

@professorfish i'm pretty sure the first flag costs 2. There's a meta post somewhere – Cruncher – 2014-06-18T18:14:34.430

@Cruncher The most 'definitive' post seems to be this one by J B, which doesn't actually specify the cost of the first flag (just that each additional flag costs one). I typically count it as one, although traditional Perl Golf rules would count it as three (one for each byte in -p, plus the necessary whitespace). One thing I disagree with in that post, is that -M5.01 should be free (in Perl, enables 'special features' that were added in v5.010).

– primo – 2014-06-19T05:38:48.107

7

Haskell 38 Chars

The function d takes a function and returns a function. It is inputted in the form of a power series, and is outputted the same way (which is a type of whatever.)

d=zipWith(*)[1..].tail

For example, if we input x->x^2, we get x->2*x.

λ <Prelude>: d [0,0,1]
[0,2]

And for the exponential function.

λ <Prelude>: take 10 exp --exp redefined above to be in power series notation
[1.0,1.0,0.5,0.16666666666666666,4.1666666666666664e-2,8.333333333333333e-3,1.388888888888889e-3,1.984126984126984e-4,2.48015873015873e-5,2.7557319223985893e-6]
λ <Prelude>: let d=zipWith(*)[1..].tail in take 10 $ d exp
[1.0,1.0,0.5,0.16666666666666666,4.1666666666666664e-2,8.333333333333333e-3,1.388888888888889e-3,1.984126984126984e-4,2.48015873015873e-5,2.7557319223985893e-6]

PyRulez

Posted 2014-06-17T13:32:40.747

Reputation: 6 547

@PyRulez Lol! I get that infinite series joke! – Drew Christensen – 2016-07-02T23:06:30.027

5But the OP doesn't know any maths! Can we expect him to express his exponential input as a power series? – Saran – 2014-06-17T15:52:51.750

Well he obviously knows notation. He just doesn't know how to do the derivative operation. – PyRulez – 2014-06-17T15:56:59.383

5Can this handle 2^x? – Kyle Kanos – 2014-06-17T16:09:07.627

@KyleKanos Yep, just put x*ln 2 into e^x. It will still be a power series. – PyRulez – 2014-06-18T01:17:04.933

5What witchcraft is this? – Christofer Ohlsson – 2014-06-18T06:51:41.570

@ChristoferOlsson: http://en.wikipedia.org/wiki/Taylor_series

– n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ – 2014-06-18T09:58:47.000

7I don't see where it "takes an expression (like 4*x^3-2) as input", as required by the OP. – Gabe – 2014-06-18T12:53:37.897

@Gabe You put the expression into d. It does take string arguments though. – PyRulez – 2014-06-18T21:52:08.197

It looks like d takes a list of numbers and returns a list of numbers. I don't see how you'd put an expression like 4*x^3-2 into d. – Gabe – 2014-06-19T01:11:04.597

@Gabe You input 4*x^3-2 in the format of [-2,0,0,4]. Also, I meant it doesn't take string arguments, the above is a typo. – PyRulez – 2014-06-19T01:30:18.130

I see. Do you expect the OP to convert from a Taylor polynomial to an algebraic expression, or is he supposed to answer all his test questions as polynomials? – Gabe – 2014-06-19T03:38:52.783

@Gabe As polynomials. He just writes the first term in 1 second and 1 cm long, the second in half a second and half a cm long, the third in a forth second and a forth cm long, and so on. He will finish in 2 seconds and it will fit in any blank 2 cm long or less time and length. – PyRulez – 2014-06-21T23:44:13.237

5

Prolog 176

d(N,0):-number(N).
d(x,1).
d(-L,-E):-d(L,E).
d(L+R,E+F):-d(L,E),d(R,F).
d(L-R,E-F):-d(L,E),d(R,F).
d(L*R,E*R+L*F):-d(L,E),d(R,F).
d(L^R,E*R*L^(R-1)+ln(L)*F*L^R):-d(L,E),d(R,F).

Supported operators: binary +, binary -, binary *, binary ^, unary -. Note that unary + is not supported.

Sample run:

49 ?- d(-3,O).
O = 0.

50 ?- d(8.5,O).
O = 0.

51 ?- d(x^0.5,O).
O = 1*0.5*x^ (0.5-1)+ln(x)*0*x^0.5.

52 ?- d(x^-7,O).
ERROR: Syntax error: Operator expected
ERROR: d(x
ERROR: ** here **
ERROR: ^-7,O) . 
52 ?- d(x^ -7,O).
O = 1* -7*x^ (-7-1)+ln(x)*0*x^ -7.

53 ?- d(x,O).
O = 1.

54 ?- d(0.5^x,O).
O = 0*x*0.5^ (x-1)+ln(0.5)*1*0.5^x.

55 ?- d(7^x,O).
O = 0*x*7^ (x-1)+ln(7)*1*7^x.

56 ?- d(3*x^5,O).
O = 0*x^5+3* (1*5*x^ (5-1)+ln(x)*0*x^5).

57 ?- d(-0.1*0.3^x,O).
O = 0*0.3^x+ -0.1* (0*x*0.3^ (x-1)+ln(0.3)*1*0.3^x).

58 ?- d(-5*x^2+10-3^x,O).
O = 0*x^2+ -5* (1*2*x^ (2-1)+ln(x)*0*x^2)+0- (0*x*3^ (x-1)+ln(3)*1*3^x).

Prolog is confused when it runs into ^- sequence. A space must be inserted between ^ and - for it to parse the expression correctly.

Hope your teacher doesn't mind the mess of equation.

Crazy time:

59 ?- d(x^x,O).
O = 1*x*x^ (x-1)+ln(x)*1*x^x.

60 ?- d((x^2-x+1)*4^ -x,O).
O = (1*2*x^ (2-1)+ln(x)*0*x^2-1+0)*4^ -x+ (x^2-x+1)* (0* -x*4^ (-x-1)+ln(4)* - 1*4^ -x).

n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

Posted 2014-06-17T13:32:40.747

Reputation: 5 683

4

C, 260

Hey, I think I know your teacher! Isn't it that one who has the supernatural ability to detect students executing library pattern-matching functions in their head?

So, using sscanf is out of question... But don't worry:

#define P s--||printf(
q=94,s,c,t,a;main(){char i[999],*p=i,*e=p;gets(i);for(;c=*p++,t=q^94|c^45?c%26==16?c%16/3:c/46:1,s=(a="30PCqspP#!C@ #cS` #!cpp#q"[s*5+t])/16-3,a&1&&(p[-1]=0),t||(P"*0"),P"/x"),P"/x*%s",e),P"*ln(%s)",e),s=0),a&2&&(e=p),c;putchar(q=c));}

Running examples (input on stdin; output goes to stdout):

4*x^3-2

4*x^3/x*3-2*0

This format is much better than just 12*x^2, because this way your teacher can be sure that you calculated the answer yourself and didn't cheat by copying it from someone else!

x+2^x

x/x+2^x*ln(2)

The output has a slight domain problem at x=0, but it's correct almost everywhere!

For reference, here is an ungolfed, readable (by mere mortals) version. It uses a state machine with 5 states and 5 categories of input characters.

void deriv(char* input)
{
    char* p = input; // current position
    char* exp = p; // base or exponent
    char q = '^'; // previous character

    // State machine has 5 states; here are examples of input:
    // state 0: 123
    // state 1: 123*
    // state 2: 123*x
    // state 3: 123*x^456
    // state 4: 123^x
    int state = 0;

    // Control bits for state machine:
    // bit 0: special action: stop recording base or exponent
    // bit 1: special action: start recording base or exponent
    // bits 4-7: if first column, specify how to calculate the derivative:
    //              3 - multiply the constant term by 0
    //              4 - divide x by x
    //              5 - divide x^n by x and multiply by n
    //              6 - multiply n^x by ln(n)
    // bits 4-7: if not first column, specify the next state
    //              (plus 3, to make the character printable)
    const char* control =
        "\x33\x30\x50\x43\x71"
        "\x73\x70\x50\x23\x21"
        "\x43\x40\x20\x23\x63"
        "\x53\x60\x20\x23\x21"
        "\x63\x70\x70\x23\x71";

    for (;;) {
        int c = *p++;

        // Convert a char to a category:
        // category 0: // - +
        // category 3: // *
        // category 2: // x
        // category 4: // ^
        // category 1: // numbers: 0...9 and decimal point
        int category;
        int action;    

        if (q == '^' && c == '-')
            category = 1; // unary minus is a part of a number
        else
            category = c%26==16?c%16/3:c/46; // just does it

        // Load new state and action to do
        action = control[state * 5 + category];

        if (action & 1)
            p[-1] = 0;
        state = (action >> 4) - 3;
        if (category == 0)
        {
            if (state == 0)
                printf("*0");
            if (state == 1)
                printf("/x");
            if (state == 2)
                printf("/x*%s", exp);
            if (state == 3)
                printf("*ln(%s)", exp);
            state = 0;
        }
        if (action & 2)
            exp = p;

        if (c == 0 || c == '\n') // either of these can mark end of input
            break;

        putchar(c);
        q = c;
    }
}

P.S. Watch out for that gets function: it has a security vulnerability that can let your teacher execute a rootkit in your mind by providing too long input...

anatolyg

Posted 2014-06-17T13:32:40.747

Reputation: 10 719

3

Lua 296 268 263

function d(a)l=""i=a:find"x" if i then if a:sub(i-1,i-1)=="^"then l="log("..a:sub(1,i-2)..")*"..a elseif a:sub(i+1,i+1)=="^"then l=a:sub(i+2).."*"..a:sub(1,i)p=a:sub(i+2)-1 if p~=1 then l= l..a:sub(i+1,i+1)..p end else l=a:sub(1,i-2)end else l="0"end return l end

Not very golfed and cannot currently handle multiple terms (you can just run it a few times, right?), but it can handle n^x, x^n and n as input.


Ungolfed...

function d(a)
   l=""
   i=a:find"x"
   if i then
      if a:sub(i-1,i-1)=="^" then
         l="log("..a:sub(1,i-2)..")*"..a
      elseif a:sub(i+1,i+1)=="^" then
         l=a:sub(i+2).."*"..a:sub(1,i)
         p=a:sub(i+2)-1 -- this actually does math here
         if p~=1 then
            l= l..a:sub(i+1,i+1)..p
         end
      else
         l=a:sub(1,i-2)
      end
   else
      l="0"
   end
   return l
end

Kyle Kanos

Posted 2014-06-17T13:32:40.747

Reputation: 4 270

str.func(str,...) == str:func(...), that's why strings got the metatable after all... – mniip – 2014-06-17T14:36:38.157

@mniip: Still learning Lua. Thanks for the tip. – Kyle Kanos – 2014-06-17T14:38:27.490

1Since the OP is only looking for code "he can calculate in his head", I wouldn't bother with defining a function and declaring l local. Just expect the input to be stored in a and say the output will be stored in l. – Martin Ender – 2014-06-17T14:44:56.790

You can omit parentheses in a:find("x"), also note that 1then only works in Lua 5.2 – mniip – 2014-06-17T14:47:38.860

@mniip: Whoa, that's pretty cool that () is optional. The 1then was just fixed as I don't have 5.2 (not doing any CPU updates until after dissertation is done b/c I don't want to mess anything up). – Kyle Kanos – 2014-06-17T14:49:23.310

3

ECMAScript 6, 127 bytes

Here is my regex attempt (using a single regex and some logic in the replacement callback):

i.replace(/(^|[*+-])(\d+|(?:([\d.]+)\^)?(x)(?:\^(-?[\d.]+))?)(?![.*^])/g,(m,s,a,b,x,e)=>s+(b?'ln'+b+'*'+a:e?e--+'*x^'+e:x?1:0))

This expects the input string to be stored in i and simply returns the result. Try it out in an ECMAScript 6 compliant console (like Firefox's).

Martin Ender

Posted 2014-06-17T13:32:40.747

Reputation: 184 808

2

sed, 110

Taking very literally "They don't need to be simplified at all or formatted exactly as shown above, as long as it's clear what the answer is saying":

s/.*/__&_/;s/x\^(-?[0-9.]+)/\1*x^(\1-1)/g;s/([0-9.]+)\^/ln\1*\1^/g;s/([^(][-+_])[0-9.]+([-+_])/\10\2/g;s/_//g

The byte count includes 1 for the r flag.

Ungolfed, with comments:

# Add underscores before and after the string, to help with solo-constant recognition
s/.*/__&_/
# Power rule: replace x^c with c*x^(c-1) where c is a number
s/x\^(-?[0-9.]+)/\1*x^(\1-1)/g
# Exponentials: replace c^ with lnc*c^ where c is a number
# (This assumes that there will be an x after the ^)
s/([0-9.]+)\^/ln\1*\1^/g
# Constants: replace ?c? with ?0? where c is a number and ? is +, -, or _
# Except if it's prededed by a parenthesis then don't, because this matches c*x^(c-1)!
s/([^(][-+_])[0-9.]+([-+_])/\10\2/g
# Get rid of the underscores
s/_//g

Sample run:

$ cat derivatives.txt
-3
8.5
x^0.5
x^-7
0.5^x
7^x
3*x^5
-0.1*0.3^x
-5*x^2+10-3^x

$ sed -re 's/.*/__&_/;s/x\^(-?[0-9.]+)/\1*x^(\1-1)/g;s/([0-9.]+)\^/ln\1*\1^/g;s/([^(][-+_])[0-9.]+([-+_])/\10\2/g;s/_//g' derivatives.txt
-0
0
0.5*x^(0.5-1)
-7*x^(-7-1)
ln0.5*0.5^x
ln7*7^x
3*5*x^(5-1)
-0.1*ln0.3*0.3^x
-5*2*x^(2-1)+0-ln3*3^x

I bet this could be golfed further; it's my first try at sed. Fun!

DLosc

Posted 2014-06-17T13:32:40.747

Reputation: 21 213

1

Ruby, 152

...or 150 if you don't need to print... or 147 if you also are ok with an array that you need to join yourself.

run with ruby -nal

p gsub(/(?<!\^)([-+])/,'#\1').split(?#).map{|s|s[/x\^/]?$`+$'+"x^(#{$'}-1)":s[/-?(.*)\^(.*)x/]?s+"*ln(#{$1}*#{$2[0]?$2:1})":s[/\*?x/]?($`[0]?$`:1):p}*''

ungolfed:

p gsub(/(?<!\^)([-+])/,'#\1').split(?#). # insert a # between each additive piece, and then split.
map{ |s|                                 
    if s[/x\^/]                          # if it's c*x^a
        $` + $' + "x^(#{$'}-1)"          #      return c*ax^(a-1)
    elsif s[/-?(.*)\^(.*)x/]             # if it's c*b^(a*x)
        ln = $1 + ?* + ($2[0] ? $2 : 1)  #      return c*b^(a*x)*ln(b*a)
        s+"*ln(#{ln})"
    elsif s[/\*?x/]                      # if it's c*x
        ($`[0] ? $` : 1)                 #      return c
    else                                 # else (constant)
        nil                              #      return nil
    end
}*''

My main problem with this one is the number of characters proper splitting takes. The only other way I could think of was split(/(?<!\^)([-+])/) which gives + and - as their own results. Any hints for a better solution?

Also, is there a shorter way to return s if it's not empty, but otherwise return y? I've used s[0]?y:s? In JS I'd just do s||y, but "" is truthy in Ruby.

Not that Charles

Posted 2014-06-17T13:32:40.747

Reputation: 1 905

Would a lookahead assertion help, like so: split(/(?<!\^)(?=[-+])/)? – DLosc – 2014-06-19T00:43:23.010