Moltiplicazione e divisione delle matrici

5

Mi sono imbattuto in una domanda che (alla fine) mi ha portato a chiedermi dell'aritmetica dell'array. Sto pensando specificamente a Ruby, ma penso che i concetti siano indipendenti dal linguaggio.

Quindi, addizione e sottrazione sono definite, in Ruby, come tali:

[1,6,8,3,6] + [5,6,7] == [1,6,8,3,6,5,6,7] # All the elements of the first, then all the elements of the second
[1,6,8,3,6] - [5,6,7] == [1,8,3] # From the first, remove anything found in the second

e array * scalar è definito:

[1,2,3] * 2 == [1,2,3,1,2,3]

Ma

Che cosa, concettualmente, dovrebbe essere il seguente? Nessuno di questi è (per quanto posso trovare) definito:

  • Array x Array: [1,2,3] * [1,2,3] #=> ?
  • Array / Scalar: [1,2,3,4,5] / 2 #=> ?
  • Array / Scalar: [1,2,3,4,5] % 2 #=> ?
  • Array / Array: [1,2,3,4,5] / [1,2] #=> ?
  • Array / Array: [1,2,3,4,5] % [1,2] #=> ?

Ho trovato alcune descrizioni matematiche di queste operazioni per la teoria degli insiemi, ma non sono riuscito a seguirle e gli insiemi non hanno duplicati (gli array fanno).

Modifica: Nota, faccio non aritmetica media vettoriale (matrice), che è completamente definita.

Edit2: Se questo è lo scambio di stack sbagliato, dimmi qual è quello giusto e lo sposterò.

Modifica 3: aggiungi operatori mod all'elenco.

Modifica 4:

I figura array / scalar è derivabile da array * scalar :

a * b = c 
=> a = b / c

[1,2,3] * 3 = [1,2,3]+[1,2,3]+[1,2,3] = [1,2,3,1,2,3,1,2,3]
=> [1,2,3] = [1,2,3,1,2,3,1,2,3] / 3

Che, dato che la divisione del programmatore ignora il modulo rimasto e ha modulo:

[1,2,3,4,5] / 2 = [[1,2], [3,4]]
[1,2,3,4,5] % 2 = [5]

Tranne che queste operazioni sono chiaramente non reversibili (non quel modulo è mai), che non è ideale.

Modifica: ho posto una domanda su Math che mi ha portato a Multisets . Penso che forse gli array estensibili siano "multiset", ma non ne sono ancora sicuro.

    
posta Narfanator 19.06.2013 - 19:59
fonte

2 risposte

7

Il modello di Ruby è fornito più per comodità che per correttezza, ed è incoerente:

  • array + array è la matrice concatenazione, consentendo duplicati, ma array - array è la differenza imposta , rimuovendo i duplicati: [1, 1] - [1] è [] , non [1] .

  • - non è l'inverso di + , perché non è il caso che a + b - c == a per tutte Array istanze a , b e c : accetta [1] + [1] - [1] .

  • array * fixnum è definito come concatenazione dell'array iterato, ma fixnum * array non è definito affatto.

Per operazioni puramente basate su array, mi aspetto che + e - siano inverse:

[1, 2] + [3, 1] == [1, 2, 3, 1]
[1, 2, 3, 1] - [3, 1] == [1, 2]

- rimuoverà gli elementi dalla coda proprio come + li ha aggiunti. Analogamente per * e / :

[1, 2] * 3 == [1, 2, 1, 2, 1, 2]
[1, 2, 1, 2, 1, 2] / 3 == [1, 2]
[5, 1, 2, 1, 2] / 2 == [1, 2]

/ eliminerebbe prima gli elementi da sinistra fino a a.size % b == 0 . Perché da sinistra? Bene, mi aspetto che un operatore a modulo di array soddisfi la legge:

a % b == a - (b * (a / b))

