Larry Fink, an iconic figure in American photography, has passed away, leaving behind a profound legacy. His career was a testament to a unique style—a veritable walking studio—with a camera in one hand and a flash in the other, he traversed the vast social landscapes of America, capturing the essence of both its opulence and austerity. Fink was a pioneer, unflinchingly bringing to light the contrasts of American life through his lens, and his absence leaves a void in the fabric of photographic artistry.
Il Problema 10 del Project Euler, che richiede di calcolare la somma di tutti i numeri primi inferiori a due milioni, offre una finestra affascinante nel mondo della matematica e dell’informatica. I numeri primi, da sempre al centro della teoria dei numeri, trovano applicazioni in diversi campi, dalla crittografia all’analisi numerica. Il problema, apparentemente semplice, cela sfide computazionali non trascurabili, soprattutto considerando la grandezza del numero limite, due milioni. Qui di seguito un codice in Python che prova a risolvere il problema:
# Il codice che segue risolve il problema 10 del Project Euler:
# https://projecteuler.net/problem=10
# La somma dei primi 10 numeri primi è 2 + 3 + 5 + 7 = 17.
# Trova la somma di tutti i numeri primi minori di due milioni.
def is_prime(n):
# Funzione che restituisce True se il numero n è primo, False altrimenti
# (utilizza il teorema di Fermat)
if n <= 1:
return False
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True
def sum_of_primes_below(n):
# Funzione che restituisce la somma di tutti i numeri primi minori di n
primes_sum = 0
for i in range(2, n):
if is_prime(i):
primes_sum += i
return primes_sum
result = sum_of_primes_below(2000000)
print(result)
Il codice Python proposto per affrontare questa sfida si compone di due funzioni principali: is_prime(n)
per determinare se un numero è primo, e sum_of_primes_below(n)
per calcolare la somma di tutti i primi inferiori a un dato numero n
. La funzione is_prime(n)
utilizza un approccio basato sul teorema di Fermat, che prevede di testare i divisori di un numero da 2 fino alla sua radice quadrata. Questo metodo è efficace e corretto, ma quando si tratta di numeri molto grandi, come nel nostro caso, la sua efficienza diventa un fattore critico. Ogni numero viene testato individualmente per la primalità, un processo che diventa progressivamente più lento man mano che n
cresce.
L’approccio di sum_of_primes_below(n)
segue una logica diretta: iterare su tutti i numeri da 2 fino a n-1
, sommando quelli che risultano primi. Anche qui, la semplicità del metodo porta con sé questioni di efficienza, specialmente data la grandezza del numero limite.
In un’ottica di ottimizzazione, sarebbe consigliabile esplorare algoritmi più efficienti come il crivello di Eratostene o il crivello di Atkin, noti per la loro capacità di ridurre drasticamente il numero di operazioni necessarie identificando e scartando i multipli di numeri già riconosciuti come non primi. Questi metodi non solo accelerano il processo di identificazione dei numeri primi ma sono anche più scalabili quando si lavora con intervalli numerici estesi.
Il problema, dunque, sebbene intrinsecamente matematico, ha profonde implicazioni in ambito informatico, specialmente nell’ottimizzazione algoritmica. La ricerca della soluzione non solo offre una dimostrazione dell’applicazione pratica dei numeri primi ma sottolinea anche l’importanza di considerare l’efficienza computazionale, soprattutto quando si affrontano problemi su larga scala. Questa sfida rappresenta un esempio eccellente di come problemi teorici possano essere trasformati in applicazioni pratiche, fornendo opportunità per l’esplorazione e l’innovazione nel campo dell’ingegneria e dell’informatica.
L’antica saggezza di Socrate ci invita a riflettere sull’immensità della nostra ignoranza rispetto ai misteri della vita, dell’anima e delle leggi che regolano l’universo. Tra questi misteri si colloca il numero Φ, noto come rapporto aureo, un numero irrazionale che appare con insistenza nelle proporzioni della natura e nelle opere d’arte e che, per secoli, ha affascinato matematici, artisti e filosofi.
Il rapporto aureo si definisce come quel particolare valore che emerge quando due grandezze, a e b, si trovano in una proporzione tale per cui il rapporto tra la somma delle due grandezze
Questa equazione può essere trasformata in una relazione più familiare, quella di una equazione quadratica. Riscrivendo il primo membro come
Moltiplicando entrambi i membri per Φ, giungiamo a:
Da cui, riarrangiando i termini, otteniamo l’equazione quadratica:
Questa può essere risolta tramite la formula risolutiva delle equazioni quadratiche, producendo due soluzioni. La soluzione positiva, che è quella di nostro interesse, è data da
Parallelamente al concetto di rapporto aureo si sviluppa la sequenza di Fibonacci, una successione in cui ogni numero è la somma dei due precedenti, iniziando da 0 e 1. Questa sequenza, nonostante la sua semplicità, si avvicina progressivamente al rapporto aureo man mano che si procede, offrendo una dimostrazione numerica della presenza di questo rapporto nella natura.
Per apprezzare concretamente questa convergenza, possiamo ricorrere a un codice Python che genera la sequenza di Fibonacci e calcola il rapporto tra numeri consecutivi. Con l’aumentare dei termini, il rapporto si stabilizza intorno a Φ, mostrando la stretta connessione tra il mondo naturale e i numeri.
def fibonacci(n):
fib_sequence = [0, 1]
for i in range(2, n):
next_number = fib_sequence[-1] + fib_sequence[-2]
fib_sequence.append(next_number)
return fib_sequence
def golden_ratio(fib_sequence):
return [fib_sequence[i] / fib_sequence[i-1] for i in range(2, len(fib_sequence))]
n = 10
fib_seq = fibonacci(n)
ratios = golden_ratio(fib_seq)
for ratio in ratios:
print(f"{ratio:.6f}")
La rivelazione che il rapporto aureo si manifesta tanto nell’architettura del nostro DNA quanto nelle proporzioni del corpo umano o nella disposizione dei rami di un albero, conferma l’ipotesi che esistano delle regole estetiche universali. La matematica, attraverso il rapporto aureo e la sequenza di Fibonacci, si fa chiave di lettura per interpretare il linguaggio nascosto della natura e per inseguire quel senso di bellezza e armonia che, da sempre, l’uomo cerca di decifrare.
Il cinema di Ivano De Matteo, come un fiume in piena, travolge senza pietà, scavalcando gli argini del consueto. Nella sua ultima opera, ‘Mia’, si naviga attraverso le acque torbide di un amore tossico, un viaggio che s’intreccia con la storia di un padre, Sergio, infermiere del 118, che si ritrova a fronteggiare la tempesta emotiva della figlia adolescente. Mia, il cuore della narrazione, vive un primo amore ossessivo e violento, un amore che si trasforma in una gabbia, togliendole la libertà e la serenità.
Sergio, interpretato con maestria da Edoardo Leo, cammina su un filo sottile tra l’amore incondizionato per sua figlia e la disperazione di un padre che vede il proprio mondo sbriciolarsi. La sua è una lotta contro un nemico invisibile, contro la violenza che si insinua silenziosamente nelle vite quotidiane, contro il dolore che si fa spazio tra le mura di casa.
Greta Gasbarri, nei panni di Mia, incarna la fragilità e la forza di una ragazza contemporanea, mentre Riccardo Mandolini, nel ruolo del ragazzo violento, diventa il volto dell’oppressione e del male che può nascondersi dietro un’apparente normalità. Milena Mancini, nei panni della madre, è la figura resiliente, colui che tenta di mantenere la famiglia unita nonostante la tempesta.
De Matteo, con la sua regia, ci porta in un viaggio senza ritorno nella psiche umana, esplorando il rapporto complesso tra padre e figlia e il tema universale della genitorialità di fronte al dolore e alla sofferenza dei figli. Il film procede in un crescendo di tensione, svelando strato dopo strato le dinamiche familiari e personali, fino a un finale che esplode in una rabbia inaspettata, spostando il focus dalla storia familiare a questioni più ampie di giustizia e vendetta.
‘Mia’ non è solo un film, è un pugno allo stomaco, un grido che risuona lungo le strade della vita quotidiana. È una storia di amore e dolore, di violenza e redenzione, una narrazione che, pur nelle sue scelte narrative a tratti discutibili, riesce a rimanere impressa nella mente dello spettatore, costringendolo a riflettere su temi profondamente radicati nella nostra società.
De Matteo, ancora una volta, dimostra la sua abilità nel raccontare storie che sono finestre aperte sul mondo, sulle sue contraddizioni e sulla sua brutale realtà. ‘Mia’ è un film da non perdere, un’opera che ci costringe a confrontarci con la nostra umanità e con le sue molteplici sfaccettature.
1. Introduzione
L’applicazione Calcolatrice Interattiva è un tool basato su Python che permette all’utente di eseguire calcoli matematici sia in modo interattivo che tramite l’input da linea di comando. Utilizza la libreria SymPy per gestire le operazioni matematiche.
2. Requisiti
- Python 3.x
- Libreria SymPy
3. Installazione
Per installare la libreria SymPy, esegui il seguente comando:
pip install sympy
4. Utilizzo
4.1 Modalità da linea di comando
Per eseguire un calcolo dalla linea di comando, utilizzare l’opzione -e
o --expression
seguita dall’espressione da valutare:
python calcolatrice.py -e "espressione"
4.2 Modalità interattiva
Per avviare la modalità interattiva, basta eseguire lo script senza argomenti:
python calcolatrice.py
Nella modalità interattiva, inserire le espressioni al prompt >
. Digitare exit
per uscire.
5. Funzionalità
5.1 Funzioni Matematiche Supportate
- Radice quadrata:
sqrt
- Logaritmo:
log
- Logaritmo naturale:
ln
- Funzioni trigonometriche:
sin
,cos
,tan
,asin
,acos
,atan
- Fattoriale:
fact
5.2 Variabili
È possibile assegnare valori alle variabili usando l’operatore =
. Una volta assegnato un valore, la variabile può essere utilizzata in espressioni successive.
5.3 Risultato Precedente
Il risultato dell’ultimo calcolo può essere richiamato utilizzando un punto (.) nelle espressioni successive.
6. Gestione degli Errori
Gli errori durante il calcolo delle espressioni vengono catturati e visualizzati all’utente.
7. Struttura del Codice
main()
: punto di ingresso dell’applicazione.interactive_mode(variables)
: gestisce la modalità interattiva.print_complex(result)
: stampa il risultato.replace_dot_with_last_result(expression, last_result)
: sostituisce il punto con l’ultimo risultato.evaluate_expression(expression, last_result, variables)
: valuta le espressioni matematiche.
Il codice completo può essere scaricato qui .
You have 100kg of potatoes, which are 99% water by weight. You let them dehydrate until they’re 98% water. How much do they weigh now?
All’inizio abbiamo
Dopo la disidratazione, la percentuale di acqua nelle patate scende al
Possiamo ora impostare l’equazione per la massa totale delle patate dopo la disidratazione:
Ovvero:
Quindi, le patate pesano ora
Il Lattice Boltzmann Method (LBM) è una tecnica potente e versatile per la simulazione numerica dei fluidi. Si distingue per la sua capacità di gestire complesse condizioni al contorno e per la relativa facilità di implementazione, rispetto ad altri metodi computazionali. Esploreremo, brevemente, la metodologia LBM attraverso una specifica implementazione: la simulazione del flusso bidimensionale attorno a un cilindro.
Il cuore del metodo LBM è l’equazione di Boltzmann discretizzata nello spazio, nel tempo e nelle velocità. In particolare, il modello D2Q9, che utilizza nove direzioni di velocità in uno spazio bidimensionale, è una scelta comune per via della sua semplicità e accuratezza. L’equazione di Boltzmann discretizzata è implementata per evolvere la funzione di distribuzione delle particelle nel reticolo.
import numpy as np
from numpy import fromfunction, roll
from numpy.linalg import norm
import matplotlib.pyplot as plt
from matplotlib import cm
import imageio
import os
# Parametri del problema
maxIter = 200000 # Numero totale di iterazioni temporali
Re = 220.0 # Numero di Reynolds
nx, ny = 520, 180 # Dimensioni della griglia
ly = ny - 1.0
q = 9 # Numero di popolazioni in ciascuna direzione
cx, cy, r = nx // 4, ny // 2, ny // 9 # Coordinate del cilindro
uLB = 0.04 # Velocità in unità di griglia
nulb = uLB * r / Re # Viscosità in unità di griglia
omega = 1.0 / (3.0 * nulb + 0.5) # Parametro di rilassamento
# Costanti del reticolo
c = np.array([(x, y) for x in [0, -1, 1] for y in [0, -1, 1]]) # Velocità del reticolo
t = 1.0 / 36.0 * np.ones(q) # Pesi del reticolo
t[np.asarray([norm(ci) < 1.1 for ci in c])] = 1.0 / 9.0
t[0] = 4.0 / 9.0
noslip = [c.tolist().index((-c[i]).tolist()) for i in range(q)] # Indice delle condizioni di non scorrimento
i1 = np.arange(q)[np.asarray([ci[0] < 0 for ci in c])] # Parete destra
i2 = np.arange(q)[np.asarray([ci[0] == 0 for ci in c])] # Parete centrale verticale
i3 = np.arange(q)[np.asarray([ci[0] > 0 for ci in c])] # Parete sinistra
# Funzioni di supporto
def sumpop(fin):
return np.sum(fin, axis=0)
def equilibrium(rho, u): # Funzione di equilibrio
cu = 3.0 * np.dot(c, u.transpose(1, 0, 2))
usqr = 3.0 / 2.0 * (u[0] ** 2 + u[1] ** 2)
feq = np.zeros((q, nx, ny))
for i in range(q):
feq[i, :, :] = rho * t[i] * (1.0 + cu[i] + 0.5 * cu[i] ** 2 - usqr)
return feq
# Inizializzazione
obstacle = fromfunction(lambda x, y: (x - cx) ** 2 + (y - cy) ** 2 < r ** 2, (nx, ny))
vel = fromfunction(lambda d, x, y: (1 - d) * uLB * (1.0 + 1e-4 * np.sin(y / ly * 2 * np.pi)), (2, nx, ny))
feq = equilibrium(1.0, vel)
fin = feq.copy()
# Percorsi delle immagini e della GIF
image_paths = []
# Loop principale
for time in range(maxIter):
# Condizione di uscita
fin[i1, -1, :] = fin[i1, -2, :]
rho = sumpop(fin)
u = np.dot(c.transpose(), fin.transpose((1, 0, 2))) / rho
# Parete sinistra: calcolo della densità
u[:, 0, :] = vel[:, 0, :]
rho[0, :] = 1.0 / (1.0 - u[0, 0, :]) * (sumpop(fin[i2, 0, :]) + 2.0 * sumpop(fin[i1, 0, :]))
# Condizione di Zou/He
feq = equilibrium(rho, u)
fin[i3, 0, :] = fin[i1, 0, :] + feq[i3, 0, :] - fin[i1, 0, :]
# Collisione
fout = fin - omega * (fin - feq)
for i in range(q):
fout[i, obstacle] = fin[noslip[i], obstacle]
# Streaming
for i in range(q):
fin[i, :, :] = roll(roll(fout[i, :, :], c[i, 0], axis=0), c[i, 1], axis=1)
# Visualizzazione e salvataggio immagini
if time % 100 == 0:
plt.clf()
img_path = "vel." + str(time // 100).zfill(4) + ".png"
plt.imshow(np.sqrt(u[0] ** 2 + u[1] ** 2).transpose(), cmap=cm.Reds)
plt.savefig(img_path)
image_paths.append(img_path)
# Creazione della GIF
with imageio.get_writer('flow_simulation.gif', mode='I') as writer:
for img_path in image_paths:
image = imageio.imread(img_path)
writer.append_data(image)
# Rimozione delle immagini
for img_path in image_paths:
os.remove(img_path)
Il codice Python fornito implementa la simulazione LBM per il flusso attorno a un cilindro in 2D. Esaminiamo il codice passo dopo passo per vedere come i concetti di LBM sono tradotti in programmazione.
Inizializzazione
Il codice inizia con l’importazione dei moduli necessari e la definizione dei parametri di simulazione:
from numpy import *; from numpy.linalg import *
import matplotlib.pyplot as plt; from matplotlib import cm
maxIter = 200000 # Numero totale di iterazioni temporali.
Re = 220.0 # Numero di Reynolds.
La scelta di maxIter
e Re
determina rispettivamente la durata della simulazione e il regime di flusso. Il numero di Reynolds è particolarmente critico poiché definisce la transizione tra flusso laminare e turbolento.
Definizione del Reticolo e Parametri di Flusso
nx = 520; ny = 180; ... # Dimensioni del reticolo e popolazioni.
uLB = 0.04 # Velocità in unità di reticolo.
nulb = uLB*r/Re; omega = 1.0 / (3.*nulb+0.5); # Parametro di rilassamento.
La definizione delle dimensioni del reticolo (nx
, ny
) e del parametro uLB
stabilisce il quadro della simulazione. Il parametro di rilassamento omega
è derivato dalla viscosità e gioca un ruolo cruciale nell’equazione di evoluzione.
Configurazione del Reticolo e Condizioni al Contorno
Il cilindro viene modellato e le condizioni al contorno vengono stabilite:
obstacle = fromfunction(lambda x,y: (x-cx)**2+(y-cy)**2<r**2, (nx,ny))
vel = fromfunction(lambda d,x,y: (1-d)*uLB*(1.0+1e-4*sin(y/ly*2*pi)), (2,nx,ny))
feq = equilibrium(1.0, vel); fin = feq.copy()
obstacle
rappresenta la posizione del cilindro nel reticolo, mentre vel
stabilisce il profilo di velocità in ingresso, fondamentale per l’innesco del flusso.
Ciclo Principale di Simulazione
Il nucleo della simulazione è un ciclo che aggiorna le funzioni di distribuzione secondo l’equazione di Lattice Boltzmann:
for time in range(maxIter):
... # Condizioni al contorno
... # Passo di collisione
... # Passo di streaming
Qui, la dinamica del flusso è simulata con passi sequenziali che includono la gestione delle condizioni al contorno (come l’uscita del flusso e il movimento del muro), la collisione (aggiornamento delle funzioni di distribuzione verso l’equilibrio), e lo streaming (spostamento delle funzioni di distribuzione attraverso il reticolo).
L’implementazione fornita illustra chiaramente come i principi teorici di LBM siano incorporati in un contesto di simulazione pratica. Questa specifica simulazione LBM ci fornisce un esempio concreto di come metodi computazionali avanzati possano essere utilizzati per investigare fenomeni fisici complessi in modo relativamente semplice e intuitivo. Attraverso un’adeguata sintonizzazione dei parametri e delle condizioni iniziali, il metodo Lattice Boltzmann si rivela uno strumento potentissimo nell’analisi e nella visualizzazione dei flussi fluidi, sia in contesti accademici che industriali.
The Logistic Equation serves as a ubiquitous model in several scientific disciplines to depict population growth within a resource-constrained environment. This article delves into how Python, aided by the SymPy, NumPy, and Matplotlib libraries, can efficiently tackle this equation both analytically and numerically.
The Logistic Equation
The Logistic Equation is mathematically expressed as:
Where:
- P represents the population,
- t is the time,
- r is the growth rate,
- K is the carrying capacity of the environment.
Analytical Solution with SymPy
To ascertain the analytical solution, one can leverage the symbolic computation capabilities of the SymPy
library.
Import Libraries and Define Variables
Firstly, SymPy
is imported and the necessary variables are defined.
from sympy import symbols, Function, Eq, dsolve
t = symbols('t')
P = Function('P')(t)
r, K = symbols('r K')
logistic_eq = Eq(P.diff(t), r * P * (1 - P/K))
Integration and Solution
The general solution of the logistic equation can be found via integration:
Using SymPy, the analytical solution can be obtained as:
analytical_solution = dsolve(logistic_eq)
The analytical solution is:
Where is an integration constant.
Numerical Solution with NumPy
and Matplotlib
For a numerical solution, the Euler method can be employed to approximate the solution to the differential equation. NumPy
is used for the computations and Matplotlib
for visualization.
Code Implementation
import numpy as np
import matplotlib.pyplot as plt
r_value = 0.1
K_value = 1000
P0 = 100
T = 100
dt = 0.1
steps = int(T / dt)
P_numerical = np.zeros(steps)
P_numerical[0] = P0
for i in range(1, steps):
dP = r_value * P_numerical[i-1] * (1 - P_numerical[i-1] / K_value) * dt
P_numerical[i] = P_numerical[i-1] + dP
time = np.linspace(0, T, steps)
plt.plot(time, P_numerical, label='Numerical Solution')
plt.xlabel('Time')
plt.ylabel('Population')
plt.legend()
plt.grid(True)
plt.show()
Comparing Analytical and Numerical Solutions
A comparison between the two solutions confirms the accuracy of the numerical model. To do this, the numerical solution can be overlaid onto the analytically calculated values.
# Code to calculate and plot the analytical solution for a subset of points
# ...
plt.plot(time_reduced, P_analytical_reduced_real, label='Analytical Solution', linestyle='dashed')
plt.scatter(time, P_numerical, color='red', s=10, label='Numerical Solution')
plt.legend()
plt.grid(True)
plt.show()
Conclusion
We have observed that Python, with specialized libraries like SymPy, NumPy, and Matplotlib, can be a powerful tool for both analytical and numerical analysis of differential equations. Specifically, the logistic equation was solved using analytical and numerical methods, providing a complete understanding of population dynamics in a resource-limited setting.
This hybrid approach, blending analytical and numerical analysis, offers a robust strategy for understanding and solving complex problems across various applied science fields.
In un mondo dove l’innocenza è una gemma rara, la perdita di essa è una tragedia incommensurabile. Rapito, il capolavoro di Marco Bellocchio, si immerge nell’oscura acqua di una storia vera, quella del giovane Edgardo Mortara, un bambino ebreo sottratto all’affetto familiare, trainato nelle spire del potere ecclesiastico del Papa Pio IX, interpretato con una carica inquietante da Paolo Pierobon.
In una società dove la religione e la politica si intrecciano, formando una ragnatela spesso soffocante, il piccolo Edgardo diventa simbolo di una lotta silente, quella dell’identità personale contro l’invadente potere che vuole plasmarla. In questa narrazione, Bellocchio non solo racconta una storia del passato, ma specchia le angosce contemporanee di un’Italia ancora stretta tra gli artigli di poteri che negano l’evoluzione, che temono il cambiamento.
La lente di Bellocchio, attenta e impavida, si posa sulle figure che orbitano attorno al giovane protagonista, dipingendo con tocchi sensibili ma decisi, l’umanità straziante dei genitori, interpretati con toccante autenticità da Fausto Maria Alesi e Barbara Ronchi. È un mondo plumbeo quello di Rapito, dove le ombre sembrano avere vita propria, raccontate dalla fotografia evocativa di Francesco Di Giacomo.
Non siamo che il risultato dell’ambiente che ci circonda, e Bellocchio esplora la metamorfosi del piccolo Edgardo con una delicatezza che tocca le corde più profonde. In questa trasformazione, il giovane scopre nuovi orizzonti, ma al prezzo di una perdita incolmabile, quella delle radici che lo ancoravano a una realtà ora lontana.
Rapito diventa così non solo una riflessione sulla fede e il potere, ma una meditazione più ampia sull’identità, sul conflitto tra chi siamo e chi ci viene chiesto di essere. È un viaggio nel cuore oscuro dell’umanità, un viaggio che Bellocchio intraprende con una maestria narrativa che pochi possono eguagliare.
La storia di Edgardo Mortara risuona attraverso i secoli, un monito delicato ma ferreo sul valore della libertà individuale, sul diritto inalienabile di ogni essere umano di scegliere il proprio destino, di rimanere ancorato alla propria identità anche quando le tempeste della vita tentano di sradicarlo.
Con Rapito, Bellocchio non solo ci regala una perla cinematografica, ma ci invita a riflettere, a guardare oltre le apparenze, a cercare quella scintilla di libertà che risiede in ognuno di noi, anche quando il mondo esterno tenta di soffocarla. In questo senso, Rapito non è solo un film, ma una esperienza emotiva e intellettuale, un viaggio nel profondo dell’anima umana che lascia un segno indelebile.
La ricerca degli zeri di una funzione è un problema centrale in matematica applicata e ingegneria. Esistono vari metodi per affrontare questa sfida, dal più semplice metodo della bisezione al più avanzato metodo di Newton-Raphson. In questo articolo, ci concentreremo sul metodo di Halley, un’estensione del metodo di Newton-Raphson, che presenta una convergenza più rapida sotto determinate condizioni. Dopo una presentazione teorica del metodo, forniremo un’implementazione in Python utilizzando la libreria NumPy, con particolare attenzione alla vettorizzazione per aumentare l’efficienza computazionale.
Formula del Metodo di Halley
Il metodo di Halley è definito dalla seguente formula iterativa:
Il metodo di Halley utilizza un’approssimazione della funzione
Risolvendo per
La forza di Python risiede nella sua ampia gamma di librerie di supporto. In questo caso, useremo NumPy per la manipolazione di array e per l’implementazione vettorizzata del nostro algoritmo.
Ecco un estratto del codice Python che implementa il metodo di Halley:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors
# Generazione del polinomio
p = np.polynomial.Polynomial(np.random.choice([-1., 1., 1.j, -1.j], 30))
dp = p.deriv()
ddp = dp.deriv()
def halley_method(z):
return z - p(z) / (dp(z) - ddp(z) * p(z) / (2 * dp(z))), z
Dettagli dell’Implementazione
-
Generazione del Polinomio: Utilizziamo la classe
Polynomial
di NumPy per generare un polinomio con coefficienti casuali. -
Calcolo delle Derivate:
dp
eddp
sono le prime e seconde derivate del polinomio. NumPy fornisce un metododeriv()
per questo scopo. -
Metodo di Halley Vettorizzato: La funzione
halley_method(z)
implementa il metodo di Halley in modo vettorizzato. Grazie a NumPy, l’operazione viene eseguita su ogni elemento dell’arrayz
simultaneamente.
La vettorizzazione è un concetto chiave per migliorare l’efficienza. Invece di utilizzare un ciclo per applicare la formula di Halley a ciascun punto, operiamo su tutto l’array z
contemporaneamente. Questo è possibile perché NumPy è ottimizzato per eseguire operazioni vettorizzate in modo efficiente.
Il metodo di Halley, in conclusione, offre un modo efficace per trovare gli zeri di una funzione con una velocità di convergenza generalmente superiore a quella del metodo di Newton-Raphson, a costo di una maggiore complessità computazionale dovuta al calcolo della seconda derivata. L’implementazione in Python sfrutta la potenza della vettorizzazione tramite NumPy, offrendo un codice sia efficiente che conciso.
L’immagine generata dal codice rappresenta una visualizzazione delle radici del polinomio in questione nel piano complesso. In particolare, l’immagine è costruita usando il metodo di Halley per trovare le radici, e il colore di ciascun punto nel piano complesso è determinato dal numero di iterazioni necessarie per arrivare a una soluzione approssimata entro una certa tolleranza.
In termini tecnici:
-
L’asse x rappresenta la parte reale del numero complesso, mentre l’asse y rappresenta la parte immaginaria.
-
Ogni punto del piano complesso inizia con un valore iniziale (z), che è iterativamente aggiornato usando il metodo di Halley fino a quando non si raggiunge una soluzione approssimata o si supera un numero massimo di iterazioni.
-
Il colore del punto rappresenta il numero di iterazioni effettuate. Un colore che rappresenta un numero minore di iterazioni indica che il punto iniziale era più vicino a una radice del polinomio. In altre parole, regioni dell’immagine con colori simili sono vicine alla stessa radice del polinomio.
In pratica, questa visualizzazione fornisce un modo intuitivo per vedere dove si trovano le radici di un polinomio nel piano complesso e come la convergenza al valore della radice varia a seconda del punto di partenza. Essa può fornire intuizioni utili sulla distribuzione delle radici e sulla robustezza del metodo di Halley nel trovare tali radici da diverse condizioni iniziali.