frattaleDopo aver visto come funziona la ricorsione in Scheme dobbiamo valutare un altro aspetto molto rilevante: il costo. Non tutto ciò che logicamente funziona funziona poi all’atto pratico. Ad esempio, se ripescate la funzione ricorsiva della Funzione di Fibonacci e provate a farla partire noterete che già con numeri relativamente bassi (come 100) il sistema inizierà a intasarsi e non darà alcun risultato se non quello di dover killare il processo.

Perché questo? Perché l’implementazione dell’algoritmo, sebbene sia vera e matematicamente corretta, necessita un numero troppo elevato di calcoli o di memoria.

Iniziamo col dare dei cenni sulla complessità di un algoritmo. La complessità di un algoritmo è definita asintoticamente con un infinito di ordine superiore a una funzione f(x). In questo caso si dice che l’algoritmo ha complessità dell’ordine di f(x).

Cosa significa? E’ semplice. Supponete di far valere 1 il costo di ogni operazione base come somma, prodotto, cdr, car e via dicendo (ovviamente è un approssimazione). Supponete di stabilire un contatore che sommi il costo di ogni operazione eseguita. Supponete poi di eseguire questa funzione (con relativo contatore) per un imput di dimensione 1, poi per un input di dimensione 2, poi per un input di dimensione 3 e così via. La serie di numeri che otterrete non sarà altro che i valori di una funzione f(x)al variare di x. La complessità si riduce quindi all’ordine di infinito di questa funzione.

Data l’asintoticità della definizione, solitamente si trascurano i valori per x piccolo e si approssima molto per x che tende a infinito.

Senza addentrarci nei particolari sappiate che in ogni algoritmo esiste un operazione, detta operazione dominante, la quale complessità è uguale alla complessità dell’algoritmo nella sua interezza.

Negli algoritmi ricorsivi l’operazione dominante è la ricorsione. Per trovare la complessità di una funzione in scheme (che sono per definizione tutte ricorsive) ci basta quindi sapere quante volte una funzione chiama la ricorsione.

Questo può essere molto complicato, ma è giusto che impariate a identificare almento i casi semplici. Questo può essere fatto molto semplicemente.

Supponiamo di voler calcolare la complessità dell’algoritmo fattoriale visto qualche lezione fa. L’espressione ricorsiva del fattoriale è la seguente:

FattorialePer trovare il costo basta seguire la formula:

CodeCogsEqn(2)Questo perché il costo di f(0) è chiaramente unitario. Il costo di f(n), per il principio dell’operazione è solamente il costo della ricorsione e il costo della ricorsione non è altro che 1 più il costo di tutte le altre funzioni invocate.

