Multiply numerical polynomials

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
7
down vote

favorite












A numerical polynomial is a polynomial p in one variable with rational coefficients such that for every integer i, p(i) is also an integer. The numerical polynomials have a basis given by the binomial coefficients:



enter image description here



For instance:



enter image description here



The product of any two numerical polynomials is a numerical polynomial, so there are formulas expressing p_m * p_n as a linear combination of p_0, p_1, ..., p_m+n.



Your job is to produce these formulas.



Goal:



Input: A pair of positive integers m and n



Output: The list of integers [a_1,...,a_m+n] of length m+n such that



$p_mtimes p_n = sum_i=1^m+n a_ip_i$



This is code golf, so shortest code wins.



Examples:



enter image description here



Test Cases:



(1,1) ==> [1,2]
(1,2) ==> [0,2,3]
(1,3) ==> [0, 0, 3, 4]
(1,4) ==> [0, 0, 0, 4, 5]
(2,2) ==> [0, 1, 6, 6]
(2,3) ==> [0, 0, 3, 12, 10]
(2,4) ==> [0, 0, 0, 6, 20, 15]
(3,4) ==> [0, 0, 0, 4, 30, 60, 35]
(4,4) ==> [0, 0, 0, 1, 20, 90, 140, 70]









share|improve this question



















  • 4




    Just an FYI we have MathJax now so you can use $ to write in-line equations like $ p_m $ instead of using images.
    – FryAmTheEggman
    20 hours ago










  • Looks like every test is missing the $a_0=0$ entry; so are the inputs actually going to be strictly positive integers or should (0,0) yield an empty list?
    – Jonathan Allan
    20 hours ago










  • @JonathanAllan Strictly positive -- none of the polynomials have a constant coefficient so it would be kind of boring.
    – Hood
    19 hours ago










  • @FryAmTheEggman That's good to know because making the images is quite annoying.
    – Hood
    19 hours ago










  • @JonathanAllan I guess I shouldn't have given p_0 as an example of a basis element...
    – Hood
    19 hours ago














up vote
7
down vote

favorite












A numerical polynomial is a polynomial p in one variable with rational coefficients such that for every integer i, p(i) is also an integer. The numerical polynomials have a basis given by the binomial coefficients:



enter image description here



For instance:



enter image description here



The product of any two numerical polynomials is a numerical polynomial, so there are formulas expressing p_m * p_n as a linear combination of p_0, p_1, ..., p_m+n.



Your job is to produce these formulas.



Goal:



Input: A pair of positive integers m and n



Output: The list of integers [a_1,...,a_m+n] of length m+n such that



$p_mtimes p_n = sum_i=1^m+n a_ip_i$



This is code golf, so shortest code wins.



Examples:



enter image description here



Test Cases:



(1,1) ==> [1,2]
(1,2) ==> [0,2,3]
(1,3) ==> [0, 0, 3, 4]
(1,4) ==> [0, 0, 0, 4, 5]
(2,2) ==> [0, 1, 6, 6]
(2,3) ==> [0, 0, 3, 12, 10]
(2,4) ==> [0, 0, 0, 6, 20, 15]
(3,4) ==> [0, 0, 0, 4, 30, 60, 35]
(4,4) ==> [0, 0, 0, 1, 20, 90, 140, 70]









share|improve this question



















  • 4




    Just an FYI we have MathJax now so you can use $ to write in-line equations like $ p_m $ instead of using images.
    – FryAmTheEggman
    20 hours ago










  • Looks like every test is missing the $a_0=0$ entry; so are the inputs actually going to be strictly positive integers or should (0,0) yield an empty list?
    – Jonathan Allan
    20 hours ago










  • @JonathanAllan Strictly positive -- none of the polynomials have a constant coefficient so it would be kind of boring.
    – Hood
    19 hours ago










  • @FryAmTheEggman That's good to know because making the images is quite annoying.
    – Hood
    19 hours ago










  • @JonathanAllan I guess I shouldn't have given p_0 as an example of a basis element...
    – Hood
    19 hours ago












up vote
7
down vote

favorite









up vote
7
down vote

favorite











A numerical polynomial is a polynomial p in one variable with rational coefficients such that for every integer i, p(i) is also an integer. The numerical polynomials have a basis given by the binomial coefficients:



enter image description here



For instance:



enter image description here



The product of any two numerical polynomials is a numerical polynomial, so there are formulas expressing p_m * p_n as a linear combination of p_0, p_1, ..., p_m+n.



Your job is to produce these formulas.



Goal:



Input: A pair of positive integers m and n



Output: The list of integers [a_1,...,a_m+n] of length m+n such that



$p_mtimes p_n = sum_i=1^m+n a_ip_i$



This is code golf, so shortest code wins.



Examples:



enter image description here



Test Cases:



(1,1) ==> [1,2]
(1,2) ==> [0,2,3]
(1,3) ==> [0, 0, 3, 4]
(1,4) ==> [0, 0, 0, 4, 5]
(2,2) ==> [0, 1, 6, 6]
(2,3) ==> [0, 0, 3, 12, 10]
(2,4) ==> [0, 0, 0, 6, 20, 15]
(3,4) ==> [0, 0, 0, 4, 30, 60, 35]
(4,4) ==> [0, 0, 0, 1, 20, 90, 140, 70]









share|improve this question















A numerical polynomial is a polynomial p in one variable with rational coefficients such that for every integer i, p(i) is also an integer. The numerical polynomials have a basis given by the binomial coefficients:



enter image description here



For instance:



enter image description here



The product of any two numerical polynomials is a numerical polynomial, so there are formulas expressing p_m * p_n as a linear combination of p_0, p_1, ..., p_m+n.



Your job is to produce these formulas.



Goal:



Input: A pair of positive integers m and n



Output: The list of integers [a_1,...,a_m+n] of length m+n such that



$p_mtimes p_n = sum_i=1^m+n a_ip_i$



This is code golf, so shortest code wins.



Examples:



enter image description here



Test Cases:



