RSS Feed

Syndicate content

Flavio Bertamini - Programmare in Excel 2/3

Submitted by aterzaghi on Tue, 12/05/2009 - 22:38

 

Un primo esempio: Calcolo con i numeri primi

 
Aprendo il file NumeriPrimi.xls ci troviamo di fronte ad prima vera applicazione. I vari fogli ci mostrano un utilizzo molto semplice di Vba per Excel®, che richiede conoscenze matematiche molto elementari: il calcolo dei numeri primi. Diciamo molto brevemente, per chi se lo è scordato, che un numero primo è un numero che può essere sottoposto a divisione intera solo per se stesso o per uno (interessante l’articolo su Wikipedia correlato all’argomento). In genere è un problema che si affronta nella scuola media inferiore.

 

Immaginiamo però di voler sapere “rapidamente” come si scompone in fattori primi un numero come quello in figura nella cella A1 di Excel nell’illustrazione sopra: la cosa potrebbe richiedere un tempo irragionevole a mano o la scrittura di centinaia di celle piene di formule con il semplice Excel.

 

Con Vba per Excel le cose cambiano notevolmente.
In sintesi il nostro programma iniziale dovrà leggere un valore numerico nella cella A1 e ne fornirà la scomposizione in fattori primi nella cella B1.
Come si realizza un simile procedimento? Affrontiamo il problema per gradi: che cosa significa innanzitutto, in termini informatici, che un numero è primo?
Possiamo proporre la realizzazione di una funzione (una Function) che risponda a questa domanda ripercorrendo il classico procedimento imparato alla scuola media.
Infatti il primo passo nel nostro sviluppo è quello di scrivere questa funzione: apriamo dunque l’Ide (Integrated Development Environment) di Vba (con il comando Alt + F11) ed analizziamo il Modulo1 (per aprire cliccare due volte sull’icona della pagina correlata nella finestra “Progetto”).
 
Il Modulo1 contiene delle routines che vengono utilizzate in tutto il foglio di calcolo.
Incominciamo a leggere:
 
Option Eplicit: è un istruzione che serve ad indicare al compilatore che ci si servirà della dichiarazione esplicita delle variabili, è sempre bene usare questa formulazione quando si programma anche se non è strettamente obbligatorio, in tal modo si evitano spesso degli errori grossolani che generano fastidiosi problemi in fase di debug.
 
Option Base 1: questa istruzione indica al compilatore che i nostri array partono con l’indice 1 e non zero come altrimenti accade.
 
Public Primi(5000) As Double : Primi() è un array che contiene cifre a doppia precisione e ci serve per immagazzinare i primi 5000 numeri primi.
 
In seguito analizziamo la function isPrime che rappresenterà il cuore del nostro programma .
 

 

 
Public Function isPrime(ByVal variabile As Double) As Boolean
'-----------------------------------------
' determina se un numero e' primo
'-----------------------------------------
    Dim isPri As Boolean
    Dim Modo As Integer
    Dim i As Integer
   
    isPri = True
    For i = 2 To 5000
        Modo = variabile Mod Primi(i)
        If Modo = 0 Then
            isPri = False
            Exit For
        ElseIf Primi(i) * Primi(i) > variabile Then
            Exit For
        End If
    Next
    isPrime = isPri

End Function
 

 
Cosa dice la nostra routine?
Public significa che la routine è visibile in tutto il nostro progetto; isPrime legge la “variabile” come doppio, ed effettua un confronto con l’array Primi() nel ciclo For.
Il valore restituito è un booleano (Vero/Falso).
Vediamo di seguito come nella memoria del Pc viene rappresentato l’array Primi().
 

L’array Primi() conterrà quindi le cifre 2,3,5….fino a 48.611 cioè i primi 5000 numeri primi: supponiamo per il momento infatti che l’array sia già inizializzato. Come procede la nostra routine? All’interno del ciclo For valuta la variabile “Modo” che rappresenta il resto della divisione intera tra “variabile” e l’elemento dell’array Primi(i), attraverso l’operatore Mod.
La variabile booleana isPri è impostata su Vero in ingresso alla routine.
Se “Modo” =0 significa che il mumero non è primo e quindi la nostra variabile isPri sarà falsa. Si può uscire quindi dal ciclo con l’istruzione: Exit For.
Un altro modo per uscire dal ciclo For si realizza quando il quadrato di Primi(i) è maggiore della variabile in oggetto. In tal caso non e’ più possibile avere divisioni intere per la nostra variabile che sarà certamente un numero primo (isPri e’ già impostato in tal senso).
Uscita dal ciclo For il valore della nostra function viene fornito dalla variabile isPri.
Notate che il ciclo inizia con i=2 e questo significa che i numeri pari non vengono valutati
(Primi(1)=2; si suppone infatti che si sappia che i numeri pari non sono primi e si controlli il caso altrove).
Vediamo così che il “nocciolo” matematico si risolve in appena 14 righe di programma.
Rimane da inizializzare l’array che contiene i nostri 5000 numeri primi.
Questo viene realizzato con una Sub chiamata naturalmente Inizializza() che sfrutta a sua volta la Function isPrime.
In sintesi la Sub inizializza “puntualmente” i primi due elementi dell’array Primi() con le istruzioni:
 