Ma siamo arrivati ad una suluzione? Ancora no. Dobbiamo esplicitare il costo ovvero toglierlo dalla sua forma ricorsiva. Per fare ciò possiamo semplicemente espandere la seconda equazione.

  • C[f(n)]
  • 1 + C[f(n-1)]
  • 2 + C[f(n-2)
  • ...
  • n + C[f(0)]
  • n+1
  • che è asintoticamente O(n)

Il costo è quindi lineare. (Non esattamente… ma per non mettere troppa carne al fuoco farò alcune precisazioni al riguardio nella prossima appendice).

Una funzione con complessità lineare è solitamente una funzione discretamente veloce e potente.

Vediamo ora la stessa cosa con la Funzione di Fibonacci.

Costo Fibonacci

La situazione è leggermente più complessa. Per esplicitare la funzione possiamo comunque espandere C[f(n)] ma non conviene in questo caso. Facciamo un ragionamento più sottile. Cerchiamo di calcolare direttamente il costo totale. Sappiamo che ogni ricorsione chiama due ricorsioni. Alla prima ricorsione avremo costo totale 1, alla seconda avremo il costo totale di prima più il costo delle due nuove ricorsioni ovvero 1 + 2. Alla terza avremo il costo di prima  più le due ricorsioni nuove lanciate da ognuna delle due ricorsione precedenti ovvero 1 + 2 + 4 (il costo di prima più le due ricorsioni nuove lanciate da ognuna delle due ricorsione precedenti).

Notiamo che abbiamo una serie geometrica che possiamo riassumere in 2^n+1 – 1 che asintoticamente è O(2^n).

Il costo è esponenziale. Un algoritmo esponenziale può solitamente calcolare solo input molto piccoli. Ecco perché il nostro algoritmo di Fibonacci si bloccava anche con numeri piccoli come 100.

Come possiamo allora migliorare questo algoritmo? Esistono tecniche un po’ più elaborate che permettono di eseguire algoritmi simili lanciando solo una ricorsione al posto delle due “canoniche”. Queste tecniche le esploreremo più in là in quanto necessitano di un po’ di esperienza.

Per il momento vi basti intuire che algoritmi che eseguono troppe ricorsioni contemporaneamente sono spesso troppo costosi per essere utili. Infatti abbiamo visto che basta lanciare due ricorsioni in vece di una per fare esplodere il costo da lineare ad esponenziale.

Non vi serve saper calcolare la complessità di tutto ma vi basta intuire quando un algoritmo sta diventando troppo costoso. Questo è basilare per ogni programmatore, anche al di là di Scheme.

Provate a calcolare il costo di altre funzioni di esempio che ho dato in precedenza. Postate il costo che avete ottenuto nei commenti e vedrò di verificare se avete indovinato.

 

Boinc LogoBOINC (Berkeley Open Infrastructure for Network Computing) è uno dei più solidi e affermati sistemi di grid-computing. Con il termine grid-computing si è soliti indicare quell’architettura di calcolo nel quale un problema viene scomposto in sotto-unità chiamate e work-unit che vengono analizzati da computer diversi appartenenti ad una rete (sia locale che globale).

Boinc è utilizzato da molti gruppi di ricerca e associazioni per condurre ricerche che necessitano di un elevato tempo di calcolo. La partecipazioni a tali ricerche è totalmente volontaria e aperta a chiunque. E’ inoltre un ottimo modo per non lasciare andare sprecata la potenza di calcolo nei momenti in cui lasciamo inutilizzato il pc. Questo aspetto. insieme alla partecipazione volontaria, sono così importanti in Boinc che spesso sono stati definiti come screensaver-computing o volounteers-computing.

Il programma esiste per ogni sistema, sia Windows che Linux, ed è composto da due componenti:

  • boinc-client : che contiene il client vero e proprio
  • boinc-manager: che contiene l’interfaccia per interagire e per configurare il client

I progetti a cui partecipiamo, inoltre, installano uno screensaver che mostra alcuni dati sulla computazione in corso, ma solo su windows. Questa feature è sfortunatamente  ancora assente da GNU/Linux e non so per quanto ancora lo sarà.

Per installare Boinc dobbiamo installare le due componenti:

# apt-get install boinc-manager boinc-client

A questo punto il client è già pronto, non resta che aprire il Boinc Manager (che trovate nel menù delle applicazioni) e aggiungere il progetto che volete.

Io ad esempio sono molto attivo su Rosetta@home che produce risultati sulla struttura tridimensionale delle proteine.

C’è però un bug almeno nella mia installazione: la funzionalità che mette in pausa automaticamente la computazione quando l’utente è attivo non mi funziona bene. Devo quindi attivare e sospendere manualmente. Non è un granché ma il bug è noto e segnalato. Speriamo in una rapida correzione.

 

KoalaSono tornato dopo qualche giorno di assenza forzata dato che qualcuno si è divertito a tagliarmi il filo del telefono. Bah… Torno giusto in tempo per la novella uscita di Ubuntu: la versione Karmic Koala.

Dato che non sopporto spandermi in elogi banali e post altrettanto scontati non pubblico una guida mia ma posto i link alle ottime guide prodotte dai membri di LQH.

  • Installare Karmic in Dual Boot. Questa è la guida più cercata e utile ai nuovi arrivati che provengono da windows.
    VAI ALLA GUIDA
  • Installare Karmic con Root e Home separate. Molto comoda per l’utente medio. Grazie a questa configurazione si può reinstallare qualunque distribuzione senza perdere dati e configurazioni.
    VAI ALLA GUIDA
  • Installare Karmic con il cd Alternate. Installazione per utenti più esperti ma adattissima per quei PC un po datati.
    VAI ALLA GUIDA

Buona lettura e, se siete nuovi utenti, benvenuti. :)

 

LaunchpadAbbiamo visto in qualche articolo precedente come utilizare Bazaar, il CVS di Launchpad, per gestire il nostro codice e upparlo sul server remoto. All’epoca però avevo volutamente tralasciato due punti per questioni di spazio: come creare un progetto su Launchpad e, soprattutto, come gestirlo fra più utenti. Due cose che, per chi usa Bazaar per piccoli progetti personali, non hanno molta importanza ma diventano indispensabili per chi vuole sviluppare un progetto in collaborazione con più persone.

La prima cosa da fare è sicuramente registrarsi su Launchpad. La registrazione è molto semplice e non penso necessiti di ulteriori spiegazioni.