(1,1) ==> [1,2]
(1,2) ==> [0,2,3]
(1,3) ==> [0, 0, 3, 4]
(1,4) ==> [0, 0, 0, 4, 5]
(2,2) ==> [0, 1, 6, 6]
(2,3) ==> [0, 0, 3, 12, 10]
(2,4) ==> [0, 0, 0, 6, 20, 15]
(3,4) ==> [0, 0, 0, 4, 30, 60, 35]
(4,4) ==> [0, 0, 0, 1, 20, 90, 140, 70]






code-golf math polynomials






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 1 hour ago









alephalpha

20.5k32886




20.5k32886










asked 20 hours ago









Hood

3868




3868







  • 4




    Just an FYI we have MathJax now so you can use $ to write in-line equations like $ p_m $ instead of using images.
    – FryAmTheEggman
    20 hours ago










  • Looks like every test is missing the $a_0=0$ entry; so are the inputs actually going to be strictly positive integers or should (0,0) yield an empty list?
    – Jonathan Allan
    20 hours ago










  • @JonathanAllan Strictly positive -- none of the polynomials have a constant coefficient so it would be kind of boring.
    – Hood
    19 hours ago










  • @FryAmTheEggman That's good to know because making the images is quite annoying.
    – Hood
    19 hours ago










  • @JonathanAllan I guess I shouldn't have given p_0 as an example of a basis element...
    – Hood
    19 hours ago












  • 4




    Just an FYI we have MathJax now so you can use $ to write in-line equations like $ p_m $ instead of using images.
    – FryAmTheEggman
    20 hours ago










  • Looks like every test is missing the $a_0=0$ entry; so are the inputs actually going to be strictly positive integers or should (0,0) yield an empty list?
    – Jonathan Allan
    20 hours ago










  • @JonathanAllan Strictly positive -- none of the polynomials have a constant coefficient so it would be kind of boring.
    – Hood
    19 hours ago










  • @FryAmTheEggman That's good to know because making the images is quite annoying.
    – Hood
    19 hours ago










  • @JonathanAllan I guess I shouldn't have given p_0 as an example of a basis element...
    – Hood
    19 hours ago







4




4




Just an FYI we have MathJax now so you can use $ to write in-line equations like $ p_m $ instead of using images.
– FryAmTheEggman
20 hours ago




Just an FYI we have MathJax now so you can use $ to write in-line equations like $ p_m $ instead of using images.
– FryAmTheEggman
20 hours ago












Looks like every test is missing the $a_0=0$ entry; so are the inputs actually going to be strictly positive integers or should (0,0) yield an empty list?
– Jonathan Allan
20 hours ago




Looks like every test is missing the $a_0=0$ entry; so are the inputs actually going to be strictly positive integers or should (0,0) yield an empty list?
– Jonathan Allan
20 hours ago












@JonathanAllan Strictly positive -- none of the polynomials have a constant coefficient so it would be kind of boring.
– Hood
19 hours ago




@JonathanAllan Strictly positive -- none of the polynomials have a constant coefficient so it would be kind of boring.
– Hood
19 hours ago












@FryAmTheEggman That's good to know because making the images is quite annoying.
– Hood
19 hours ago




@FryAmTheEggman That's good to know because making the images is quite annoying.
– Hood
19 hours ago












@JonathanAllan I guess I shouldn't have given p_0 as an example of a basis element...
– Hood
19 hours ago




@JonathanAllan I guess I shouldn't have given p_0 as an example of a basis element...
– Hood
19 hours ago










5 Answers
5






active

oldest

votes

















up vote
4
down vote














Haskell, 84 bytes





n#m=[foldr1(-)[i!k*k!n*k!m|k<-[i,i-1..0]]|i<-[1..n+m]]
_!0=1
n!k=n*(n-1)!(k-1)`div`k


Try it online!



Explanation



Given a polynomial $q$, the following algorithm (see here) computes the unique coefficients $a_0,a_1,ldots$ such that $q=a_0p_0+a_1p_1+ldots$.



Let
$$
Delta^(0)q(x)=q(x)\
Delta^(i+1)q(x)=Delta^(i)q(x+1)-Delta^(i)q(x).
$$



Then $a_i=Delta^(i)q(0)$.



Moreover, it can be easily proved that
$$
Delta^(i)q(0)=sum_k=0^i(-1)^i-kichoose kq(k).
$$




_!0=1
n!k=n*(n-1)!(k-1)`div`k


n!k is the binomial coefficient $nchoose k$.




n#m=[foldr1(-)[i!k*k!n*k!m|k<-[i,i-1..0]]|i<-[1..n+m]]


The function # computes the answer to the challenge. It simply uses the observation above to compute
$$
a_i=sum_k=0^i(-1)^i-kichoose kkchoose nkchoose m.
$$

The foldr1(-) is the only thing that needs explaining. When applied to a list $[b_0,b_1,ldots,b_l]$, foldr1(-) returns
$$
b_0-(b_1-(ldots-(b_l-1-b_l)ldots)=sum_j=0^l(-1)^jb_j
$$





Haskell, 108 100 bytes



I'm also keeping this one. It is, in my opinion, more elegant, albeit much more verbose.



n#m=take(n+m).tail$head<$>iterate(zipWith(-)=<<tail)[x!n*x!m|x<-[0..]]
_!0=1
n!k=n*(n-1)!(k-1)`div`k


Try it online!




_!0=1
n!k=n*(n-1)!(k-1)`div`k


n!k is the binomial coefficient $nchoose k$.




n#m=take(n+m).tail$head<$>iterate(zipWith(-)=<<tail)[x!n*x!m|x<-[0..]]


The function # computes the answer to the challenge. Let $q=p_ncdot p_m$.




  • [x!n*x!m|x<-[0..]] is the infinite list
    $$
    left[0choose n0choose m,1choose n1choose m,2choose n2choose m,ldotsright]
    $$

    i.e.
    $$
    [q(0),q(1),q(2),ldots].
    $$


  • Given a list $[b_0,b_1,b_2,ldots]$, the function zipWith(-)=<<tail returns $[b_1-b_0,b_2-b_1,ldots]$. Thus the code head<$>iterate(zipWith(-)=<<tail)[x!n*x!m|x<-[0..]] returns the infinite list
    $$
    [Delta^(0)q(0),Delta^(1)q(0),Delta^(2)q(0),ldots]=[a_0,a_1,a_2,ldots].
    $$


  • Finally with take(n+m).tail we drop $a_0$ and we take the first $n+m$ coefficients, thereby solving the challenge.





share|improve this answer






















  • Cool! I guess this is a sort of discrete Taylor expansion.
    – Hood
    19 hours ago










  • @Hood Indeed ;)
    – Delfad0r
    19 hours ago










  • This description also explains why p_m*p_n never involves binomial coefficients lower than min(m,n): the discrete derivative has a product rule: D(f*g)=D(f(x))*g(x+1) + f(x)*D(g(x)). Applying this k times, you can see that D^k(f*g)=D^n(f(x))*g(x+k) + D^k-1(f(x))*D(g(x+k-1)) + ... + f(x)*D^n(g(x)). If k<n then all of the derivatives of p_n have value zero at zero, so D^k(p_n*g)(0)=0.
    – Hood
    3 hours ago


















