Giochiamo con Gauss
Questo programma implementa la risoluzione di un sistema di
m equazioni in n incognite, con il metodo
della riduzione a scala di Gauss-Jordan. Sono proposte due
versioni: nella prima è richiesto l'intervento
diretto dell'utente per le varie operazioni e per la
risoluzione effettiva del sistema, nella seconda la risoluzione
è completamente automatica. La seconda versione è
semplicemente un adattamento della prima e non prevede l'uso
di un metodo ricorsivo che sarebbe il più naturale per
questo tipo di problema. Come gli altri programmi proposti in
questa sezione del nostro sito, lo scopo è
prevalentemente didattico e non si è cercata
l'ottimizzazione del codice.
Dal punto di vista matematico non ci sono particolari
osservazioni da fare e quelle più importanti si possono
leggere nella pagina di Help collegata alla prima versione del
programma.
Il codice Javascript è stato sviluppato secondo lo schema
seguente:
-
Viene innanzitutto richiesto il numero di equazioni e di
incognite del sistema. Entrambi i numeri devono essere non
superiori a 10, per ridurre al minimo eventuali problemi di
overflow, prevedibili in quanto l'algoritmo è
scritto per utilizzare frazioni di interi e non decimali in
virgola mobile. Questi problemi si presentano comunque per
sistemi con molte incognite e/o equazioni e, in ogni caso,
occorre verificare l'attendibilità dei risultati
ottenuti. Una apposita funzione (solonum())
controlla che vengano inseriti solo numeri e non accetta
altri caratteri da tastiera.
-
Successivamente viene generata dinamicamente una maschera per
l'input dei dati, con delle caselle di input per ognuno
dei coefficienti e termini noti. E' anche prevista
un'introduzione casuale di questi valori, nel qual caso,
sempre per i citati problemi di overflow, i coefficienti sono
limitati agli interi compresi tra -10 e +10. Le caselle di
input (del tipo text) sono referenziate utilizzando
l'array form.elements[] (in questo caso il nome
del form è matrice e quindi
matrice.elements[]), in cui l'indice è
l'indice progressivo di numerazione degli elementi man
mano che vengono generati.
-
A questo punto l'utente può introdurre i
coefficienti, nella forma numeratore/denominatore,
ove numeratore e denominatore sono interi. Una funzione
(solofraz()) controlla che vengano inseriti solo
caratteri adatti (numeri, eventuale segno meno, eventuale
segno di frazione).
-
Introdotti i coefficienti tutte le caselle di input vengono
controllate (funzione controlla()) per verificare
l'accettabilità del dato, e, se del caso, i dati
vengono vengono memorizzati in una tabella (array a due
indici), i cui elementi sono a loro volta array con due
componenti (il numeratore e il denominatore). Poiché
Javascript non prevede esplicitamente array bidimensionali,
si è fatto ricorso ad un array di array, secondo la
tecnica standard. Per questioni di semplicità
l'eventuale segno negativo del numero può
comparire solo al numeratore. Si sono introdotti anche altri
due array, uno per memorizzare i dati iniziali, nel caso
l'utente voglia tornare al punto di partenza, uno per
memorizzare i dato prima di ogni variazione, nel caso
l'utente voglia tornare indietro di un passo. Dopo
l'accettazione dei dati le caselle di input vengono
disabilitate alla lettura per evitare errate manipolazioni da
parte dell'utente. E' comunque possibile cambiare i
dati iniziali: la funzione cambiadato() fa ritornare
al punto di partenza riabilitando le caselle di input.
-
Terminata questa fase l'utente può iniziare ad
operare per ridurre il sistema in forma a scala, seguendo
esattamente il procedimento che si farebbe manualmente.
Quando l'utente ha terminato di operare su una riga si
può scendere di livello: il codice controlla che sotto
il candidato pivot di una riga ci siano solo zeri.
Questa operazione è gestita da un'apposita
funzione: scendi().
-
Le operazioni di riscrittura delle caselle di input con i
valori modificati, operazione che evidenzia le caselle con
zeri e le caselle con i pivot, sono gestite dalla funzione
riscrivi().
-
E' sempre possibile semplificare i calcoli se i
coefficienti di una riga hanno divisori comuni o se si vuole
avere pivot unitari, operazione utile al momento della
risoluzione del sistema.
-
Terminate tutte le operazioni il codice può, su
richiesta dell'utente, risolvere il sistema (con la
funzione risolvi()) con la stessa tecnica che si usa
manualmente, cioè cominciando dall'ultima riga e
procedendo all'indietro: le incognite i cui
coefficienti non sono pivot rimarranno indeterminate e
compariranno come parametri arbitrari. Per consentire
l'esecuzione di calcoli simbolici (le incognite
"già trovate" e da sostituire nelle
righe superiori non sono numeri, ma espressioni lineari nei
parametri), la soluzione del sistema è costruita
mediante un array di n elementi (tanti quante le
incognite) e ciascun elemento dell'array è, a sua
volta, un array contenente al posto zero il termine noto e
negli altri posti i coefficienti dei parametri via via
introdotti. Naturalmente gli elementi di quest'ultimo
array saranno a loro volta array con due componenti, una per
il numeratore e una per il denominatore.
-
Un'ultima difficoltà da superare: la scrittura
dell'output, per evitare di scrivere frazioni con
denominatore uno, o di far comparire parametri con
coefficiente nullo. Questo è gestito dalla funzione
compatta().
-
Numerose altre funzioni svolgono compiti particolari, per
esempio la riduzione delle frazioni ai minimi termini,
l'operazione di inserimento casuale dei coefficienti, la
somma di array di frazioni o il prodotto di una frazione per
un array di frazioni, il ritorno indietro di un passo, il
ritorno al punto di partenza, ecc.
Come si vede si tratta di un codice abbastanza complesso,
soprattutto per la necessità di controllare le numerose
operazioni eseguite dall'utente. Il codice per la soluzione
automatica è notevolmente più semplice e potrebbe
essere ulteriormente semplificato, come già detto, con
una implementazione ricorsiva. In ogni caso le 720 righe da cui
è costituito questo script potrebbero essere ridotte
eliminando alcune ripetizioni e compattando alcune funzioni.
Potrebbe essere un utile esercizio per lo studente volenteroso!
Commenti:
-
event.keyCode proprietà dell'oggetto
event che fornisce la codifica Unicode del tasto
premuto.
-
event.returnValue altra proprietà
dell'oggetto event che specifica un valore da
restituire al gestore di eventi (nel nostro caso il gestore
dei tasti premuti sulla tastiera): se viene restituito
false è come se l'utente non avesse
premuto alcun tasto.
-
HTMLElement. innerHTML e
HTMLElement.outerHTML consentono di modificare
rispettivamente tutto l'interno di un qualunque elemento
HTML presente nella pagina (identificato da un opportuno
id) o sia l'interno che l'elemento stesso.
-
Abbiamo fatto uso anche di uno speciale operatore
ternario di Javascript: l'operatore
?:. La sintassi di questo operatore è
la seguente: (operando 1) ? (operando 2) : (operando
3). Il primo operando deve contenere un valore booleano,
il secondo e il terzo un valore qualsiasi. Se l'operando
booleano restituisce true viene eseguito il codice contenuto
nel secondo operando, se restituisce false il codice
contenuto nel terzo operando. In sostanza si tratta di
un'utile scorciatoia al posto di un if con
eventuale else.
-
Math.abs() restituisce il valor assoluto di un
numero.
-
Per referenziare gli elementi di un array di array si usano
due (o più) coppie di parentesi quadre:
nomearray[][].
copyright 2000 et seq. maddalena falanga & luciano battaia
pagina pubblicata il 01/12/2001 - ultimo aggiornamento il
01/09/2003