Una volta che abbiamo il nostro utente è necessario configurare una chiave RSA per il collegamento sicuro SSH. Per fare questo dobbiamo aprire il terminale e digitare:

$ ssh-keygen

E seguire le istruzioni. Una volta completato avremo il file id_rsa.pub con la nostra chiave pubblica nella cartella .ssh all’interno della nostra Home. Fatto questo andiamo sul nostro profilo in Launchpad, individuiamo la voce SSH Keys e clicchiamo sul bollino giallo a fianco. Noterete una casella di testo piuttosto grande: copiate lì il contenuto del file id_rsa.pub e data infine Import Public Key.

Ora siete pronti per creare il progetto. Anche qui la procedura è semplice: dalla Home Page di Launchpad, sotto Get Started, troverete il link per Create new project. Cliccateci sopra ed inserite le informazioniche vichiede, sono tutte molto ovvie, nome, descrizione del progetto, url e via dicendo.

Avrete a disposizione ora la nuova pagina per il vostro progetto.

La prima cosa da fare è creare una series. Una serie non è altro che una “corrente” di sviluppo. Progetti complessi hanno solitamente più series: la trunk che contiene gli ultimi sviluppi, una series per laversione 0.5, una series per la versione 0.6 e così via. All’inizio vi conviene partire con la serie base, la trunk. Createla.

Una volta creata la serie dovete creare un branches o ramo. I rami corrispondono a vari percorsi di sviluppo all’interno della stessa serie. Se le series indicano qual’è il fine di un percorso di sviluppo, i branches rappresentano le varie strade che conducono a tale fine. Infatti, solitamente , tutti i rami di una serie, alla fine del ciclo di sviluppo, o vengono eliminati o si riuniscono nella cosiddetta versione “stable”.

Creato il vostro ramo (che solitamente essendo il principale si chiama, con molta fantasia, main) potete uppare il vostro progetto con bazaar come spiegato nell’apposita guida. In generale basta il comando:

$ bzr push lp:~userid/project-name/branch-name

Se non sapete come usare bazaar datevi una letta alla pratica guida.

Ora è il momento di scegliere come far collaborare il gruppo al progetto. Esistono infatti 3 approcci differenti:

  • Centralizzato: Tutti i membri del gruppo inviano i propri commit direttamente sul server remoto (meccanismo di Subversion).
  • Centralizzato con commit locali: Ogni membro commissiona alcuni commit locali e, quando è pronto, invia sul server remoto tutti i commit in un solo colpo (meccanismo di Git).
  • Decentralizzato con guida condivisa: Ogni utente ha il proprio ramo di sviluppo che all’occorrenza viene “fuso” con un ramo principale (main).

In questa guida spiegherò il meccanismo più semplice e intuitivo. Il centralizzato.

Il meccanismo centralizzato è il più semplice ma anche il più rigido dei meccanismi. Si adatta bene a gruppi non molto numerosi e che possono facilmente tenersi in contatto. In gruppi troppo numerosi infatti la possibilità di conflitti cresce esponenzialmente.

Per spiegare questo meccanismo supponiamo che ci sia un gruppo di sviluppo composto da tre utenti: L, Q e H. (messaggio subliminale).

  • I tre membri creano un team, che si chiama casualmente proprio LQH, cliccando l’apposito link nell’home di Launchpad (non penso sia complicato ma se avete difficoltà ditemelo che aggiungerò questa parte).
  • Tutti e tre i membri devono effettuare il checkout del progetto. Per fare ciò si aprono il terminale nella cartella dei loro progetti e digitano:
     $ bzr checkout lp:team-name/project-name/branch-name
  • L decide di effettuare alcune modifiche al codice. Le fa tranquillamente e alla fine commissiona con
    $ bzr commit -m "Messaggio"
  • Q e H a questo punto possono aggiornare la loro cartella con
    $ bzr update
  • Se Q o H avessero fatto modifiche al codice prima dell’update le due verisone verrebbero “fuse”. Nonostante  ciò è possibile che si verifichino dei conflitti. Questi vanno risolti (spesso a mano) prima di effettuare nuovamente l’update.

Questo è il meccanismo base con il quale potete organizzare un progetto su Launchpad con Bazaar. Ovviamente ci sono piccoli trucchi che imparerete col tempo che miglioreranno la vostra produttività.

Qui mi sono già dilungato troppo. Se ci sono problemi tornerò volentieri sull’argomento.

 

VerniceMe lo hanno chiesto in alcuni: quale plugin usi per colorare la sintassi del codice? La risposta è semplice: CodeColorer. Ho provato un paio di plugin analoghi ma solo quest’ultimo era semplice da usare, altamente personalizzabile e con un numero molto ampio di linguaggi supportati (come sapete ho necessità di colorare la sintassi Scheme).

