Newsgroup: it.comp.www.php
Titolo: Generazione codici

Ho un problema che mi genera forti perplessità.
Ho creato una serie di funzione che generano codici alfanumerici e li
inserisce in un db. I codici devono essere tutti diversi e rispettare
la distanza di levenshtein di un carattere e tutto funziona, ma se
refresho la pagina alla velocità di un minipimer il controllo viene
saltato e il codice viene inserito due volte nel db.
Solo se inserisco degli echo di debug e ho così dell'output a monitor
le stringhe non vengono inserite doppie.
Posto il codice per chiarezza anche perchè può essere d'aiuto a
qualcuno.
function generate_code(){
	mt_srand(time());
 
	$lett1 = chr(mt_rand(65,90));
	$lett2 = chr(mt_rand(65,90));
	$lett3 = chr(mt_rand(65,90));
 
	$num1 = mt_rand(0,9);
	$num2 = mt_rand(0,9);
	$num3 = mt_rand(0,9);
 
	$code = $lett1.$lett2.$lett3.$num1.$num2.$num3;
 
	return $code;
}
//Se le stringhe differiscono per 1 carattere o sono uguali ritorna
falso
function is_similar($string1,$string2){
	if (levenshtein(strtolower($string1),strtolower($string2)) <=
1){
		return true;
		break;
	}else{
		return false;
		break;
	}
}
function is_unique($code){
 
	$query_all = mysql_query("SELECT * FROM codici");
 
	while ($result = mysql_fetch_array($query_all)){
		//echo "Checking ".$result['codice']."<br>";
		if (is_similar($result['codice'],$code)){
			//echo $result['codice']." check NON
superato"."<br><br>";
			return false;
			die;
		}
	}
 
	return true;
}
function create_valid_code(){
 
	$code = generate_code();
 
	echo "Codice generato: ".$code."<br>";
 
	if (is_unique($code)){
		$query_insert = mysql_query("INSERT INTO codici
VALUES('','".$code."')");
		echo $code." inserito"."<br><br>";
	}else{
		create_valid_code();
	}
 
}
Viene richiamata una pagina in cui c'è la funzione create_valid_code()
che è quella che fa partire tutto e non dovrebbe arrivare all'insert
se la funzione is_unique ritorna false, ma invece refreshando molto
velocemente e togliendo l'echo "Codice generato.." , l'insert lo fa lo
stesso.
 ------------------------------------
 Il peccato di Caino
 La seduzione del male
 
www.vampiripavia.com
-------------------------------------
                *IcarusDream*
  -Dove un sogno è ancora libero-

 mt_srand(time());
per ora direi di usare microtime()
se ho tempo leggo il resto del codice, troppo malindentato ;-)
vedo solo due cose (che non centra con il problema):
   return true;
   break;  // never reached
   return false;
   die;    // never reached
Per indagare meglio sul problema, direi di provare con un mt_srand(1234); e
verificare che cosa tornano le funzioni...

se ho tempo leggo il resto del codice, troppo malindentato ;-)
Non mi puoi dire una cosa del genere. E' il freeAgent che l'ha ridotto
cosě. Io sono un maniaco dell'indentazione :)
 ------------------------------------
 Il peccato di Caino
 La seduzione del male
 
www.vampiripavia.com
-------------------------------------
                *IcarusDream*
  -Dove un sogno č ancora libero-

   -Dove un sogno è ancora libero-

 così. Io sono un maniaco dell'indentazione :)
Bene!
Male per freeAgent (forse non gli piace il tab?).

