Convolução discreta ou multiplicação polinomial

19

Dadas duas listas de números inteiros não vazias , seu envio deve calcular e retornar a convolução discreta dos dois. Curiosamente, se você considerar os elementos da lista como coeficientes de polinômios, a convolução das duas listas representa os coeficientes do produto dos dois polinômios.

Definição

Dadas as listas A=[a(0),a(1),a(2),...,a(n)]e B=[b(0),b(1),b(2),...,b(m)](definição a(k)=0 for k<0 and k>ne b(k)=0 for k<0 and k>m), a convolução das duas é definida como A*B=[c(0),c(1),...,c(m+n)]ondec(k) = sum [ a(x)*b(y) for all integers x y such that x+y=k]

Regras

  • Qualquer formatação conveniente de entrada e saída para o seu idioma é permitida.
  • Não são permitidos embutidos para convolução, criação de matrizes de convolução, correlação e multiplicação polinomial.

Exemplos

[1,1]*[1] = [1,1]
[1,1]*[1,1] = [1,2,1]
[1,1]*[1,2,1] = [1,3,3,1]
[1,1]*[1,3,3,1] = [1,4,6,4,1]
[1,1]*[1,4,6,4,1] = [1,5,10,10,5,1]

[1,-1]*[1,1,1,1,1] = [1,0,0,0,0,-1]
[80085,1337]*[-24319,406] = [-1947587115,7,542822]
flawr
fonte
3
A especificação implica que entradas de comprimento n, m devem produzir uma saída de comprimento n + m - 1, mas isso não vale para o seu caso de teste [1,1]*[] = []e não pode ser válido para []*[] = ?. A convolução não está bem definida em listas vazias. Eu acho que você deve garantir que as listas de entrada não sejam vazias.
Anders Kaseorg 16/05
11
@AndersKaseorg Você está certo, eu vou mudar isso.
Flawr

Respostas:

14

J, 10 8 bytes

[:+//.*/

Uso:

ppc =: [:+//.*/    NB. polynomial product coefficients 
80085 1337 ppc _24319 406
_1947587115 7 542822

Descrição: o programa pega duas listas, faz uma tabela de multiplicação e depois adiciona os números nas diagonais positivas.

ljeabmreosn
fonte
Abordagem muito inteligente!
Luis Mendo 16/05
Você não precisa contar os parênteses. A expressão dentro deles é avaliada como um verbo tácito, que pode ser atribuído a uma variável.
Dennis
Ótimo exemplo de advérbios!
milhas
6

MATL , 19 bytes

PiYdt"TF2&YStpsw]xx

Experimente online!

Explicação

Isso cria uma matriz diagonal de bloco com as duas entradas, revertendo a primeira. Por exemplo, com entradas [1 4 3 5], [1 3 2]a matriz é

[ 5 3 4 1 0 0 0
  0 0 0 0 1 3 2 ]

Cada entrada da convolução é obtida deslocando a primeira linha uma posição para a direita, computando o produto de cada coluna e somando todos os resultados.

Em princípio, a mudança deve ser feita preenchendo com zeros a partir da esquerda. Equivalentemente, o deslocamento circular pode ser usado, porque a matriz contém zeros nas entradas apropriadas.

Por exemplo, o primeiro resultado é obtido da matriz deslocada

[ 0 5 3 4 1 0 0
  0 0 0 0 1 3 2 ]

e é assim 1*1 == 1. O segundo é obtido de

[ 0 0 5 3 4 1 0
  0 0 0 0 1 3 2 ]

e é assim 4*1+1*3 == 7, etc. Isso deve ser feito m+n-1vezes, onde me nsão os comprimentos de entrada. O código usa um loop com m+niterações (que economiza alguns bytes) e descarta o último resultado.

P          % Take first input (numeric vactor) implicitly and reverse it
i          % Take second input (numeric vactor) 
Yd         % Build diagonal matrix with the two vectors
t          % Duplicate
"          % For each column of the matrix
  TF2&YS   %   Circularly shift first row 1 step to the right
  t        %   Duplicate
  p        %   Product of each column
  s        %   Sum all those products
  w        %   Swap top two elements in stack. The shifted matrix is left on top
]          % End for
xx         % Delete matrix and last result. Implicitly display
Luis Mendo
fonte
4

Haskell, 55 49 bytes

(a:b)#c=zipWith(+)(0:b#c)$map(a*)c++[]#b
_#c=0<$c

Define um operador #.

Anders Kaseorg
fonte
11
Eu acho que o preenchimento [0,0..]pode ser (0<$b)para fornecer exatamente o comprimento necessário, permitindo o estojo de base vazio _#b=0<$b.
Xnor 17/05
@ xnor De fato, isso economiza 6 bytes.
Anders Kaseorg 17/05
Agora que finalmente entendi sua resposta, devo dizer que isso é muito inteligente! Estou impressionado!
flawr
3

Matlab / Octave, 41 bytes

@(p,q)poly([roots(p);roots(q)])*p(1)*q(1)

Isso define uma função anônima. Para chamá-lo, atribua a uma variável ou use ans.

Experimente aqui .

Explicação

Isso explora os fatos que

  • As raízes (possivelmente repetidas) caracterizam um polinômio até seu coeficiente principal.
  • O produto de dois polinômios tem as raízes de ambos.

O código calcula as raízes dos dois polinômios (função roots) e concatena-os em uma matriz de colunas. A partir disso, obtém os coeficientes do polinômio do produto com uma liderança 1(função poly). Finalmente, o resultado é multiplicado pelos coeficientes principais dos dois polinômios.

Luis Mendo
fonte
3

Oitava , 48 bytes

@(p,q)ifft(fft([p q*0]).*fft([q p*0]))(1:end-1)

Experimente aqui .

Explicação

Convolução discreta corresponde à multiplicação das transformadas de Fourier (em tempo discreto). Portanto, uma maneira de multiplicar os polinômios seria transformá-los, multiplicar as seqüências transformadas e transformar de volta.

Se a transformada de Fourier discreta (DFT) for usada em vez da transformada de Fourier, o resultado será a convolução circular das seqüências originais dos coeficientes polinomiais. O código segue esta rota. Para tornar a convolução circular igual à convolução padrão, as seqüências são preenchidas com zero e o resultado é aparado.

Luis Mendo
fonte
Droga, eu ainda deveria ter proibido o FFT, mas bom trabalho!
flawr
@ flawr Sim, acho que conversamos sobre isso ...? :-P
Luis Mendo
2

05AB1E , 18 17 bytes

Código

0Ev²¹g<Å0«y*NFÁ}+