Code colorer potete scaricarlo da qui oppure installarlo dal menù plugin direttamente da WordPress.

Il funzionamento è immediato, basta aggiungere un tag prima e dopo il codice che volete inserire specificando quale linguaggio utilizzare. Queste ed altre informazioni per l’utilizzo sono disponibile nella pagina che ho linkato.

C’è solo un “problema” che ho individuato e che penso sia causato da qualche impostazione sbagliata che ho: WordPress infatti rimuove le indentazioni del testo ed è quindi necessario racchiudere il codice comprensivo dei tag di CodeColorer all’interno dei tag <pre>.

Io lo sto utilizzando nel riassetto generale del blog e mi ci sto trovando bene. Spero possa tornarvi utile.

 

Logo SlashCodeBenvenuti a tutti! Benvenuti ai vecchi utenti e benvenuti ai nuovi! SlashCode cambia veste, cambia grafica, cambia dominio, cambia tutto ma non la qualità che ho sempre cercato di mettere nelle guide e nei post di questo blog.

Ho appena finito di sistemare il grosso dei problemi derivanti dal passaggio di dominio e di riassetto grafico. Altri ritocchi verranno fatti nei prossimi giorni insieme alla modifica dei vecchi articoli per renderli più adatti alla nuova veste grafica.

Cosa c’è di nuovo? Molte cose, innanzitutto l’indirizzo. Il nuovo indirizzo di SlashCode è http://slashcode.davideaversa.it inglobato nel mio sito personale che tratta temi più generali e non-informatici. Ricordatevi quindi di aggiornare i feed RSS ;) .

Inoltre ho inserito il plugin per la colorazione della sintassi per rendere più leggibili i vari codici e le varie guide che ho proposto e che proporrò. C’è anche il plugin per inserire formule matematiche in LaTex.

Oltre a questo sto cercando di organizzare in modo più ordinato tag e categorie per facilitare la navigazione il più possibile. Oltre al fatto che il template è dotato di un apposita colonna in cui posso “fissare” gli articoli più importanti per evitare di far scivolare tali articoli nell’oblio degli archivi troppo velocemente.

Insomma. Spero che continuerete a seguirmi con lo stesso interesse anche in questo nuovo dominio perché io continuerò a metterci lo stesso impegno di prima, se non maggiore. ;)

Grazie a Tutti.

 

Lente IngrandimentoSpesso capita di dover cercare quale pacchetto contiene un file particolare. Questo perché a volte ci troviamo a maneggiare dei programmi che ci danno come errore frasi del tipo “Impossibile trovare il file xxx.yy” e non non abbiamo idea di cosa instalalre per trovare quel file. Questa situazione capita poi spessissimo se si stanno compilando sorgenti che non abbiamo scritto noi a causa di una mancata dipendenza.

Come fare?

Semplice. Esiste il programma fatto apposta per noi. apt-file.

Possiamo installare facilmente il programma con

# apt-get install apt-file

Una volta che abbiamo installato apt-file ci basta dare

# apt-file update

Per aggiornare la cache del programma e subito dopo

# apt-file search xxx.yy

Questo comando restituirà la lista dei pacchetti contenenti un file di nome xxx.yy. Ci basta quindi installarli per vedere risolti i nostri problemi. :)

 

Logo LQHSono passati quasi due mesi dall’apertura di LinuxQualityHelp. Ora ci sono 480 iscritti e altri se ne iscrivono ogni giorno. Abbiamo una bella schiera di utenti esperti  pronti a risolvere ogni problema e abbiamo un ottimo tasso di problemi risolti. Sono decisamente soddisfatto. :D

Prossimamente però lo staff sta preparando interessanti novità:

  • Un concorso per il modding desktop incentrato sulla usabilità.
  • Un repository che contiene software e script utili non presenti nei repository ufficiali.
  • Il team di sviluppo invece sta sviluppando un programma in grado di aiutare l’utente ad identificare i problemi della propria installazione.

E oltre a questo molte altre sono le proposte quindi rimanete collegati su LQH

 

Escher - Mani che Disegnano se StesseNella scorsa lezione abbiamo visto come funziona la semplice ricorsione applicata a funzioni numeriche. La ricorsione è però molto più potente e ci permette di operari anche con oggetti che non siano solamente numeri.

Diamo quindi ora una definizione di algoritmo ricorsivo meno formale ma che serve a decifrare meglio il problema a livello pratico.