E questa regola sembra funzionare se si esaminano alcuni esempi:

  • [1, 1] % 2 == [1, 1] - (2 * ([1, 1] / 2)) == []
  • [5, 1, 1] % 2 == [5, 1, 1] - (2 * ([5, 1, 1] / 2)) == [5]

Fondamentalmente definisce la divisione come sottrazione iterata.

Ci sono un paio di interpretazioni coerenti e ragionevolmente intuitive di array ♦ array :

  • Prodotto cartesiano: [1, 2] ♦ [3, 4] == [1 ♦ 3, 1 ♦ 4, 2 ♦ 3, 2 ♦ 4]

  • Prodotto a coppie: [1, 2] ♦ [3, 4] == [1 ♦ 3, 2 ♦ 4]

Con un prodotto cartesiano, la dimensione del risultato è il prodotto della dimensione degli input. Ecco come funzionano le hash list e le monad lista in Haskell:

[x ♦ y | x <- [1, 2], y <- [3, 4]]

do
  x <- [1, 2]
  y <- [3, 4]
  return (x ♦ y)

Anche un prodotto a coppie ha senso, in quanto ([x1, y1, z1] * [x2, y2, z2]).reduce(:+) sarebbe il prodotto punto dei vettori [x1, y1, z1] e [x2, y2, z2] . Ovviamente, è necessario definire il risultato quando gli input hanno lunghezze diverse; in Haskell, la funzione zipWith prende il più breve dei due elenchi di input:

   zipWith (♦) [1, 2] [3, 4, 5]
== zipWith (♦) [1, 2] [3, 4]

Quindi la risposta è che ci sono diverse possibili interpretazioni, la cui scelta spetta ai progettisti di linguaggi e librerie. Fintanto che sono coerenti, nessuno di loro è strettamente più "giusto" o "intuitivo" di qualsiasi altro. La convenzione stabilita in lingue degli array è per array * array per fare riferimento al prodotto pairwise, perché questo si generalizza bene con le dimensioni superiori di array e dalla promozione di scalari a matrici di dimensioni appropriate.

    
risposta data 20.06.2013 - 03:07
fonte
2

A mio parere, non vi è alcun motivo per cui gli operatori aritmetici debbano applicare agli array. Il tentativo di forzare gli array ad avere una semantica significativa con operatori aritmetici è fonte di confusione e la confusione è la fonte di bug. Anche la semantica Ruby che chiami non è così ovvia come potresti pensare.

Ad esempio, si noti il comportamento della moltiplicazione per uno scalare. Potrebbe essere ragionevole presumere che la moltiplicazione per un intero sarà equivalente a un'aggiunta ripetuta, ma non è questo il caso:

[1, 2, 3] * 2 == [2, 4, 6]
[1, 2, 3] + [1, 2, 3] == [1, 2, 3, 1, 2, 3]

(Non l'ho verificato, ma sto andando su quello che hai postato.) Come tale, si può sostenere che la moltiplicazione per uno scalare si comporta in modo non intuitivo. (D'altra parte, si comporta esattamente come una moltiplicazione vettoriale per uno scalare, quindi in questo senso è intuitivo. Tuttavia, si noti che la moltiplicazione vettoriale per uno scalare è non un'operazione aritmetica.)

Quale dovrebbe essere il comportamento di questi altri operatori? A mio parere, è stato un errore fornire operator+ ecc. Per gli array, proprio perché questi operatori non possono comportarsi in modo simile ai soliti operatori aritmetici: sarebbe stato meglio espandere l'insieme di operatori disponibili (per definire operatori unici che ha senso con gli array - per prendere a prestito un esempio, Haskell usa ++ per la concatenazione dell'array), OPPURE per usare le funzioni non-operator per implementare queste semantiche (per esempio, [1, 2, 3].append [4, 5, 6] può comportarsi in modo simile a [1, 2, 3] + [4, 5, 6] ).

In ogni caso, non sovraccaricherei i significati dei simboli dell'operatore per tipi non correlati come questo.

    
risposta data 20.06.2013 - 00:56
fonte

Leggi altre domande sui tag