Derivative of a product

In calculus, the derivative of a mathematical function defines the rate at which it changes. The derivative of a function f(x) can be marked as f'(x), and these can sometimes be abbreviated to f and f'.

The derivative of the product of two functions fg is f'g + g'f. The derivative of three fgh is f'gh + fg'h + fgh'. In general, the derivative of a product of any number of functions is the sum of the product of all but one, multiplied by the derivative of the remaining one, for each individual function.

Your challenge is to take a string of distinct alphabetical characters and transform it into its derivative. No spacing or simplification is required, and terms separated by + may be in any order. The string will contain at least two characters.

This is , so the shortest code wins!

Testcases

abc -> a'bc+ab'c+abc'
de -> d'e+de'
longabcdef -> l'ongabcdef+lo'ngabcdef+lon'gabcdef+long'abcdef+longa'bcdef+longab'cdef+longabc'def+longabcd'ef+longabcde'f+longabcdef'
short -> s'hort+sh'ort+sho'rt+shor't+short'
• Can we assume all letters are distinct?
– att
Commented May 7, 2022 at 1:33
• @att Sure, tht's fine. Commented May 7, 2022 at 2:09
• do the functions in each term need to remain in the original order? In other words, is aabc'+bca'+cab' an acceptable output for abc? Commented May 7, 2022 at 9:10
• @DominicvanEssen That's a tricky one... I'm gonna say no simply because it's a major part of the challenge, and pretty much all answers would have to change to remain competitive under that. Commented May 7, 2022 at 11:18
• If a leading + were acceptable, it could be done in a one-pass regex substitution. Commented Mar 26, 2023 at 6:31

Pip, 15 bytes

aRL#aJ'+<>#aJ''

Attempt This Online!

Explanation

aRL#aJ'+<>#aJ''
a                Command-line arg       "abc"
#a            Length of arg          3
RL              Repeat-list            ["abc"; "abc"; "abc"]
J'+         Join on "+"            "abc+abc+abc"
<>#a     Groups of size len(a)  ["abc"; "+ab"; "c+a"; "bc"]
J''  Join on "'"            "abc'+ab'c+a'bc"

Python, 54 bytes

lambda s:((l:=len(s))*(l*"'"+-~l*(s+"+")))[-~l*~l::-l]

Attempt This Online!

Python, 56 bytes

lambda s:((l:=-~len(s))*(l*(s+"+")+-~l*"'"))[l:-3*l:-~l]

Attempt This Online!

Old Python, 58 bytes

lambda s:((l:=-~len(s))*(l*(s+"+")+l*"'"+"+"))[l:-3*l:-~l]

Attempt This Online!