Explicação

A teoria por trás:

Para encontrar a convolução, vamos dar o exemplo [1, 2, 3], [3, 4, 5]. Posicionamos os valores da primeira matriz de cabeça para baixo e verticalmente, assim:

3
2
1

Agora, colocamos a segunda matriz como uma escada e a multiplicamos por:

3 ×       [3  4  5]
2 ×    [3  4  5]
1 × [3  4  5]

Resultando em:

        9   12   15
    6   8   10
3   4   5

Em seguida, adicionamos-os, resultando em:

        9   12   15
    6   8   10
3   4   5       

3   10  22  22   15

Então, a convolução é [3, 10, 22, 22, 15].

O código em si:

Vamos fazer isso passo a passo usando o [1, 2, 3], [3, 4, 5]como o caso de teste.

0Ev²¹g<Å0«y*NFÁ}+

Primeiro pressionamos 0e depois avaliamos Ea primeira matriz de entrada. Mapeamos cada elemento usando v.

Portanto, para cada elemento, pressionamos a segunda matriz com ²e, em seguida, o comprimento da primeira matriz, usando-a ¹ge diminuindo-a em 1 (com <). Convertemos isso em uma lista de zeros com (comprimento 1ª matriz - 1) zeros, usando Å0e anexando isso à nossa lista. Nossa pilha agora se parece com isso no primeiro item da lista de entrada:

[3, 4, 5, 0, 0]

Multiplicamos essa matriz pelo item atual, feito com y*. Depois disso, pressionamos N, que indica o índice do item atual (indexado a zero) e rotacionamos o array muitas vezes para a direita usando FÁ}. Finalmente, adicionamos isso ao nosso valor inicial ( 0). Então, o que basicamente é feito é o seguinte:

[0, 0, 9, 12, 15] +
[0, 6, 8, 10, 0] +
[3, 4, 5, 0, 0] =

[3, 10, 22, 22, 15]

Que é impresso implicitamente. Usa a codificação CP-1252 . Experimente online! .

Adnan
fonte
2

Geléia , 9 bytes

0;+
×'Ṛç/

Experimente online! ou verifique todos os casos de teste .

Como funciona

×'Ṛç/  Main link. Arguments: p, q (lists)

×'     Spawned multiplication; multiply each item of p with each item of q.
  Ṛ    Reverse the rows of the result.
   ç/  Reduce the rows by the helper link.


0;+    Helper link. Arguments: p, q (lists)

0;     Prepend a 0 to p.
  +    Perform vectorized addition of the result and q.
Dennis
fonte
O que? Gelatina por mais tempo que J? Isso é impossível por definição!
Adám 17/05/19
2

Casca , 5 bytes

mΣ∂Ṫ*

Experimente online!

Nota: Ao fornecer a lista polinomial zero / vazia, você precisa especificar seu tipo (ou seja, []:LN)!

Explicação

mΣ∂Ṫ*  -- implicit inputs xs ys, for example: [1,-1] [1,1]
   Ṫ*  -- compute the outer product xsᵀ·ys: [[1,1],[-1,-1]]
  ∂    -- diagonals: [[1],[1,-1],[-1]]
mΣ     -- map sum: [1,0,1]
ბიმო
fonte
2

Matlab, 33 bytes

@(x,y)sum(spdiags(flip(x').*y),1)

Experimente online!

Cria uma matriz de todos os produtos em elementos dos insumos e depois soma ao longo das diagonais. O ,1no final força o matlab a somar na direção correta quando um dos vetores de entrada tiver o comprimento 1.

O Octave spdiagsnão funciona para vetores, resultando em um erro quando uma das entradas tiver o comprimento 1. O Matlab 2016b ou mais recente é necessário para a expansão explícita do produto em termos de elementos.

LukeS
fonte
Boa abordagem !!
Luis Mendo
1

Python, 90 bytes

lambda p,q:[sum((p+k*[0])[i]*(q+k*[0])[k-i]for i in range(k+1))for k in range(len(p+q)-1)]
orlp
fonte
1

JavaScript (ES6), 64 bytes

(a,b)=>a.map((n,i)=>b.map((m,j)=>r[j+=i]=m*n+(r[j]||0)),r=[])&&r

Retorna a matriz vazia se uma das entradas estiver vazia. Com base na minha resposta ao Polynomialception .

Neil
fonte
1

Clojure, 104 bytes

#(vals(apply merge-with +(sorted-map)(for[i(range(count %))j(range(count %2))]{(+ i j)(*(% i)(%2 j))})))

Mesclando para sorted-mapgarantir que os valores sejam retornados na ordem correta. Eu gostaria que houvesse mais alguns casos de teste.

NikoNyrh
fonte