timeslice di un secondo.
ho usato floor(mysql_query("select rand()*1000000)) come seme e in
questo modo non inserisce più doppioni nel db anche se frullo sul
refresh, ma continuo a non capire come mai il codice che ho postato
permette l'inserimento di doppioni.
Se uso un valore fisso al posto di un random, al secondo refresh va in
palla perchè trova che il valore è già presente e looppa con lo stessa
costante, quindi le funzioni di controllo sulla distanza di
levenshtein funzionano...(se impongo la distanza > 1 automaticamente
escludo anche i doppioni).
Deve essere un fatto riguardante l'engine del php che non riesce a
processare velocemente quanto il mio dito :)
Se avessi tempo di sbirciare quelle funzioni mi faresti un favorone,
perchè vorrei avere la conferma che siano corrette, per una questione
personale.
Thnx
 ------------------------------------
 Il peccato di Caino
 La seduzione del male
 
www.vampiripavia.com
-------------------------------------
                *IcarusDream*
  -Dove un sogno è ancora libero-

 personale.
L'ho provato e funziona. L'unica cosa che potrebbe essere e' la definizione
della tua tabella (ho messo un codice char(6).).  La puoi postare?
Sarebbe interessante sapere quanti codici sono possibili con questa
procedura (17 M di dati possibili)
Ecco il codice provato (leggermente modificato e provato su w2k):
<?
mysql_connect("localhost","root","");
mysql_select_db("codici");
//mysql_query("CREATE TABLE codici (codice char(6))") or die(mysql_error());
$cnt=0;
function generate_code(){
    GLOBAL $cnt;
    mt_srand(time()-$cnt*251);
    $cnt++;
    $code .= chr(mt_rand(65,90));
    $code .= chr(mt_rand(65,90));
    $code .= chr(mt_rand(65,90));
    $code .= mt_rand(0,9);
    $code .= mt_rand(0,9);
    $code .= mt_rand(0,9);
    return $code;
}
function is_similar($string1,$string2){
    return levenshtein(strtolower($string1),strtolower($string2)) <= 1;
}
function is_unique($code){
    $query_all = mysql_query("SELECT * FROM codici");
    while ($result = mysql_fetch_array($query_all)){
        if (is_similar($result['codice'],$code))
            return false;
    }
    return true;
}
function create_valid_code(){
    $code = generate_code();
    echo "Codice generato: ".$code."<br>";
    if (is_unique($code)){
        $query_insert = mysql_query("INSERT INTO codici
VALUES('".$code."')")
            or die(mysql_error());
        echo $code." inserito"."<br><br>";
    }else{
// pericoloso con questa recursione:
        create_valid_code();
    }
}
// refresh ultra fast ;-)
// poi si lanciano un paio di thread nello stesso momento, bel test di
stress
for ($i=0; $i < 10000; $i++)
    create_valid_code();
?>

?>
Già fatto il ciclo for e ho notato che ne crea il 30/40%
Sto cercando di capire perchè. 
Il Mersenne Twister ha uno spread molto largo, quindi non penso
vengano creati doppioni su 1000 cicli.
La perdita deve essere dovuta a qualcosaltro.


 vengano creati doppioni su 1000 cicli.
perche' usi mt_srand() ogni momento. Piu' giusto sarebbe utilizzare solo una
volta mt_srand() ed usare dopo solo piu la funzione mt_rand(). Ma mi sembra
solo possibile all'interno di un unico programma, i valori non puoi
ereditare ad un altro script (sniff).

ereditare ad un altro script (sniff).
Hai ragione. mt_srand() inizializza ogni volta e quindi la statistica
di spread dei valori del MT va a farsi benedire e siccome mt_srand() è
void non posso passare nulla a un altro script.
Il check sulla distanza di levenshtein assicura comunque una certa
differenza tra i codici.
Nel tuo calcolo non hai tenuto conto della clausola della distanza di
levenshtein che riduce drasticamente il numero dei codici.
Per ogni codice di n lettere generato ne esclude n*2.
Nel mio caso un la generazione di un codice ne esclude 12.


 Nel mio caso un la generazione di un codice ne esclude 12.
Questo non ho capito, ho forse mi ero mal espresso, ho forse I'm missing
something ;-)
"The Levenshtein distance is defined as the minimal number of characters you
have to replace, insert or delete to transform str1 into str2"
Dato che tu hai strlen(str1)==strlen(str2) e ametti solo una operazione
(levenshtein distance uguale 1) o nessuna per dire che siano simili mir pare
che non puoi usare delete e insert come operazione. Un delete avrebbe gia'
distanza 1 un altro insert distanza 2 e automaticamente non e' piu simile
con il (lev<=1) e verra' accettao dalla tua procedura. O sbaglio la logica?
Ju
P.S.: OT, pero' molto interessante come discorso!
P.P.S.: Le note di malmsteen in un suo asolo avrebbe un levenshtein distance
molto basso (codificando l'altezza di ogni nota, senza contare sul ritmo)?
;-)

molto basso (codificando l'altezza di ogni nota, senza contare sul ritmo)?
La distanza metrica delle sue note non è bassa.
Distanza 1/2 sarebbero cromatismi e lui usa scale minori con distanze
1 e 1.5
La distanza ritmica invece è bassissima :)


1).
Quindi non capisco ancora come arrivavi a quel 12 (n*2)!
Ho fatto un test empirico con questo codice, dall'ordine dell'array di input
($vals) dipende leggermente quanti valori sono in realta' possibile (di
intuito capisco anche perche', si genera una albero che dipende dai stati di
prima) (7.5-7.6% passano +-)
<?
set_time_limit(1000);
srand (time());
$pos=4;
$MAX=pow(10,$pos);
$codeEx="%0{$pos}d"; // code 0000-9999
for ($i=0; $i < $MAX; $i++) {
  $vals[$i]=sprintf($codeEx,$i);
}
shuffle($vals);
$arrL=array();
for ($i=0; $i < $MAX; $i++) {
  $insert=true;
  foreach($arrL as $val) {
    $levLow=levenshtein($val,$vals[$i]) <= 1;
    if ($levLow){ // to low, do not insert.
      $insert=0;
      break;
    }
  }
  if ($insert)
    $arrL[]=$vals[$i];
}
// print_r($arrL);
echo count($arrL)*100/$MAX ."%";
?>

Quindi non capisco ancora come arrivavi a quel 12 (n*2)!
In effetti mi sono confuso io pensando al solo replace di un carattere
ma immediatamente adiacente. Ovvere A sostituibile solo con B o con Z.
Quello che intendevo io era, considerando codici sempre della stessa
lunghezza che dato ABC devi cominciare ad escludere BBC, DBC, EBC ecc
perchè sono tutti codici che hanno distanza di lev = 1 rispetto ad
ABC.