Primi(1) = 2
Primi(2) = 3
 
 Utilizza quindi una variabile temporanea chiamata Start che assume tutti i valori dispari cominciando da 3 e che viene incrementata di 2 ogni volta che viene eseguita l’istruzione:
Start= Start +2.
Notate che il ciclo succesivo For incomincia con i=3 fino a 5000, ma le righe di codice interne vengono eseguite molte più volte perché all’interno del ciclo agisce pure l’istruzione GoTo inizio, che rimanda l’esecuzione all’incremento di Start, e viene letta tutte le volte che il numero analizzato NON è primo senza con questo incrementare la variabile i che controlla il ciclo stesso.
Le poche righe di codice cosa fanno? Trovano i primi 5000 numeri primi.
Le variabile Start viene analizzata dalla function isPrime e se risulta un numero primo viene immagazzinata nell’array Primi() altrimenti viene incrementata di 2.
Il primo valore analizzato, notate bene, è 5 che risulta essere un numero primo.
La variabile booleana pubblica Done viene impostata sul valore “True” alla fine della routine, per indicare al nostro programma che il calcolo dei primi 5000 numeri primi è stato effettuato ed è residente in memoria.
Notate che non c’è contraddizione per il fatto che le due routines così realizzate sembrino mangiarsi la coda l’una con l’altra: infatti la ruotine Inizializza() prima di richiamare la funzione isPrime ha già immagazzinato il numero minimo di elementi (il solo numero 3) che permettono di operare al nostro software.
Non si hanno problemi perché il ciclo For non viene mai completato in questa fase.
Realizzate queste due piccole routines che serviranno in tutto il nostro foglio di calcolo possiamo passare all’utilizzo delle stesse in alcune applicazioni che mostriamo di seguito.
 
Foglio: Tabella.
 
Nel foglio chiamato “Tabella”, vogliamo inizialmente far scrivere ad Excel alcuni numeri primi sul foglio di calcolo per controllare rapidamente la validità delle nostre implementazioni. Mettiamoci in modalità “Progettazione” cliccando sull’apposito pulsante. Creiamo quindi un pulsante impostando la proprietà Name al valore BtnTabella e la Caption al valore Tabella. In fase di progettazione possiamo cliccare due volte sul pulsante realizzato, per creare nello Ide di Visual Basic for Application la Sub BtnTabella_Click() che si scatenerà in fase di esecuzione al cliccare sul pulsante stesso.
Notiamo che la Sub così creata è dichiarata Private cioè non è visibile in tutta l’applicazione.
La Sub è molto semplice: dopo aver verificato che la Sub Inizializza() sia stata eseguita, ripulisce il foglio di calcolo dai dati eventualmente scritti precedentemente, e quindi propone un InputBox che ci chiederà il numero di numeri primi che vogliamo visualizzare sul nostro foglio Excel. All’ ok dato all’Input Box seguirà un ciclo For di scrittura nelle varie celle della prima colonna del foglio “Tabella” con completamento finale di un breve titolo. Si consiglia di non salvare troppi dati in fase di chiusura del file .xls anche perché si possono recuperare con un semplice clic. Può risultare comodo il pulsante “Cancella” che con una routine di una sola riga ripulisce il foglio. Indubbiamente sono delle routines che non producono risultati sorprendenti, ma è un utile esercizio iniziale.
 
Foglio: Scomponi in fattori primi
 
Nel foglio “Scomponi …” si userranno le due rotines del Modulo1 per scomporre un numero in fattori primi.
La routine CmdScomponi_Click() che viene scatenata tutte le volte che si preme il CommandButton nel foglio durante l’esecuzione.
Per visualizzarla basta premere due volte col tasto sinistro del mouse nella finestra “Progetto” nell’ Ide di Vba sull’icona del foglio di calcolo.
Cosa fa questa routine? Scompone un numero in fattori primi.
Per prima cosa si accerta che la Sub Inizializza() sia stata eseguita attraverso la lettura della variabile booleana Done (che appunto per questo è stata dichiarata pubblica) e nel caso procede alla sua esecuzione. Sappiamo quindi a questo punto, che nella memoria del nostro computer ci sono i primi 5000 numeri primi.
La routine procede all’analisi della variabile Str in cui è stato provvisoriamente immesso il valore della cella A1 e si accerta che non sia un numero con virgola perché in tal caso le operazioni indicate in seguito non avrebbero senso. Tale controllo viene eseguito dalla funzione Instr. In caso di numero con virgola manda un messaggio di avviso all’utente tramite l’istruzione MsgBox e procede alla terminazione della routine. Se invece è stato correttamente immesso un numero naturale questo viene immagazzinato nella variabile Start come doppio.  
In seguito procede alla dichiarazione dell’intercettazione degli errori attraverso la dichiarazione: On Error GoTo Errtrap che sposta la gestione di eventuali errori in fondo alla routine.
Il calcolo vero e proprio è costituito dal ciclo For che confronta la variabile Start con i vari numeri primi che abbiamo inizializzato. In Rest è posto il risultato dell’operazione Start Mod Primi(i). Se Rest =0 allora Primi(i) è un divisore di Start: la variabile j, inizializzata a zero, verrà incrementata di 1, l’array Sco() verrà ingrandito di un elemento salvando gli elementi già presenti con l’istruzione ReDim Preserve Sco(j) e conterrà all’interno di Sco(j) un valore pari all’elemento Primi(i), quindi Start verrà diviso per questo numero, la variabile ottenuta sarà chiamata ancora Start e sarà necessario ripartire dalla linea chiamata “inizio” con il nuovo Start ottenuto, per procedere ulteriormente nella fattorizzazione. Nel caso poi che il quadrato di Primi (i) superi il valore di Start, allora Start è un numero primo.
La scomposizione del nostro numero è a questo punto completata nell’array Sco(): con però un piccolo difetto: infatti l’ultima divisione potrebbe avere come quoziente 1. A tale inconveniente provvede l’istruzione: If Sco(j) = 1 Then
              ReDim Preserve Sco(j - 1)
        End If.
