Inkrementieren bei Buchstaben
Klaus Müller
- php
Hallo!
ich habe ein Startwert von abc
nun möchte ich, wenn z.B. 12 übergeben wird, dass das Ergebnis dann "abo" ist
dieser Ansatz ist bestimmt nocht effizient, oder?
$a=12;
$start = 'abc';
for ($i = 1; $i <= $a; $i++) $start++;
print $start
wenn ich $a = 500 oder nochmehr übergebe, ist das alles andere als effizient ;-)
wie kann man soetwas besser lösen?
Müller
Holladiewaldfee,
Du könntest z.B. ein Array der Art
$a = array('a', 'b', ...);
anlegen. Zunächst zerlegst Du Deinen String in Zahlentupel in Z_26 (Z_26 ist der Ring der Zahlen 0 bis 25) - das machst Du mit array_search (http://de3.php.net/array-search). Am besten packst Du das Ergebnis auch in ein Array. Dann brichst Du die zu addierende Zahl mit ein paar Modulooperationen soweit auf, daß Du sie nach Potenzen von 26 geordnet hast:
z = a*26^0 + b*26^1 + c*26^2 + ...
Den Startwert (=höchsten Wert) für die Potenzen bekommst Du aus der 26ten Wurzel der Zahl (nach unten gerundet).
Jetzt addierst Du a auf den letzten Buchstaben, d.h. die letzte Zahl drauf. Wenn Du Dir eine Funktion zur Zahlenbehandlung in Z_26 schreibst, mußt Du den neuen Wert der Zahl zurückgeben. Nun mußt Du vergleichen, ob die neue Zahl >=0 und <=alte Zahl ist. Ist sie das, mußt Du b inkrementieren, ist sie es nicht, gehst Du direkt zu b über. Nun machst Du mit b und dem vorletzten "Buchstaben" genau das gleiche Spielchen, dann mit c usw.
Wenn Du schließlich die neuen Werte für die Buchstaben hast bastelst Du Dir mit Hilfe des Arrays $a den neuen String zusammen. Fertig. Ganz einfach ;-)
Allerdings habe ich so das Gefühl, daß Deine "simple" Lösung bis hin zu Faktoren jenseits von 10^6 deutlich performanter ist. Danach dürfte meine Funktion so langsam aber sicher aufholen und schließlich überholen. Bei richtig großen Zahlen rennt Deine dann irgendwann ins Timeout. Meine hoffentlich nicht so schnell. Schätze mal, das war nicht das, was Du hören wolltest, oder?
Ciao,
Harry
Hallo!
zur Zeit bin ich soweit
function erhoehen($anzahl, $startstring = 'abc'){
$buchstaben = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z');
$einzelne_buchstaben = str_split($startstring);
$startwert = 0;
for ($i = 0, $potenz = count($einzelne_buchstaben)-1; $i < count($einzelne_buchstaben); $i++, $potenz--){
$wert = array_search ($einzelne_buchstaben[$i], $buchstaben);
$bla = pow(count($buchstaben), $potenz);
$startwert = $startwert + ($wert*$bla);
if ($wert === false) die('unbekannter Buchstabe enthalten!');
}
$endwert = $startwert+$anzahl;
}
print erhoehen(328,'abc');
bloss was muss ich mit "$endwert" machen, damit ich nachher "anu" herausbekomme?
Müller
Holladiewaldfee,
da ich da grade nicht durchblicke und es auf den ersten Blick auch viel zu einfach aussieht, hier mal meine Funktion:
---
<?php
function fAscciiInc($sString, $iInc)
{ # Hilfsarray
$aBuchstaben = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z');
# String zerlegen
$aEinzeln = str_split(strrev($sString));
# Zu addierende Zahl in Potenzen der Basis zerlegen
$aFac = fModB($iInc, count($aBuchstaben));
# Ueberpruefen, ob die Laenge des Basisstrings fuer die Addition ausreicht.
# Wenn nicht, String mit Nullwerten auffuellen
if(count($aFac)>count($aEinzeln))
{ for($i=count($aEinzeln); $i<count($aFac); $i++)
$aEinzeln[$i] = $aBuchstaben[0]; }
# Addition durchfuehren
for($i=0, $j=count($aEinzeln); $i<$j; $i++)
{ # Buchstaben in Zahl umwandeln
$aEinzeln[$i] = array_search($aEinzeln[$i], $aBuchstaben);
# Entsprechenden Wert addieren
$aRes = fZAdd($aEinzeln[$i], $aFac[$i], count($aBuchstaben));
# Neuen Wert zuweisen
$aEinzeln[$i] = $aRes[0];
# Ueberpruefung, ob die naechste Potenz zusaetzlich erhoeht werden muss
if($aRes[1]===true)
$aFac[$i+1]++;
# Zahl wieder in Buchstaben umwandeln
$aEinzeln[$i] = $aBuchstaben[$aEinzeln[$i]]; }
return strrev(implode('', $aEinzeln));
}
function fModB($iZahl, $iBase)
{ # Zerlegt eine Zahl in Potenzen von $iBase
# Startwert
$iS = floor(log($iZahl, $iBase));
$aMult = array();
for($i=$iS; $i>-1; $i--)
{ # Ganzzahlige Division ohne Rest
$aMult[$i] = floor($iZahl / pow($iBase, $i));
# Zahl um entsprechenden Betrag vermindern
$iZahl -= $aMult[$i] * pow($iBase, $i); }
return $aMult; }
function fZAdd($iZahl, $iInc, $iBase)
{ # Zunaechst einfache Addition
$iZahl += $iInc;
$bMod = false;
# Muss die naechste "Zahl" ebenfalls erhoeht werden?
if($iZahl >= $iBase)
{ $iZahl -= $iBase;
$bMod = true; }
return array($iZahl, $bMod); }
echo fAscciiInc('abc', 328);
?>
---
Viel Spaß beim Verdauen, da steckt jede Menge Mathematik drin ;-)
Übrigens hab ich vorher einen Fehler gemacht, den Startwert für die Potenzzerlegung bekommst Du natürlich nicht aus der abgerundeten 26ten Wurzel sondern aus dem abgerundeten Logarithmus zur Basis 26.
Die Funktion oben ist übrigens so ausgelegt, daß sie mit jeder beliebigen Buchstabenbasis funktioniert, die Du in $aBuchstaben vorgibst. Das können auch Sonderzeichen sein. Oder Pommes. Oder Currywürste. Egal. Sie kann aber nur addieren - subtrahieren ist nicht. Du darfst sie aber gerne um die entsprechende Fähigkeit erweitern ;-)
bloss was muss ich mit "$endwert" machen, damit ich nachher "anu" herausbekomme?
Gar nichts ... es kommt nämlich "ans" heraus ;-)
Ciao,
Harry
(der grade über einen höchstwahrscheinlich überflüssigen Feature-Artikel zum Thema "Rechnen mit abstrakten Gebilden" nachdenkt ...)
(der die Funktion bei Gelegenheit mal zu einer Klasse erweitern wird, die dann auch subtrahieren und evtl. multiplizieren kann)
Holladiewaldfee,
antworte ich mir mal selber:
Meine Lösung von vorher ist viel zu kompliziert. Es reicht, den String mittels des Arrays und als Basis der Potenzen der Länge des Arrays in eine dezimale Zahl umzurechnen, ganz stinklangweilig den Wert zu addieren und anschließend wieder (Potenzzerlegung) in einen String umzuwandeln. Das ist alles prinzipiell schon in der vorhergehenden Funktion enthalten, nur viel, viel zu kompliziert.
Ciao,
Harry
Hallo,
wie wäre es mit dieser Lösung?
function string2dec($string, $baseFrom, $baseTo){
$stringLen = strlen($string);
$baseFromLen = strlen($baseFrom);
$baseToLen = strlen($baseTo);
$stringArray = str_split($string);
$baseFromArray = str_split($baseFrom);
$baseToArray = str_split($baseTo);
$ausgabe = 0;
for ($i = 0, $potenz = $stringLen-1; $i < $stringLen; $i++, $potenz--){
$wert = array_search ($stringArray[$i], $baseFromArray);
$bla = pow(count($baseFromArray), $potenz);
$ausgabe = $ausgabe + ($wert*$bla);
if ($wert === false) die('Fehler');
}
return $ausgabe;
}
function dec2string($string, $baseFrom, $baseTo){
$stringLen = strlen($string);
$baseFromLen = strlen($baseFrom);
$baseToLen = strlen($baseTo);
$stringArray = str_split($string);
$baseFromArray = str_split($baseFrom);
$baseToArray = str_split($baseTo);
$convert = base_convert($string, $baseFromLen, $baseToLen);
$hilfsBuchstaben = array('0','1', '2', '3', '4', '5', '6','7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z');
for ($i = 0; $i < $baseToLen; $i++) $buchstaben_r[$hilfsBuchstaben[$i]] = $baseToArray[$i];
return strtr($convert, $buchstaben_r);
}
function convert($erhoehen, $startwert = 'abc'){
$buchstaben = 'abcdefghijklmnopqrstuvwxyz';
$zahlen = '0123456789';
$end = ($startwert != 'abc') ? string2dec($startwert, $buchstaben, $zahlen) + $erhoehen : 28 + $erhoehen;
$reverse = dec2string($end, $zahlen, $buchstaben);
$ausgabe = (strlen($startwert) > strlen($reverse)) ? str_pad($reverse, strlen($startwert), 'a', STR_PAD_LEFT) : $reverse;
return $ausgabe;
}
print convert('1000', 'aaaaaaaa');
mfg
Twilo
Holladiewaldfee,
wie wäre es mit dieser Lösung?
So hier meine letzte Lösung zu diesem Thema:
---
function fAscciiInc($sString, $iInc)
{ # Hilfsvariablen
$aBuchstaben = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z');
$iBase = count($aBuchstaben); $sLength = strlen($sString);
# String in Zahl umwandeln
$iZahl = 0;
for($i=$sLength-1; $i>=0; $i--)
$iZahl += array_search($sString[$i], $aBuchstaben) * pow($iBase, $sLength-1-$i);
# Addition
$iZahl += $iInc;
# Zahl in String umwandeln
$iS = floor(log($iZahl, $iBase));
$aMult = array();
for($i=$iS; $i>-1; $i--)
{ # Ganzzahlige Division ohne Rest
$aMult[$i] = floor($iZahl / pow($iBase, $i));
# Zahl um entsprechenden Betrag vermindern
$iZahl -= $aMult[$i] * pow($iBase, $i); }
$sString = '';
for($i=0, $j=count($aMult); $i<$j; $i++)
$sString .= $aBuchstaben[$aMult[$i]];
return strrev($sString);
}
---
Bedingt durch ihren Aufbau ist diese Funktion restriktiver als die vorhergegangene Lösung, d.h. sie schmeißt führende "a", die ja als 0 behandelt werden, weg. Dafür ist sie (glaube ich) schneller und kann theoretisch auch subtrahieren, solange das Ergebnis >= 0 ist.
Ciao,
Harry