Schlagwort: Mathematik

Darstellung von IP-Adressen (Ergänzung: Versionskennungen) 👍 👎

Zu meinem letzten Eintrag hat mich eine Mischung aus Frage und Idee erreicht, dieses Verfahren doch auch bei Versionskennungen (z. B. v1.2.3) anzuwenden, welche schließlich ähnlich aussehen, wie eine IP-Adresse.

Grundsätzlich ist das natürlich naheliegend und nicht völlig abwegig, funktioniert jedoch nur bedingt. Das Problem: Die Ziffern unserer Stellen müssten nach bisherigem Vorgehen endlich sein, damit wir die Basis korrekt wählen können, um das Ergebnis eindeutig zu halten. Im Dezimalsystem stehen uns als Ziffern 09 zur Verfügung. Bei IP-Adressen lauten die "Ziffern" 0255 (dezimal).

Bei einer Versionskennung muss im Allgemeinen beispielsweise auf v1.0.9 nicht zwangsläufig v1.1.0 folgen (da die einzelnen "Stellen" traditionell* als Hauptversion, Nebenversion und Fehlerbehebungsversion geführt werden). Das heißt, werden erneut lediglich einige Fehler behoben, wird eher v1.0.10 folgen usw. Bei anders aufgebauten Versionskennungen (variierende Stellenanzahl, "beta" etc.) wird es noch schwieriger.

Sollte jedoch, aus welchen Gründen auch immer, jede Stelle auf z. B. 09 begrenzt sein, so könnten wir natürlich analog zum "üblichen" Dezimalsystem verfahren und als Basis die 10 wählen (oder einfach die Punkte entfernen …), womit das Verfahren wieder funktioniert. Dies dürfte jedoch ein eher seltener und ungewöhnlicher Spezialfall sein, insofern sollte hier der Einfachheit wegen nach wie vor eher stellenweise verglichen werden.


*) Ich persönlich halte daran auch fest. Viele Projekte scheinen diesem Schema jedoch nicht mehr zu folgen. Teilweise gibt es zumindest zur Kommunikation nach Außen gar nur noch eine einzelne Versionsnummer (z. B. v19), was jedoch auf den ersten Blick weniger über den Inhalt der Version aussagt. Es ist sicherlich nicht ganz falsch, dass das den Endanwender auch nicht direkt interessieren muss – praktisch finde ich es trotzdem. Smiley: tongue_out

Darstellung von IP-Adressen 👍 👎

Vorweg sei erwähnt, dass ich mich im weiteren Verlauf des Artikels der Übersicht wegen auf IPv4 beschränken werde. Das vorgestellte Konzept funktioniert prinzipiell jedoch auch bei IPv6 in ähnlicher Form.

Jeder hat bestimmt schon einmal IP-Adressen der Form "127.0.0.1" (lokale Adresse), "192.168.1.1" (private Adresse) oder "217.160.176.125" (öffentliche Adresse) gesehen. Das ist eine für den Menschen bequeme Schreibweise, jedoch nur bedingt zur automatisierten Verarbeitung (z. B. für Vergleiche bei Sortierungen) geeignet.

Eine IPv4-Adresse besteht aus vier Segmenten, die jeweils 8 Bit (≙ 4 * 1 Byte = 32 Bit, bzw. 4 Byte) umfassen. Insofern verwendet eine IPv4-Adresse dieser Schreibweise ("dotted decimal") eine Art Stellenwertsystem wie unser gebräuchliches Dezimalsystem, wobei die einzelnen Stellen jedoch mehrere Ziffern umfassen und daher durch ein Trennzeichen (in diesem Fall ein ".") getrennt werden.

Die Wertigkeit der Stelle einer Zahl unseres Dezimalsystems ergibt sich bekanntermaßen aus dessen Position:
Stellenwertsystem von Dezimalzahlen
01020304050607
  1 =                     1 * 100  5 =                     5 * 100
10 = 1 * 101 + 0 * 100 15 = 1 * 101 + 5 * 100
123 = 1 * 102 + 2 * 101 + 3 * 100
Ähnlich verhält es sich nun bei einer IPv4-Adresse, jedoch mit entsprechend anderen Wertigkeiten:
Stellenwertsystem von IP-Adressen
0102030405
127.  0.  0.  1 = 127 * 224 +   0 * 216 +   0 * 28 +   1 * 20 = 2 130 706 433
192.168. 1. 1 = 192 * 224 + 168 * 216 + 1 * 28 + 1 * 20 = 3 232 235 777
82.165. 40.206 = 82 * 224 + 165 * 216 + 40 * 28 + 206 * 20 = 1 386 555 598
In Anbetracht dieser Tatsache können wir einer IPv4-Adresse also einen ein-eindeutigen ganzzahligen Wert zuordnen, indem wir – analog zu unserem Dezimalsystem – die Summe der einzelnen Stellen bilden. Durch diese Darstellungsweise sind nun beispielsweise sehr einfach "größer"- und "kleiner"-Relationen zu ermitteln.

