Klaus Müller: Inkrementieren bei Buchstaben

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

  1. 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

    --
      Irgendwann kommt die Waldfee - oder auch nicht ... (Projektphase: Keine Ahnung)
      Bis dahin:
      Ski- und Bergtouren in den Tölzer Voralpen und im Karwendel
    1. 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

      1. 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)

        --
          Irgendwann kommt die Waldfee - oder auch nicht ... (Projektphase: Keine Ahnung)
          Bis dahin:
          Ski- und Bergtouren in den Tölzer Voralpen und im Karwendel
        1. 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

          --
            Irgendwann kommt die Waldfee - oder auch nicht ... (Projektphase: Keine Ahnung)
            Bis dahin:
            Ski- und Bergtouren in den Tölzer Voralpen und im Karwendel
          1. 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

            1. 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

              --
                Irgendwann kommt die Waldfee - oder auch nicht ... (Projektphase: Keine Ahnung)
                Bis dahin:
                Ski- und Bergtouren in den Tölzer Voralpen und im Karwendel