up vote
3
down vote














Haskell, 74 bytes





a%b|let a?i|a<1=0^(i-b)^2|d<-a-1=div(i*d?(i-1)-(d-i)*d?i)a=map(a?)[1..a+b]


Try it online!



Uses a recursive formulation I found. Let $g_b(a,i)$ be the coefficient of $p_i$ in $p_a p_b$, which is implemented in the code by a?i (with b fixed). We can express $g_b$ recursively as



$$g_b(a,i)=fracicdot g_b(a-1,i-1)-(a-1-i)cdot g_b(a-1,i-1)a$$



with base case
$$g_b(0,i)=
begincases
1,text if b=i\
0,text otherwise\
endcases
$$



The base case corresponds to the expansion for $p_b$, which has a single nonzero coefficient of $p_i$ at $i=b$. The code implements this indicator function as 0^(i-b)^2.



The code has guards belonging to the definition of ? in the let binding. It's perhaps easier to read as the following (non-working) code where the definition of a?i expects b as a global variable. Perhaps confusingly, the a in the definition of ? is not always the same as the input to %, since it recurses down.



a?i|a<1=0^(i-b)^2|d<-a-1=div(i*d?(i-1)-(d-i)*d?i)a
a%b=map(a?)[1..a+b]


The main function % lists the coefficients for each i from 1 to a+b. It's possible to code the recursion in % directly by zipping shifted lists, but I did not find a short way to do it especially with the dependence on the position i.