Die zu dieser Thematik passende Eigenschaft Address der IPAddress-Klasse des .NET-Frameworks ist als veraltet gekennzeichnet. Solltet ihr diese Funktionalität dennoch benötigen, so lässt sich dies selbstverständlich auch schnell selbst schreiben – wobei zu berücksichtigen gilt, dass das nur für IPv4-Adressen in dieser Form verwendet werden kann:
Vorgehensweise zur ganzzahligen Darstellung von IPv4-Adressen
010203040506
IPAddress ipAddress = IPAddress.Parse("217.160.176.125");byte[] segmentList = ipAddress.GetAddressBytes();
long ip2long = Convert.ToInt64((segmentList[0] * Math.Pow(2, 24)) + (segmentList[1] * Math.Pow(2, 16)) + (segmentList[2] * Math.Pow(2, 8)) + segmentList[3]); // – oder auch -long ip2long = (((long) segmentList[0] << 24) | ((long) segmentList[1] << 16) | ((long) segmentList[2] << 8) | (long) segmentList[3]);
PHP bietet hier beispielsweise die Funktion ip2long (und deren Gegenstück long2ip) an.

Anzahl der Ziffern einer Zahl ermitteln 👍 👎

Oftmals sehe ich zur Ermittlung der Anzahl der Ziffern einer Zahl, dass diese explizit in eine Zeichenkette konvertiert und anschließend mit Zeichenketten-Operationen bearbeitet wird. Unter sehr dynamischen Sprachen wie beispielsweise PHP wird einem dies besonders einfach gemacht.

Das funktioniert zwar zweifelsohne und kann je nach Kontext auch der schnellste Weg sein, mathematisch elegant ist das aber nicht und darum möchte ich zumindest der Vollständigkeit wegen gerne aufzeigen, wie sich dieses Problem rein rechnerisch lösen lässt.

Dazu erinnern wir uns zuerst einmal an den Begriff der Größenordnung, welche eine Zahl zu gegebener Basis (im Alltag also meistens die 10, bei Informatikern evtl. auch noch 2, 8 oder 16 Smiley: winking) um eben diesen Faktor erhöht. Anschaulicher lässt sich dies als Potenz vorstellen, bei der der Exponent inkrementiert wird. Da dabei praktisch im jeweiligen Stellenwertsystem auch die Anzahl der Ziffern der jeweiligen Zahl steigt, fällt dem geneigten Leser möglicherweise bereits ein Zusammenhang auf. Zuerst einmal wollen wir die bisherigen Kenntnisse jedoch der Übersicht wegen formal notieren:
Größenordnungen
0102030405060708091011
   1 = 100    // eine Stelle   5 = 100,69  // eine Stelle  10 = 101    // zwei Stellen  15 = 101,17  // zwei Stellen 100 = 102    // drei Stellen 999 = 102,99  // drei Stellen1000 = 103    // vier Stellen1234 = 103,09  // vier Stellen
Es besteht offensichtlich ein Zusammenhang zum Exponenten der Basis des Zahlensystems, wie groß diese Zahl ist – was ja auch völlig klar so sein muss und letztlich keine besondere Erkenntnis ist. Genau diese Rückbesinnung auf Bekanntes bringt uns jedoch zur Lösung des Problems: Wir müssen zu unserer gegebenen Zahl den Exponenten zur Basis des gegebenen Zahlensystems suchen, wodurch unsere Zahl erzeugt wird.

Mit gewissem Entsetzen denken nun vielleicht die ersten Leser spontan an Wurzeln (und nebenbei an ihre letzte Wurzelbehandlung Smiley: grinning), aber es ist "noch schlimmer": Da uns Wurzeln die Basis liefern, wir jedoch den Exponenten benötigen und das Kommutativgesetz bei Potenzen im Allgemeinen nicht gilt (d. h. ab ≠ ba), benötigen wir die zweite Umkehroperation – den Logarithmus.

Dieser löst uns die Gleichung 10x = Zahl nach x auf und damit haben wir bereits beinahe die Lösung unserer eigentlichen Fragestellung. Da der Exponent auch Dezimalstellen enthalten kann, die Anzahl der Ziffern jedoch in jedem Fall eine natürliche Zahl sein muss, runden wir das Ergebnis erst einmal pauschal ab. Da die Zählung des Exponenten nun auch noch bei 0 beginnt (was uns als Softwareentwickler aber nur mäßig schockieren dürfte Smiley: cool), korrigieren wir diesen Versatz durch Addition von 1 und erhalten folgende Lösung:

Anzahl der Ziffern einer Zahl (im Dezimalsystem) = ⌊log10 Zahl⌋ + 1

