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