share|improve this answer



























    up vote
    2
    down vote














    Pari/GP, 66 bytes



    (m,n)->b=matpascal(m+n);([b[i,m+1]*b[i,n+1]|i<-[1..#b]]/b~)[2..#b]


    Try it online!






    share|improve this answer




















    • Nice answer. As a completely minor golf, curryinng gives -1 bytes (m->n->).
      – Mr. Xcoder
      21 mins ago

















    up vote
    1
    down vote













    JavaScript (ES6), 109 bytes



    This is really just a port of Delfad0r's great answer, implemented as nested recursive functions.



    Takes input as (n)(m).





    n=>m=>(g=i=>i?[...g(i-1),(h=k=>k&&h(k-1)-(b=(y,x=k)=>y?x*b(y-1,x-1)/y:i-k&1||-1)(k,i)*b(n)*b(m))(i)]:)(n+m)


    Try it online!



    How?



    The function $b(y,x)$ computes:



    $$-(-1)^i-kxchoose y$$



    It's slightly shorter to return $-(-1)^i-k$ as the last iteration of this function than processing it separately. Because we compute the product of three binomial coefficients, the signs of the first two of them are simply cancelling each other out.



    The function $h()$ is used to compute:



    $$sum_k=1^i-b(k,i)times b(n,k)times b(m,k)\
    =sum_k=1^i(-1)^i-kichoose kkchoose nkchoose m$$



    And the function $g()$ is used to compute each term $a_i$ for $1le i le n+m$.






    share|improve this answer





























      up vote
      1
      down vote














      Wolfram Language (Mathematica), 58 bytes



      Inverse[b=Array[Binomial,+##1,1]].(b[[;;,#]]b[[;;,#2]])&


      Try it online!






      share|improve this answer




















        Your Answer




        StackExchange.ifUsing("editor", function ()
        return StackExchange.using("mathjaxEditing", function ()
        StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
        StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
        );
        );
        , "mathjax-editing");

        StackExchange.ifUsing("editor", function ()
        StackExchange.using("externalEditor", function ()
        StackExchange.using("snippets", function ()
        StackExchange.snippets.init();
        );
        );
        , "code-snippets");

        StackExchange.ready(function()
        var channelOptions =
        tags: "".split(" "),
        id: "200"
        ;
        initTagRenderer("".split(" "), "".split(" "), channelOptions);

        StackExchange.using("externalEditor", function()
        // Have to fire editor after snippets, if snippets enabled
        if (StackExchange.settings.snippets.snippetsEnabled)
        StackExchange.using("snippets", function()
        createEditor();
        );

        else
        createEditor();

        );

        function createEditor()
        StackExchange.prepareEditor(
        heartbeatType: 'answer',
        convertImagesToLinks: false,
        noModals: false,
        showLowRepImageUploadWarning: true,
        reputationToPostImages: null,
        bindNavPrevention: true,
        postfix: "",
        onDemand: true,
        discardSelector: ".discard-answer"
        ,immediatelyShowMarkdownHelp:true
        );



        );













         

        draft saved


        draft discarded


















        StackExchange.ready(
        function ()
        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodegolf.stackexchange.com%2fquestions%2f173984%2fmultiply-numerical-polynomials%23new-answer', 'question_page');

        );

        Post as a guest






























        5 Answers
        5






        active

        oldest

        votes








        5 Answers
        5






        active

        oldest

        votes









        active

        oldest

        votes






        active

        oldest

        votes








        up vote
        4
        down vote














        Haskell, 84 bytes





        n#m=[foldr1(-)[i!k*k!n*k!m|k<-[i,i-1..0]]|i<-[1..n+m]]
        _!0=1
        n!k=n*(n-1)!(k-1)`div`k


        Try it online!



        Explanation



        Given a polynomial $q$, the following algorithm (see here) computes the unique coefficients $a_0,a_1,ldots$ such that $q=a_0p_0+a_1p_1+ldots$.



        Let
        $$
        Delta^(0)q(x)=q(x)\
        Delta^(i+1)q(x)=Delta^(i)q(x+1)-Delta^(i)q(x).
        $$



        Then $a_i=Delta^(i)q(0)$.



        Moreover, it can be easily proved that
        $$
        Delta^(i)q(0)=sum_k=0^i(-1)^i-kichoose kq(k).
        $$




        _!0=1
        n!k=n*(n-1)!(k-1)`div`k


        n!k is the binomial coefficient $nchoose k$.




        n#m=[foldr1(-)[i!k*k!n*k!m|k<-[i,i-1..0]]|i<-[1..n+m]]


        The function # computes the answer to the challenge. It simply uses the observation above to compute
        $$
        a_i=sum_k=0^i(-1)^i-kichoose kkchoose nkchoose m.
        $$

        The foldr1(-) is the only thing that needs explaining. When applied to a list $[b_0,b_1,ldots,b_l]$, foldr1(-) returns
        $$
        b_0-(b_1-(ldots-(b_l-1-b_l)ldots)=sum_j=0^l(-1)^jb_j
        $$





        Haskell, 108 100 bytes



        I'm also keeping this one. It is, in my opinion, more elegant, albeit much more verbose.



        n#m=take(n+m).tail$head<$>iterate(zipWith(-)=<<tail)[x!n*x!m|x<-[0..]]
        _!0=1
        n!k=n*(n-1)!(k-1)`div`k


        Try it online!




        _!0=1
        n!k=n*(n-1)!(k-1)`div`k


        n!k is the binomial coefficient $nchoose k$.




        n#m=take(n+m).tail$head<$>iterate(zipWith(-)=<<tail)[x!n*x!m|x<-[0..]]


        The function # computes the answer to the challenge. Let $q=p_ncdot p_m$.




        • [x!n*x!m|x<-[0..]] is the infinite list
          $$
          left[0choose n0choose m,1choose n1choose m,2choose n2choose m,ldotsright]
          $$

          i.e.
          $$
          [q(0),q(1),q(2),ldots].
          $$


        • Given a list $[b_0,b_1,b_2,ldots]$, the function zipWith(-)=<<tail returns $[b_1-b_0,b_2-b_1,ldots]$. Thus the code head<$>iterate(zipWith(-)=<<tail)[x!n*x!m|x<-[0..]] returns the infinite list
          $$
          [Delta^(0)q(0),Delta^(1)q(0),Delta^(2)q(0),ldots]=[a_0,a_1,a_2,ldots].
          $$


        • Finally with take(n+m).tail we drop $a_0$ and we take the first $n+m$ coefficients, thereby solving the challenge.





        share|improve this answer






















        • Cool! I guess this is a sort of discrete Taylor expansion.
          – Hood
          19 hours ago










        • @Hood Indeed ;)
          – Delfad0r
          19 hours ago










        • This description also explains why p_m*p_n never involves binomial coefficients lower than min(m,n): the discrete derivative has a product rule: D(f*g)=D(f(x))*g(x+1) + f(x)*D(g(x)). Applying this k times, you can see that D^k(f*g)=D^n(f(x))*g(x+k) + D^k-1(f(x))*D(g(x+k-1)) + ... + f(x)*D^n(g(x)). If k<n then all of the derivatives of p_n have value zero at zero, so D^k(p_n*g)(0)=0.
          – Hood
          3 hours ago















        up vote
        4
        down vote














        Haskell, 84 bytes





        n#m=[foldr1(-)[i!k*k!n*k!m|k<-[i,i-1..0]]|i<-[1..n+m]]
        _!0=1
        n!k=n*(n-1)!(k-1)`div`k


        Try it online!



        Explanation



        Given a polynomial $q$, the following algorithm (see here) computes the unique coefficients $a_0,a_1,ldots$ such that $q=a_0p_0+a_1p_1+ldots$.



        Let
        $$
        Delta^(0)q(x)=q(x)\
        Delta^(i+1)q(x)=Delta^(i)q(x+1)-Delta^(i)q(x).
        $$



        Then $a_i=Delta^(i)q(0)$.



        Moreover, it can be easily proved that
        $$
        Delta^(i)q(0)=sum_k=0^i(-1)^i-kichoose kq(k).
        $$




        _!0=1
        n!k=n*(n-1)!(k-1)`div`k


        n!k is the binomial coefficient $nchoose k$.




        n#m=[foldr1(-)[i!k*k!n*k!m|k<-[i,i-1..0]]|i<-[1..n+m]]


        The function # computes the answer to the challenge. It simply uses the observation above to compute
        $$
        a_i=sum_k=0^i(-1)^i-kichoose kkchoose nkchoose m.
        $$

        The foldr1(-) is the only thing that needs explaining. When applied to a list $[b_0,b_1,ldots,b_l]$, foldr1(-) returns
        $$
        b_0-(b_1-(ldots-(b_l-1-b_l)ldots)=sum_j=0^l(-1)^jb_j
        $$





        Haskell, 108 100 bytes



        I'm also keeping this one. It is, in my opinion, more elegant, albeit much more verbose.



        n#m=take(n+m).tail$head<$>iterate(zipWith(-)=<<tail)[x!n*x!m|x<-[0..]]
        _!0=1
        n!k=n*(n-1)!(k-1)`div`k


        Try it online!




        _!0=1
        n!k=n*(n-1)!(k-1)`div`k


        n!k is the binomial coefficient $nchoose k$.




        n#m=take(n+m).tail$head<$>iterate(zipWith(-)=<<tail)[x!n*x!m|x<-[0..]]


        The function # computes the answer to the challenge. Let $q=p_ncdot p_m$.




        • [x!n*x!m|x<-[0..]] is the infinite list
          $$
          left[0choose n0choose m,1choose n1choose m,2choose n2choose m,ldotsright]
          $$

          i.e.
          $$
          [q(0),q(1),q(2),ldots].
          $$


        • Given a list $[b_0,b_1,b_2,ldots]$, the function zipWith(-)=<<tail returns $[b_1-b_0,b_2-b_1,ldots]$. Thus the code head<$>iterate(zipWith(-)=<<tail)[x!n*x!m|x<-[0..]] returns the infinite list
          $$
          [Delta^(0)q(0),Delta^(1)q(0),Delta^(2)q(0),ldots]=[a_0,a_1,a_2,ldots].
          $$


        • Finally with take(n+m).tail we drop $a_0$ and we take the first $n+m$ coefficients, thereby solving the challenge.





        share|improve this answer






















        • Cool! I guess this is a sort of discrete Taylor expansion.
          – Hood
          19 hours ago










        • @Hood Indeed ;)
          – Delfad0r
          19 hours ago










        • This description also explains why p_m*p_n never involves binomial coefficients lower than min(m,n): the discrete derivative has a product rule: D(f*g)=D(f(x))*g(x+1) + f(x)*D(g(x)). Applying this k times, you can see that D^k(f*g)=D^n(f(x))*g(x+k) + D^k-1(f(x))*D(g(x+k-1)) + ... + f(x)*D^n(g(x)). If k<n then all of the derivatives of p_n have value zero at zero, so D^k(p_n*g)(0)=0.
          – Hood
          3 hours ago













        up vote
        4
        down vote










        up vote
        4
        down vote










        Haskell, 84 bytes





        n#m=[foldr1(-)[i!k*k!n*k!m|k<-[i,i-1..0]]|i<-[1..n+m]]
        _!0=1
        n!k=n*(n-1)!(k-1)`div`k


        Try it online!



        Explanation



        Given a polynomial $q$, the following algorithm (see here) computes the unique coefficients $a_0,a_1,ldots$ such that $q=a_0p_0+a_1p_1+ldots$.



        Let
        $$
        Delta^(0)q(x)=q(x)\
        Delta^(i+1)q(x)=Delta^(i)q(x+1)-Delta^(i)q(x).
        $$



        Then $a_i=Delta^(i)q(0)$.



        Moreover, it can be easily proved that
        $$
        Delta^(i)q(0)=sum_k=0^i(-1)^i-kichoose kq(k).
        $$




        _!0=1
        n!k=n*(n-1)!(k-1)`div`k


        n!k is the binomial coefficient $nchoose k$.




        n#m=[foldr1(-)[i!k*k!n*k!m|k<-[i,i-1..0]]|i<-[1..n+m]]


        The function # computes the answer to the challenge. It simply uses the observation above to compute
        $$
        a_i=sum_k=0^i(-1)^i-kichoose kkchoose nkchoose m.
        $$

        The foldr1(-) is the only thing that needs explaining. When applied to a list $[b_0,b_1,ldots,b_l]$, foldr1(-) returns
        $$
        b_0-(b_1-(ldots-(b_l-1-b_l)ldots)=sum_j=0^l(-1)^jb_j
        $$





        Haskell, 108 100 bytes



        I'm also keeping this one. It is, in my opinion, more elegant, albeit much more verbose.



        n#m=take(n+m).tail$head<$>iterate(zipWith(-)=<<tail)[x!n*x!m|x<-[0..]]
        _!0=1
        n!k=n*(n-1)!(k-1)`div`k


        Try it online!




        _!0=1
        n!k=n*(n-1)!(k-1)`div`k


        n!k is the binomial coefficient $nchoose k$.




        n#m=take(n+m).tail$head<$>iterate(zipWith(-)=<<tail)[x!n*x!m|x<-[0..]]


        The function # computes the answer to the challenge. Let $q=p_ncdot p_m$.




        • [x!n*x!m|x<-[0..]] is the infinite list
          $$
          left[0choose n0choose m,1choose n1choose m,2choose n2choose m,ldotsright]
          $$

          i.e.
          $$
          [q(0),q(1),q(2),ldots].
          $$


        • Given a list $[b_0,b_1,b_2,ldots]$, the function zipWith(-)=<<tail returns $[b_1-b_0,b_2-b_1,ldots]$. Thus the code head<$>iterate(zipWith(-)=<<tail)[x!n*x!m|x<-[0..]] returns the infinite list
          $$
          [Delta^(0)q(0),Delta^(1)q(0),Delta^(2)q(0),ldots]=[a_0,a_1,a_2,ldots].
          $$


        • Finally with take(n+m).tail we drop $a_0$ and we take the first $n+m$ coefficients, thereby solving the challenge.





        share|improve this answer















        Haskell, 84 bytes





        n#m=[foldr1(-)[i!k*k!n*k!m|k<-[i,i-1..0]]|i<-[1..n+m]]
        _!0=1
        n!k=n*(n-1)!(k-1)`div`k


        Try it online!



        Explanation



        Given a polynomial $q$, the following algorithm (see here) computes the unique coefficients $a_0,a_1,ldots$ such that $q=a_0p_0+a_1p_1+ldots$.



        Let
        $$
        Delta^(0)q(x)=q(x)\
        Delta^(i+1)q(x)=Delta^(i)q(x+1)-Delta^(i)q(x).
        $$



        Then $a_i=Delta^(i)q(0)$.



        Moreover, it can be easily proved that
        $$
        Delta^(i)q(0)=sum_k=0^i(-1)^i-kichoose kq(k).
        $$




        _!0=1
        n!k=n*(n-1)!(k-1)`div`k


        n!k is the binomial coefficient $nchoose k$.




        n#m=[foldr1(-)[i!k*k!n*k!m|k<-[i,i-1..0]]|i<-[1..n+m]]


        The function # computes the answer to the challenge. It simply uses the observation above to compute
        $$
        a_i=sum_k=0^i(-1)^i-kichoose kkchoose nkchoose m.
        $$

        The foldr1(-) is the only thing that needs explaining. When applied to a list $[b_0,b_1,ldots,b_l]$, foldr1(-) returns
        $$
        b_0-(b_1-(ldots-(b_l-1-b_l)ldots)=sum_j=0^l(-1)^jb_j
        $$





        Haskell, 108 100 bytes



        I'm also keeping this one. It is, in my opinion, more elegant, albeit much more verbose.



        n#m=take(n+m).tail$head<$>iterate(zipWith(-)=<<tail)[x!n*x!m|x<-[0..]]
        _!0=1
        n!k=n*(n-1)!(k-1)`div`k


        Try it online!




        _!0=1
        n!k=n*(n-1)!(k-1)`div`k


        n!k is the binomial coefficient $nchoose k$.




        n#m=take(n+m).tail$head<$>iterate(zipWith(-)=<<tail)[x!n*x!m|x<-[0..]]


        The function # computes the answer to the challenge. Let $q=p_ncdot p_m$.




        • [x!n*x!m|x<-[0..]] is the infinite list
          $$
          left[0choose n0choose m,1choose n1choose m,2choose n2choose m,ldotsright]
          $$

          i.e.
          $$
          [q(0),q(1),q(2),ldots].
          $$


        • Given a list $[b_0,b_1,b_2,ldots]$, the function zipWith(-)=<<tail returns $[b_1-b_0,b_2-b_1,ldots]$. Thus the code head<$>iterate(zipWith(-)=<<tail)[x!n*x!m|x<-[0..]] returns the infinite list
          $$
          [Delta^(0)q(0),Delta^(1)q(0),Delta^(2)q(0),ldots]=[a_0,a_1,a_2,ldots].
          $$


        • Finally with take(n+m).tail we drop $a_0$ and we take the first $n+m$ coefficients, thereby solving the challenge.






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited 10 hours ago

























        answered 19 hours ago









        Delfad0r

        963213




        963213











        • Cool! I guess this is a sort of discrete Taylor expansion.
          – Hood
          19 hours ago










        • @Hood Indeed ;)
          – Delfad0r
          19 hours ago










        • This description also explains why p_m*p_n never involves binomial coefficients lower than min(m,n): the discrete derivative has a product rule: D(f*g)=D(f(x))*g(x+1) + f(x)*D(g(x)). Applying this k times, you can see that D^k(f*g)=D^n(f(x))*g(x+k) + D^k-1(f(x))*D(g(x+k-1)) + ... + f(x)*D^n(g(x)). If k<n then all of the derivatives of p_n have value zero at zero, so D^k(p_n*g)(0)=0.
          – Hood
          3 hours ago

















        • Cool! I guess this is a sort of discrete Taylor expansion.
          – Hood
          19 hours ago










        • @Hood Indeed ;)
          – Delfad0r
          19 hours ago










        • This description also explains why p_m*p_n never involves binomial coefficients lower than min(m,n): the discrete derivative has a product rule: D(f*g)=D(f(x))*g(x+1) + f(x)*D(g(x)). Applying this k times, you can see that D^k(f*g)=D^n(f(x))*g(x+k) + D^k-1(f(x))*D(g(x+k-1)) + ... + f(x)*D^n(g(x)). If k<n then all of the derivatives of p_n have value zero at zero, so D^k(p_n*g)(0)=0.
          – Hood
          3 hours ago
















        Cool! I guess this is a sort of discrete Taylor expansion.
        – Hood
        19 hours ago




        Cool! I guess this is a sort of discrete Taylor expansion.
        – Hood
        19 hours ago












        @Hood Indeed ;)
        – Delfad0r
        19 hours ago




        @Hood Indeed ;)
        – Delfad0r
        19 hours ago












        This description also explains why p_m*p_n never involves binomial coefficients lower than min(m,n): the discrete derivative has a product rule: D(f*g)=D(f(x))*g(x+1) + f(x)*D(g(x)). Applying this k times, you can see that D^k(f*g)=D^n(f(x))*g(x+k) + D^k-1(f(x))*D(g(x+k-1)) + ... + f(x)*D^n(g(x)). If k<n then all of the derivatives of p_n have value zero at zero, so D^k(p_n*g)(0)=0.
        – Hood
        3 hours ago





        This description also explains why p_m*p_n never involves binomial coefficients lower than min(m,n): the discrete derivative has a product rule: D(f*g)=D(f(x))*g(x+1) + f(x)*D(g(x)). Applying this k times, you can see that D^k(f*g)=D^n(f(x))*g(x+k) + D^k-1(f(x))*D(g(x+k-1)) + ... + f(x)*D^n(g(x)). If k<n then all of the derivatives of p_n have value zero at zero, so D^k(p_n*g)(0)=0.
        – Hood
        3 hours ago











        up vote
        3
        down vote














        Haskell, 74 bytes





        a%b|let a?i|a<1=0^(i-b)^2|d<-a-1=div(i*d?(i-1)-(d-i)*d?i)a=map(a?)[1..a+b]


        Try it online!



        Uses a recursive formulation I found. Let $g_b(a,i)$ be the coefficient of $p_i$ in $p_a p_b$, which is implemented in the code by a?i (with b fixed). We can express $g_b$ recursively as



        $$g_b(a,i)=fracicdot g_b(a-1,i-1)-(a-1-i)cdot g_b(a-1,i-1)a$$



        with base case
        $$g_b(0,i)=
        begincases
        1,text if b=i\
        0,text otherwise\
        endcases
        $$



        The base case corresponds to the expansion for $p_b$, which has a single nonzero coefficient of $p_i$ at $i=b$. The code implements this indicator function as 0^(i-b)^2.



        The code has guards belonging to the definition of ? in the let binding. It's perhaps easier to read as the following (non-working) code where the definition of a?i expects b as a global variable. Perhaps confusingly, the a in the definition of ? is not always the same as the input to %, since it recurses down.



        a?i|a<1=0^(i-b)^2|d<-a-1=div(i*d?(i-1)-(d-i)*d?i)a
        a%b=map(a?)[1..a+b]


        The main function % lists the coefficients for each i from 1 to a+b. It's possible to code the recursion in % directly by zipping shifted lists, but I did not find a short way to do it especially with the dependence on the position i.






        share|improve this answer
























          up vote
          3
          down vote














          Haskell, 74 bytes





          a%b|let a?i|a<1=0^(i-b)^2|d<-a-1=div(i*d?(i-1)-(d-i)*d?i)a=map(a?)[1..a+b]


          Try it online!



          Uses a recursive formulation I found. Let $g_b(a,i)$ be the coefficient of $p_i$ in $p_a p_b$, which is implemented in the code by a?i (with b fixed). We can express $g_b$ recursively as



          $$g_b(a,i)=fracicdot g_b(a-1,i-1)-(a-1-i)cdot g_b(a-1,i-1)a$$



          with base case
          $$g_b(0,i)=
          begincases
          1,text if b=i\
          0,text otherwise\
          endcases
          $$



          The base case corresponds to the expansion for $p_b$, which has a single nonzero coefficient of $p_i$ at $i=b$. The code implements this indicator function as 0^(i-b)^2.



          The code has guards belonging to the definition of ? in the let binding. It's perhaps easier to read as the following (non-working) code where the definition of a?i expects b as a global variable. Perhaps confusingly, the a in the definition of ? is not always the same as the input to %, since it recurses down.



          a?i|a<1=0^(i-b)^2|d<-a-1=div(i*d?(i-1)-(d-i)*d?i)a
          a%b=map(a?)[1..a+b]


          The main function % lists the coefficients for each i from 1 to a+b. It's possible to code the recursion in % directly by zipping shifted lists, but I did not find a short way to do it especially with the dependence on the position i.






          share|improve this answer






















            up vote
            3
            down vote










            up vote
            3
            down vote










            Haskell, 74 bytes





            a%b|let a?i|a<1=0^(i-b)^2|d<-a-1=div(i*d?(i-1)-(d-i)*d?i)a=map(a?)[1..a+b]


            Try it online!



            Uses a recursive formulation I found. Let $g_b(a,i)$ be the coefficient of $p_i$ in $p_a p_b$, which is implemented in the code by a?i (with b fixed). We can express $g_b$ recursively as



            $$g_b(a,i)=fracicdot g_b(a-1,i-1)-(a-1-i)cdot g_b(a-1,i-1)a$$



            with base case
            $$g_b(0,i)=
            begincases
            1,text if b=i\
            0,text otherwise\
            endcases
            $$



            The base case corresponds to the expansion for $p_b$, which has a single nonzero coefficient of $p_i$ at $i=b$. The code implements this indicator function as 0^(i-b)^2.



            The code has guards belonging to the definition of ? in the let binding. It's perhaps easier to read as the following (non-working) code where the definition of a?i expects b as a global variable. Perhaps confusingly, the a in the definition of ? is not always the same as the input to %, since it recurses down.



            a?i|a<1=0^(i-b)^2|d<-a-1=div(i*d?(i-1)-(d-i)*d?i)a
            a%b=map(a?)[1..a+b]


            The main function % lists the coefficients for each i from 1 to a+b. It's possible to code the recursion in % directly by zipping shifted lists, but I did not find a short way to do it especially with the dependence on the position i.






            share|improve this answer













            Haskell, 74 bytes





            a%b|let a?i|a<1=0^(i-b)^2|d<-a-1=div(i*d?(i-1)-(d-i)*d?i)a=map(a?)[1..a+b]


            Try it online!



            Uses a recursive formulation I found. Let $g_b(a,i)$ be the coefficient of $p_i$ in $p_a p_b$, which is implemented in the code by a?i (with b fixed). We can express $g_b$ recursively as



            $$g_b(a,i)=fracicdot g_b(a-1,i-1)-(a-1-i)cdot g_b(a-1,i-1)a$$



            with base case
            $$g_b(0,i)=
            begincases
            1,text if b=i\
            0,text otherwise\
            endcases
            $$



            The base case corresponds to the expansion for $p_b$, which has a single nonzero coefficient of $p_i$ at $i=b$. The code implements this indicator function as 0^(i-b)^2.



            The code has guards belonging to the definition of ? in the let binding. It's perhaps easier to read as the following (non-working) code where the definition of a?i expects b as a global variable. Perhaps confusingly, the a in the definition of ? is not always the same as the input to %, since it recurses down.



            a?i|a<1=0^(i-b)^2|d<-a-1=div(i*d?(i-1)-(d-i)*d?i)a
            a%b=map(a?)[1..a+b]


            The main function % lists the coefficients for each i from 1 to a+b. It's possible to code the recursion in % directly by zipping shifted lists, but I did not find a short way to do it especially with the dependence on the position i.







            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered 2 hours ago









            xnor

            87.7k17182433




            87.7k17182433




















                up vote
                2
                down vote














                Pari/GP, 66 bytes



                (m,n)->b=matpascal(m+n);([b[i,m+1]*b[i,n+1]|i<-[1..#b]]/b~)[2..#b]


                Try it online!






                share|improve this answer




















                • Nice answer. As a completely minor golf, curryinng gives -1 bytes (m->n->).
                  – Mr. Xcoder
                  21 mins ago














                up vote
                2
                down vote














                Pari/GP, 66 bytes



                (m,n)->b=matpascal(m+n);([b[i,m+1]*b[i,n+1]|i<-[1..#b]]/b~)[2..#b]


                Try it online!






                share|improve this answer




















                • Nice answer. As a completely minor golf, curryinng gives -1 bytes (m->n->).
                  – Mr. Xcoder
                  21 mins ago












                up vote
                2
                down vote










                up vote
                2
                down vote










                Pari/GP, 66 bytes



                (m,n)->b=matpascal(m+n);([b[i,m+1]*b[i,n+1]|i<-[1..#b]]/b~)[2..#b]


                Try it online!






                share|improve this answer













                Pari/GP, 66 bytes



                (m,n)->b=matpascal(m+n);([b[i,m+1]*b[i,n+1]|i<-[1..#b]]/b~)[2..#b]


                Try it online!







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered 42 mins ago









                alephalpha

                20.5k32886




                20.5k32886











                • Nice answer. As a completely minor golf, curryinng gives -1 bytes (m->n->).
                  – Mr. Xcoder
                  21 mins ago
















                • Nice answer. As a completely minor golf, curryinng gives -1 bytes (m->n->).
                  – Mr. Xcoder
                  21 mins ago















                Nice answer. As a completely minor golf, curryinng gives -1 bytes (m->n->).
                – Mr. Xcoder
                21 mins ago




                Nice answer. As a completely minor golf, curryinng gives -1 bytes (m->n->).
                – Mr. Xcoder
                21 mins ago










                up vote
                1
                down vote













                JavaScript (ES6), 109 bytes



                This is really just a port of Delfad0r's great answer, implemented as nested recursive functions.



                Takes input as (n)(m).





                n=>m=>(g=i=>i?[...g(i-1),(h=k=>k&&h(k-1)-(b=(y,x=k)=>y?x*b(y-1,x-1)/y:i-k&1||-1)(k,i)*b(n)*b(m))(i)]:)(n+m)


                Try it online!



                How?



                The function $b(y,x)$ computes:



                $$-(-1)^i-kxchoose y$$



                It's slightly shorter to return $-(-1)^i-k$ as the last iteration of this function than processing it separately. Because we compute the product of three binomial coefficients, the signs of the first two of them are simply cancelling each other out.



                The function $h()$ is used to compute:



                $$sum_k=1^i-b(k,i)times b(n,k)times b(m,k)\
                =sum_k=1^i(-1)^i-kichoose kkchoose nkchoose m$$



                And the function $g()$ is used to compute each term $a_i$ for $1le i le n+m$.






                share|improve this answer


























                  up vote
                  1
                  down vote













                  JavaScript (ES6), 109 bytes



                  This is really just a port of Delfad0r's great answer, implemented as nested recursive functions.



                  Takes input as (n)(m).





                  n=>m=>(g=i=>i?[...g(i-1),(h=k=>k&&h(k-1)-(b=(y,x=k)=>y?x*b(y-1,x-1)/y:i-k&1||-1)(k,i)*b(n)*b(m))(i)]:)(n+m)


                  Try it online!



                  How?



                  The function $b(y,x)$ computes:



                  $$-(-1)^i-kxchoose y$$



                  It's slightly shorter to return $-(-1)^i-k$ as the last iteration of this function than processing it separately. Because we compute the product of three binomial coefficients, the signs of the first two of them are simply cancelling each other out.



                  The function $h()$ is used to compute:



                  $$sum_k=1^i-b(k,i)times b(n,k)times b(m,k)\
                  =sum_k=1^i(-1)^i-kichoose kkchoose nkchoose m$$



                  And the function $g()$ is used to compute each term $a_i$ for $1le i le n+m$.






                  share|improve this answer
























                    up vote
                    1
                    down vote










                    up vote
                    1
                    down vote









                    JavaScript (ES6), 109 bytes



                    This is really just a port of Delfad0r's great answer, implemented as nested recursive functions.



                    Takes input as (n)(m).





                    n=>m=>(g=i=>i?[...g(i-1),(h=k=>k&&h(k-1)-(b=(y,x=k)=>y?x*b(y-1,x-1)/y:i-k&1||-1)(k,i)*b(n)*b(m))(i)]:)(n+m)


                    Try it online!



                    How?



                    The function $b(y,x)$ computes:



                    $$-(-1)^i-kxchoose y$$



                    It's slightly shorter to return $-(-1)^i-k$ as the last iteration of this function than processing it separately. Because we compute the product of three binomial coefficients, the signs of the first two of them are simply cancelling each other out.



                    The function $h()$ is used to compute:



                    $$sum_k=1^i-b(k,i)times b(n,k)times b(m,k)\
                    =sum_k=1^i(-1)^i-kichoose kkchoose nkchoose m$$



                    And the function $g()$ is used to compute each term $a_i$ for $1le i le n+m$.






                    share|improve this answer














                    JavaScript (ES6), 109 bytes



                    This is really just a port of Delfad0r's great answer, implemented as nested recursive functions.



                    Takes input as (n)(m).





                    n=>m=>(g=i=>i?[...g(i-1),(h=k=>k&&h(k-1)-(b=(y,x=k)=>y?x*b(y-1,x-1)/y:i-k&1||-1)(k,i)*b(n)*b(m))(i)]:)(n+m)


                    Try it online!



                    How?



                    The function $b(y,x)$ computes:



                    $$-(-1)^i-kxchoose y$$



                    It's slightly shorter to return $-(-1)^i-k$ as the last iteration of this function than processing it separately. Because we compute the product of three binomial coefficients, the signs of the first two of them are simply cancelling each other out.



                    The function $h()$ is used to compute:



                    $$sum_k=1^i-b(k,i)times b(n,k)times b(m,k)\
                    =sum_k=1^i(-1)^i-kichoose kkchoose nkchoose m$$



                    And the function $g()$ is used to compute each term $a_i$ for $1le i le n+m$.







                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited 7 hours ago

























                    answered 8 hours ago









                    Arnauld

                    66.3k583280




                    66.3k583280




















                        up vote
                        1
                        down vote














                        Wolfram Language (Mathematica), 58 bytes



                        Inverse[b=Array[Binomial,+##1,1]].(b[[;;,#]]b[[;;,#2]])&


                        Try it online!






                        share|improve this answer
























                          up vote
                          1
                          down vote














                          Wolfram Language (Mathematica), 58 bytes



                          Inverse[b=Array[Binomial,+##1,1]].(b[[;;,#]]b[[;;,#2]])&


                          Try it online!






                          share|improve this answer






















                            up vote
                            1
                            down vote










                            up vote
                            1
                            down vote










                            Wolfram Language (Mathematica), 58 bytes



                            Inverse[b=Array[Binomial,+##1,1]].(b[[;;,#]]b[[;;,#2]])&


                            Try it online!






                            share|improve this answer













                            Wolfram Language (Mathematica), 58 bytes



                            Inverse[b=Array[Binomial,+##1,1]].(b[[;;,#]]b[[;;,#2]])&


                            Try it online!







                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered 26 mins ago









                            alephalpha

                            20.5k32886




                            20.5k32886



























                                 

                                draft saved


                                draft discarded















































                                 


                                draft saved


                                draft discarded














                                StackExchange.ready(
                                function ()
                                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodegolf.stackexchange.com%2fquestions%2f173984%2fmultiply-numerical-polynomials%23new-answer', 'question_page');

                                );

                                Post as a guest













































































                                Comments

                                Popular posts from this blog

                                Long meetings (6-7 hours a day): Being “babysat” by supervisor

                                Is the Concept of Multiple Fantasy Races Scientifically Flawed? [closed]

                                Confectionery