a b c d +
(a)b c d +
a(b)c d +
a b(c)d +
a b c(d)+
' ' ' '(')'
a b c d(+)
a b c d +
(a)b c d +
a(b)c d +
a b(c)d +
' ' '(')' '
a b c(d)+
a b c d(+)
a b c d +
(a)b c d +
a(b)c d +
' '(')' ' '
a b(c)d +

etc.

C (clang), 6964 59 bytes

-10 thanks to @Noodle9 and @ceilingcat

l;f(char*a){for(l=0;a[l];)printf("+%.*s'%s"+!l,++l,a,a+l);}

Try it online!

Brainfuck, 87 84 bytes

>,>---[>+<+++++++]>++[>,]<[<]<[<]>[[.>]>.>[.[-<<+>>]>[.>]<[<]<++++.----[->+<]<[<]]>]

Saved 3 bytes thanks to Nitrodon.

Try it online!

Explanation:

memory progress for abc:
null a null 'bc
null ab null 'c
null abc' null

string is split to two parts by null character

---[>+<+++++++]>++ load ' to memory; taken form esolangs wiki
<[<]<[<]>          go to beginning of memory
[
[.>]              print first part
>.>               print '
[                 check if there is text after '
.                print first char of second part
[-<<+>>]         move char to the first part
>[.>]            print rest of second part
<[<]<            go to position of '
++++.----        use ' to print plus
[->+<]           move '
<[<]             go before beginning
]
>                 move back to beginning
if there wasn't text after ' move after end instead
]
• I think you can save 2 bytes by moving ' after printing the rest of the second part. Commented May 9, 2022 at 20:24
• @Nitrodon Thank you, it saved even 3 bytes!
– Jiří
Commented May 10, 2022 at 15:28

Python, 47 bytes

lambda s:"+".join(s.replace(c,c+"'")for c in s)

Attempt This Online!

How it works:

a  b  c  d --> a' b  c  d
a  b  c  d --> a  b' c  d
a  b  c  d --> a  b  c' d
a  b  c  d --> a  b  c  d'

then join with '+'

Factor, 53 bytes

[ dup length [1,b] [ cut "'"glue ] with map "+"join ]

Try it online!

If the order of each product doesn't matter:

Factor, 43 bytes

[ all-rotations [ 39 suffix ] map "+"join ]

Try it online!

Explanation

! "short"
all-rotations     ! { "short" "horts" "ortsh" "rtsho" "tshor" }
[ 39 suffix ] map ! { "short'" "horts'" "ortsh'" "rtsho'" "tshor'" }
"+"join           ! "short'+horts'+ortsh'+rtsho'+tshor'"

JavaScript (Node.js), 46 44 bytes

l=>[...l].map(s=>l.replace(s,s+"'")).join+

Try it online!

-2 bytes thanks to emanresu A

• This answer only works with 'distinct alphabetical characters'. Commented May 9, 2022 at 10:37
• Welcome to Code Golf, and nice first answer! Be sure to check out our Tips for golfing in Javascript page for ways you can golf your program! In particular, you're generally allowed to take input as a char array instead of a string, and .join('+') can become .join+ Commented May 9, 2022 at 10:39
• @emanresuA The problem with "[taking] input as a char array instead of a string" is that .replace would be messed up. Is there another thing you're suggesting that will allow there to be less bytes with regards to the char array? Commented May 9, 2022 at 19:22
• @Samathingamajig Oh, never mind :P Commented May 9, 2022 at 19:43

Jelly, 14 bytes

J‘œṖ€j€”'j”+”'

Try It Online!

J‘œṖ€j€”'j”+”'  Full Program
J               [1, 2, ..., len]
‘              [2, 3, ..., len + 1]
œṖ€           For each of these, partition (*)
j€”'       Join each partition on ' (**)
j”+    Join on +
”'  An unparseable nilad forces the current string to be printed

(And then the ' is printed at the end)

(*)  partition takes a list on the left but since we give it a single number,
it auto-wraps it so we just get all needed splits
(**) the last partition only has one sublist so join doesn't work properly here

Retina, 15 13 bytes

L$|+.$>'$' Try it online! -2 thanks to Neil. Match every character. For each one, take the text before the separator after the matched string, followed by an apostrophe, followed by the text after the matched string. Print as a list with separator +. • L$|+w. / $>'$' is 14 bytes. (But I didn't know you didn't need the ' before the +; that's only documented to work with .)
– Neil
Commented May 7, 2022 at 6:46
• Make that 13 bytes, because I forgot to remove the w.
– Neil
Commented May 7, 2022 at 6:51

K (ngn/k), 21 bytes

Most straightforward K solution.

{"+"/?[x;;"'"]'1+!#x}

Try it online!

Explanation

{"+"/?[x;;"'"]'1+!#x}
?[x;;"'"]          / insert "'" in string at
'         / each position
1+!#x    / 1..len of string
"+"/                   / join the resulting strings with "+"

Wolfram Language (Mathematica), 45 36 bytes

aRiffle[a/.#->#<>"'"&/@a,"+"]<>""

Try it online!

Input [{characters...}]. Assumes all characters in input are unique. Returns a string.

Wolfram Language (Mathematica), 35 bytes

ToString@Expand[Dt[1##]/._@a_:>a']&

Try it online!

Input [symbols...]. Assumes symbols are unique and not defined. Returns a spaced string.

Dt treats strings as constants.

JavaScript (Node.js), 83 $$\\cdots\$$ 72 69 bytes

s=>g=(r="",i=0)=>s[i]?g(r+("+"+s).slice(!i,++i+1)+"'"+s.slice(i),i):r

Try it online!

Saved 7 8 bytes thanks to emanresu A!!!
Saved 3 bytes thanks to Samathingamajig!!!

• r.join+ and s[i-1] I think Commented May 7, 2022 at 23:44
• @emanresuA Very nice, this is my first JavaScript golf so thanks very much! :D Commented May 7, 2022 at 23:51
• You might be able to save some more bytes by using a string for r although conditionally adding the + might cost more bytes than it's worth. Commented May 7, 2022 at 23:52
• Maybe if you conditionally add the + and just return r at the end? Commented May 8, 2022 at 0:17
• @emanresuA Found it! Thanks for the encouragement. :))) Commented May 8, 2022 at 17:57

Charcoal, 10 bytes

⪫⪪⪫Ｅθθ+Ｌθ'

Try it online! Link is to verbose version of code. Explanation: Port of @DLosc's Pip answer.

θ       Input string
Ｅ        For each character
θ      Input string
⪫         Join on
+     Literal string +
⪪          Split into substrings of length
θ   Input string
Ｌ    Length
⪫           Join on
'  Literal string '
Implicitly print

Husk, 14 12 bytes

J''CL¹J'+SRL

Try it online!

Same approach as DLosc's Pip answer.

BQN, 21 bytesSBCS

1↓⟜∾'+'∾¨1↓↑∾⟜'''⊸∾¨↓

Run online!

gives prefixes, suffixes. ∾⟜'''⊸∾¨ joins matching prefixes and suffixes with a single quote.

1↓ removes the first value (empty prefix ∾ ' ∾ full suffix).

'+'∾¨ prepends a + to each string.

1↓⟜∾ joins the string and removes the leading +.

• 20 bytes using Fold to join the string Commented May 9, 2022 at 21:50

BQN, 28 bytesSBCS

{1↓⥊'''∾˘⟨∘,≠𝕩⟩⥊«⥊'+'∾˘⊢⌜˜𝕩}

Run online!

returns the required string with a trailing space. removing that is +3 bytes

-3 from ovs, who also made this tacit.

• ''' feels so cursed to me lol Commented May 8, 2022 at 8:24
• save 1 byte by changing ⥊'+'∾˘𝕩⥊˜∾˜≠𝕩 to (∾'+'∾¨≠⥊⋈)𝕩. Still seems a bit awkward, though... Commented May 8, 2022 at 10:05
• 𝕩⥊˜∾˜≠𝕩 can be ⊢⌜˜𝕩. Then tacit is the same length
– ovs
Commented May 8, 2022 at 10:12

v=$1\';until [$v = \'$1 ];do a+=+$v;v=$(sed "s/$$.$$'/'\1/"<<<$v);done;echo ${a#+} Try it online! C (gcc), 151 bytes char*f(char*c,int i){char*r=malloc(i*i+i);char*o=r,k=0;for(;k<i*i;){if(k++%(i+1)==1)*o++='\'';*o++=*c++;if(!*c)c-=i,*o++='+';}*o--=0;*o='\'';return r;} Try it online! • 122 bytes Commented May 8, 2022 at 20:08 Julia, 40 bytes a->join([replace(a,i=>"$i'") for i=a],+)

Attempt This Online!

only works with all letters distinct

Why...how...what...

• [... for i=a] iterates over the string a and store the results in a list. i will be a Char
• replace(a, i=>"$i'") replaces all characters equal to i in a with i followed by ' • join(..., +) joins the list with +. + is a function, so string(+) == "+" is used. • Why...how...what... Can you remove the space between the ) and the for? Commented May 9, 2022 at 9:31 • @emanresuA sadly no. It might have been possible in older versions of Julia Commented May 9, 2022 at 9:48 • I will add an explanation Commented May 9, 2022 at 9:49 R, 81 75 bytes Edit: -6 bytes thanks to Giuseppe function(x,[=substring)paste0(x[1,s<-1:nchar(x)],"'",x[s+1],collapse="+") Try it online! Takes advantage of the vectorization of substring over a vector of start (& optional end) positions, and the vectorization & argument-recycling of paste over vectors of strings to paste-together. • 75 bytes Commented May 10, 2022 at 16:04 • @Giuseppe - Ow. How on earth did I manage not to do that? Thanks. Commented May 10, 2022 at 16:19 Vyxal, 8 bytes ż\'vṀ\+j Try it Online! No clue why I didn't vectorise insertion back in May 2022. Explained żƛ?n\'Ṁ;\+j żƛ # For each item P in the range 1...len(input): ?n # at position P in the input, \'Ṁ # insert a ' ; # end map \+j # join the result of that on "+" and output • I have a completely different 11 Commented May 7, 2022 at 0:41 • interesting. I'm still golfing so maybe I'll break 12 Commented May 7, 2022 at 0:42 • @emanresuA is that the 11 you had? Commented May 7, 2022 at 0:47 • Yep :) - Maybe there's something better Commented May 7, 2022 at 0:50 • A flag to make implicit stack pushing in lambdas use the input instead of lambda arguments would be useful Commented May 7, 2022 at 2:01 Retina 0.8.2, 14 bytes .$$&'$'+
.$Try it online! Link includes test cases. Explanation: .$$&'$'+

Replace each variable with its derivative multiplied by the other terms plus a trailing + to sum the products together.

.$Delete the final trailing +. Jelly, 10 bytes ṛ€j”+sLj”' Try it online! Port of DLosc's Pip solution. € For each element of the input, ṛ the input. j”+ Join the copies on +, sL split into slices the length of the original input, j”' and join those on '. Jelly, 11 bytes p”'$ṛ¦ⱮJj”+

Try it online!

The implementation of ¦ is a bit questionable, so we can just abuse Cartesian product to make up for it.

p”'            Pair each element of the input with ',
$ṛ¦ and substitute elements of that into the input at ⱮJ each index, individually. j”+ Join the results on +. Just for fun, without preserving the order within each term, ṭ-Ƥ”'ż@j”+ is 10. PARI/GP, 58 bytes s->j=strjoin;j([j(strsplit(s,c),Str(c"'"))|c<-Vec(s)],"+") Attempt This Online! A port of @att's Mathematica answer. Takes input as a string. Requires that all characters are unique. PARI/GP, 64 bytes v->i=1;j=strjoin;j([j([j(v[1..i]),j(v[i++..#v])],"'")|c<-v],"+") Attempt This Online! Take input as a list of characters. 05AB1E, 10 bytes ε''«Nǝ}'+ý Try it online! ε map each character (of the implicit input string) '' the ' character « append to the current character N iteration index ǝ replace the input string's Nth character with the character with ' appended } '+ + character ý join by it • Nice answer, +1 from me, but you forgot to remove the explicit Iy inside your TIO link. :) Commented May 9, 2022 at 8:59 Raku, 33 bytes This 33-byte version works on my local recent-ish version of Raku: {join '+',.comb.map:{S/$^a/$a'/}} But not on TIO, where a few more bytes are needed: {join '+',.comb.map:{$^a;S/$a/$a'/}}

Try it online!

• .comb breaks the input string up into characters.
• .map: { S/$^a/$a'/ } returns a copy of the input string for each character, in which that character has had an apostrophe appended to it.
• join '+' joins those strings together with a plus sign.

Japt, 10 bytes

¬£i''YÄÃq+

Try it

¬£i''YÄÃq+     :Implicit input of string
¬              :Split
£             :Map each element at 0-based index Y
i''          :  Insert a "'" in U at index
YÄ        :  Y+1
Ã       :End map
q+     :Join with "+"

Ruby, 36 bytes

->s{s.chars.map{s.sub _1,'\&\''}*?+}

Attempt This Online!

J-uby, 52 bytes

~(:-&(:& &:sub|(~:%&(~:+&?')|:& &:*)|:|&A)).D|~:*&?+

Attempt This Online!

Rust, 123 bytes

|s|{s.chars().map(|_|s).enumerate().map(|(i,w)|{let mut t=w.repeat(1);t.insert(i+1,'\'');t}).collect::<Vec<_>>().join("+")}

Attempt This Online!