Supponiamo che ci sia una funzione F che risolva un problema su un input P di dimensione N. N può essere il numero di elementi di una lista, il numero di nodi di un grafo o di un albero, il numero di byte in un file e qualunque altra cosa. Chiamiamo P-N un qualunque ingresso di dimensione N e chiamiamo F(P-N) l’algoritmo che opera sull’input P. Inoltre definiamo con K un qualunque P fissato (ad esempio una lista vuota, oppure una lista con 2 elementi, oppure un albero senza sotto-albero destro, etc…

Da questo ricaviamo che un algoritmo ricorsivo è tale se e solo se:

  • F(K) ha un risultato immediato (passo base)
  • F(P-N) può essere calcolato immediatamente conoscendo il risultato di F(P-M) dove M è minore di N. (passo ricorsivo)

Questo cosa vuol dire. Vuol dire che un algoritmo ricorsivo ricava il risultato su un input di dimensione N grazie al risultato che ottiene su un sotto insieme dell’input iniziale di dimensione M.

Come al solito l’esempio chiarisce molto più di mille parole. Supponiamo di voler scrivere l’algoritmo ricorsivo per invertire una lista ovvero mettere in testa l’ultimo elemento, in seconda posizione il penultimo e così via.

1
2
3
4
5
(define (rovescia lista)
  (cond ((null? lista) '())
      (else (append (rovescia (cdr lista)) (list (car lista))))
  )
)

L’algoritmo non fa altro che appendere l’elemento di testa della lista in coda alla sottolista rovesciata. Ovvero, supponiamo di avere la lista (1 2 3 4) e di volerla rovesciare. Prendendo il primo elemento della lista avremo (1) e la sottolista (2 3 4). Supponiamo di avere la sottolista già rovesciata, ovvero (4 3 2). Ci accorgiamo che per avere la lista originale rovesciata ci basta prendere (1) e metterlo in coda a (4 3 2).

Il problema si riduce quindi a rovesciare (2 3 4). Ma questo a sua volta si riduce a rovesciare (3 4). Che a sua volta si riduce a rovesciare (4). Ma (4) è già rovesciato. E quindi abbiamo concluso.

Ecco un altro esempio:

1
2
3
4
5
6
(define (presente x lista)
   (cond ((null? lista) #f)
      ((= (car lista) x) #t)
      (else (presente x (cdr lista)))
   )
)

Questo algoritmo verifica se l’elemento x è presente nella lista lista. Ancora più semplicemente possiamo applicare il seguente ragionamento ricorsivo:

  • Se x è il primo elemento della lista allora l’elemento x è presente. (caso ase)
  • Se la lista è vuota allora l’argomento non è presente. (caso base)
  • Altrimenti verifico se è presente nel resto della lista. (caso ricorsivo)

Questo è un esempio di ragionamento ricorsivo. In pratica si sposta il problema ad un qualcosa di più piccolo rispetto all’input originale.

Come ultimo esempio vi cito questo:

1
2
3
4
5
(define (sommalista lista)
  (cond ((null? lista) 0)
      (else (+ (car lista) (sommalista (cdr lista))))
  )
)

L’algoritmo non fa altro che sommare tutti gli elementi di una lista. Anche in questo caso il ragionamento ricorsivo è semplice.

  • Se la lista è vuota la somma è ZERO. (caso base)
  • Altrimenti la somma è uguale al primo elemento più la somma del resto della lista.

Torneremo a parlare di ricorsione ancora e ancora. Cerco di fare il possibile per trattarla in modo comprensibile nonostante gli spazi “ristretti” di un blog.

La prossima lezione affronterà un tema molto importante. Il costo della ricorsione. Cercheremo di trovare un modo semplice per capire quanto costa a livello di prestazioni un algoritmo ricorsivo e come evitare che diventi troppo esoso.

 

WarningIl problema è legato alla libreria libpthread-stub0. Dopo l’ultimo aggiornamento Xorg non si avvia su piattaforme a 32bit con driver Intel.

Nel caso disgraziato in cui abbiate aggiornato (come ho fatto io -.-) potete rimediare inserendo i driver vesa al posto di intel nel file /etc/X11/xorg.conf.

Fanno un po schifo sul mio pc ma almeno permettono di usarlo.

EDIT: Potete trovare il vecchio pacchetto qui http://ftp.debian.org/debian/pool/main/libp/libpthread-stubs/libpthread-stubs0_0.2-1_i386.deb

Installatelo e tutto tornerà a funzionare.

EDIT 2: Il problema sembra essersi risolto. ;)

© 2008-2012 SlashCode Suffusion theme by Sayontan Sinha