Tale istruzione semplicemente elimina l’ultimo fattore di Sco() se questo fattore ha come valore il banale uno.
Non resta che mettere in forma leggibile i vari fattori scrivendo la formula come stringa nella variabile Str che verrà immessa in seguito nella cella B1.
L’istruzione Exit Sub costringe quindi Excel ad uscire dalla procedura mentre le istruzioni succesive vengono eseguite solo in caso di errore e consistono nella segnalazione tramite messaggio critico opportuno e succesivo “Clear” dell’errore stesso.
In tal modo l’utente occasionale non viene coinvolto da Excel nel richiamo standard del debug dell’applicazione.
     Se il numero in questione è primo allora il calcolo porterà ad un array Sco() che contiene un solo elemento: in tal caso Ubound(Sco())=1 e nella cella B1 verrà visualizzata la scritta “Numero Primo”.
            In caso contrario la variabile stringa Str viene inizializzata nella forma (‘=) e ad essa vengono poi aggiunti i vari fattori separati da asterischi(*).
Una riprova della validità del nostro programma stà nel fatto che eliminando manualmente l’apice all’inizio della stringa che compare come output nella cella B1 (operando questo tramite Excel), si ottiene una formula di scomposizione vera e propria che viene letta da Excel e darà naturalmente come risultato il numero di partenza.
Facciamo notare inoltre che sebbene i numeri primi immagazzinati siano “solo” 5000 il nostro programma analizza cifre ben più grandi scomponendo in fattori ad esempio numeri come 21.659.636 = 47 * 460.829 dove il secondo fattore è circa 10 volte più grande di Primi(5000).
In generale possiamo dire che 5000 numeri primi sono in grado di soddisfare la curiosità dei dilettanti della matematica, ma nulla vieta, in linea di principio di impegnare Excel e il Visual Basic su terreni più “tortuosi” aumentando la dimensione dell’array Primi()! La dimensione massima possibile di un array varia tuttavia a seconda del sistema operativo in uso e della memoria disponibile. L'utilizzo di array di dimensioni superiori alla memoria RAM disponibile nel sistema comporterà un rallentamento notevole del funzionamento poiché i dati devono, in tal caso, essere letti e quindi scritti su disco. La dimensione, inoltre, è stata scelta anche per rendere il programma abbastanza veloce.
 
Foglio:Uso dei controlli
 
Nel foglio “Uso dei controlli” si dà al nostro piccolo programma di calcolo per la scomposizione dei fattori primi una versione grafica più accattivante con l’uso dei controlli Label e TextBox.
La scrittura del codice non richiede molto tempo perché è sostanzialmente la stessa del foglio precedente solo che mentre nel primo avevamo come oggetti le celle di Excel ora abbiamo questi due nuovi controlli. Si può quindi ottenerla facilmente copiando la routine precedentemente sviluppata nel foglio ”Scomponi..” e successivamente apportando le opportune correzioni alla routine in meno di 10 minuti. Il foglio però assume un aspetto decisamente più “professionale”…
 
Foglio:Esercizio1 (e Modulo2)
 
Nell’implementazione del codice nei due fogli precendenti si è creata la potenzialità di avere una potente function che scompone in fattori un numero…
Questa function è stata implementata rapidamente e sviluppata a partire dal codice del foglio precedente “Uso dei controlli”, e “sistemata” nel Modulo2 per comodità ed è stata chiamata ScomponiinFattoriPrimi. Invece un Variant ed emette una stringa attraverso le varie uscite…
 
Questa function è però sufficientemente potente da permettere di scrivere una ruotine che calcola un “Esercizio” di scomposizione in fattori primi in appena 5 righe di codice………
 

Post new comment

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <img> <style> <center> <p>
  • Lines and paragraphs break automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Copy the characters (respecting upper/lower case) from the image.

Custom Search