PrimeGenerator
In questo esempio andremo a scrivere una classe che genera dei numeri primi fino a un massimo specificato dall'utente.
Prima del refactoring
<?php
/**
* Questa classe genera dei numeri primi fino a un massimo specificato dall'utente.
* L'algoritmo usato è il Setaccio di Eratostene.
* Eratostene di Cirene fu il primo uomo a calcolare la circonferenza della Terra.
* Noto anche per aver lavorato sui calendari con anni bisestili e aver gestito la libreria di Alessandria.
* L'algoritmo è semplice. Dato un array di interi che partono da 2. Elimina tutti i multipli di 2.
* Trova il prossimo intero disponibile ed elimina tutti i suoi multipli.
* Ripeti fino ad aver superato la radice quadrata del valore massimo.
*/
class GeneratePrimes
{
/**
* @param int $maxValue è il limite della generazione
*
* @return int[]
*/
public static function generate(int $maxValue): array
{
// L'unico caso valido
if ($maxValue >= 2) {
// Dichiarazioni
$s = $maxValue + 1; // Dimensione dell'array
$f = [];
// Inizializza l'array a true
for ($i = 0; $i < $s; $i++) {
$f[$i] = true;
}
// Elimina i non-primi
$f[0] = $f[1] = false;
// Setaccio
for ($i = 2; $i < sqrt($s) + 1; $i++) {
// Se i è primo, elimina i suoi multipli
if ($f[$i]) {
for ($j = 2 * $i; $j < $s; $j += $i) {
$f[$j] = false; // Un multiplo non può essere primo
}
}
}
// Quanti primi ci sono?
$count = 0;
for ($i = 0; $i < $s; $i++) {
if ($f[$i]) {
$count++;
}
}
// Sposto i primi nel risultato
$primes = [];
for ($i = 0, $j = 0; $i < $s; $i++) {
// Se è primo
if ($f[$i]) {
$primes[$j++] = $i;
}
}
return $primes;
} else { // $maxValue < 2
return [];
}
}
}
Dopo il refactoring
<?php
/**
* Questa classe genera dei numeri primi fino a un massimo specificato dall'utente.
* L'algoritmo usato è il Setaccio di Eratostene.
* Dato un array di interi a partire da 2:
* - trova il primo intero non eliminato ed elimina tutti i suoi multipli
* - ripeti finché non vi sono più multipli nell'array
*/
class PrimeGenerator
{
private static $crossedOut = [];
private static $result = [];
public static function generate(int $maxValue): array
{
if ($maxValue < 2) {
return [];
}
self::uncrossIntegersUpTo($maxValue);
self::crossOutMultiples();
self::putUncrossedIntegersIntoResult();
return self::$result;
}
private static function uncrossIntegersUpTo(int $maxValue): void
{
for ($i = 0; $i < $maxValue + 1; $i++) {
self::$crossedOut[$i] = false;
}
}
private static function crossOutMultiples(): void
{
$limit = self::determineIterationLimit();
for ($i = 2; $i <= $limit; $i++) {
if (self::notCrossed($i)) {
self::crossOutMultiplesOf($i);
}
}
}
/**
* Ogni multiplo nell'array ha un fattore primo che è minore o uguale alla radice
* delle dimensioni dell'array, così che possiamo evitare di eliminare i multipli
* dei numeri maggiori di tale radice
* @return int
*/
private static function determineIterationLimit(): int
{
return (int)sqrt(count(self::$crossedOut));
}
private static function notCrossed(int $i): bool
{
return self::$crossedOut[$i] === false;
}
private static function crossOutMultiplesOf(int $i): void
{
for ($multiple = 2 * $i; $multiple < count(self::$crossedOut); $multiple += $i) {
self::$crossedOut[$multiple] = true;
}
}
private static function putUncrossedIntegersIntoResult()
{
for ($j = 0, $i = 2; $i < count(self::$crossedOut); $i++) {
if (self::notCrossed($i)) {
self::$result[$j++] = $i;
}
}
}
}
Last updated