Natürlich lässt sich dies auch in Programmen sehr einfach verwenden, daher wollen wir eine Erweiterungsmethode für ganze Zahlen in C# schreiben, die uns die Anzahl der Ziffern liefert:
Anzahl der Ziffern einer Zahl (Implementierung in C#)
010203040506070809
public static int GetDigitCount(this int number) {    if(number != 0) {        double baseExp = Math.Log10(Math.Abs(number));
return Convert.ToInt32(Math.Floor(baseExp) + 1); } else { return 1; }}
Wir berücksichtigen dabei die 0 separat und beugen außerdem Problemen mit negativen Zahlen durch Verwendung der Betragsfunktion vor. Die Anwendung gestaltet sich nun denkbar einfach:
Anzahl der Ziffern einer Zahl (Verwendung der C#-Implementierung)
01020304
int digitCount = (1).GetDigitCount();     // 1int digitCount = (12).GetDigitCount();    // 2int digitCount = (123).GetDigitCount();   // 3int digitCount = (1234).GetDigitCount();  // 4
Natürlich wäre auch die Implementierung in Form einer Schleife, welche die Zahl fortlaufend durch die Basis (10) teilt, denkbar, um die Anzahl der Ziffern zu ermitteln. Aber das sei dem geneigten Leser zur Übung überlassen. Smiley: tongue_out

Notation formaler Ausdrücke 👍 👎

a + b ist ein Term, wobei a und b als Summanden bezeichnet werden. Das sollte soweit jedem Leser dieses Blogs klar sein – mit Grundschulmathematik soll es nun aber gar nicht weitergehen, wir benötigen nur eine gemeinsame Grundlage für den weiteren Teil des Artikels. Smiley: grinning

Einen Bestandteil haben wir bisher jedoch gar nicht näher betrachtet: Das beinahe selbstverständliche Symbol der Addition, das +. Dabei ist das gar nicht so uninteressant. Was genau ist das eigentlich? Nun, auf jeden Fall wird es als Operator bezeichnet. Diese Schreibweise ist uns so vertraut, dass sich die meisten bisher wahrscheinlich gar keine Gedanken zu den Hintergründen einer solchen Schreibweise gemacht haben – und wir auch andere ganz selbstverständlich verwenden.

Tatsächlich handelt es sich nämlich um den in sogenannter Infixnotation geschriebenen Aufruf der Funktion +(a,b); der Name der Funktion lautet also + und Funktionen dieser Darstellung werden üblicherweise in Präfixnotation geschrieben. Dies ist einem aus der Mathematik bei Funktionen wie sin und cos geläufig und selbstverständlich auch als Entwickler der meisten Programmiersprachen und sogar bei Konsoleneingaben.

Präfixnotation findet in der Mathematik meist bei unären Operationen Anwendung, wohingegen bei binären Operationen für gewöhnlich die Infixnotation zum Einsatz kommt. Mehrstellige Operationen werden (auch) in einfacher Funktionsschreibweise notiert.

Bei in Infixnotation geschriebenen Ausdrücken ist unbedingt die Operatorpräzedenz (und ggf. Operatorassoziativität) zu berücksichtigen. Dazu gehört auch der den meisten Lesern sicherlich bekannte Spruch "Punkt vor Strich": Manche Operationen müssen vor anderen ausgewertet werden, um das Ergebnis eindeutig zu halten; im Allgemeinen gilt beispielsweise:

(a + b) * c  ≠  a + (b * c)

Wie dadurch bereits ersichtlich und hoffentlich bekannt, lässt sich eine bestimmte Rangfolge mit Klammerung auch erzwingen. Üblicherweise gilt ohne nähere Spezifikation folgende Rangfolge bei einfachen Rechnungen:
  1. Klammerung
  2. Potenzierung
  3. Multiplikation, Division ("Punktrechnung")
  4. Addition, Subtraktion ("Strichrechnung")
Auch Programmiersprachen (z. B. C#, PHP) besitzen eine solche Rangfolge und unterscheiden sich teilweise.

Um aber noch einmal auf unsere Feststellung der unterschiedlichen Schreibweisen einzugehen: Tatsächlich ist dieses Problem nämlich der Infixnotation geschuldet; Präfixnotation (im Übrigen auch die umgekehrte Variante, die Postfixnotation) kann nämlich klammerfrei erfolgen und ist ganz einfach – wenn auch für die meisten sicherlich eher etwas ungewohnt – auf unser Beispiel der Addition übertragbar:
Addition mit Symbol und Präfixnotation
01
+ a b
Auch "komplexere" (na ja …) Anwendungen stellen kein Problem dar:
Vergleich von Infix- und Präfixnotation
0102030405
  // Infixnotation(a + b) * (c – d)
// Präfixnotation* + a b – c d
Wie man sieht, können hier die Klammern entfallen und man erhält dennoch das erwartete Ergebnis. Diese Darstellungsform vereinfacht im Übrigen das automatisierte Verarbeiten erheblich.

Projektverweise

Kategorien / Archiv  |  Übersicht RSS-Feed

Schlagworte

Suche