Seite 1 von 2
Stellen einer Zahl ermitteln
Verfasst: Montag 12. Oktober 2009, 19:21
von Twilo
Hallo,
gibt es eine "bessere" Methode als diese?
mfg
Twilo
Verfasst: Montag 12. Oktober 2009, 19:31
von BlackJack
Ja:
Allerdings musst Du mit der 0 aufpassen.
Verfasst: Montag 12. Oktober 2009, 19:35
von Redprince
Inwiefern ist das nun "besser"? Mathematischer ist das natürlich, aber ist es wirklich performanter als die Typumwandlung? Oder gibt es tatsächlich Fälle, in denen len(str(n)) ein falsches Ergebnis liefert..?
Verfasst: Montag 12. Oktober 2009, 20:06
von BlackJack
@Redprince: Es ist IMHO eleganter die Anzahl der Stellen auszurechnen, und nicht eine Zeichenkette anzulegen, nur um deren Länge abzufragen und dann wieder wegzuwerfen. Ab einer bestimmten Grösse ist es spürbar schneller.
Verfasst: Montag 12. Oktober 2009, 20:18
von gkuhl
Wobei man von negative Zahlen nur den Betrag betrachten sollte und auch für Null den Fehler abfangen muss.
Verfasst: Montag 12. Oktober 2009, 21:38
von numerix
Code: Alles auswählen
>>> int(log(100,10))+1
3
>>> int(log(1000,10))+1
3
>>> len(str(1000))
4
Verfasst: Montag 12. Oktober 2009, 21:43
von Darii
Redprince hat geschrieben:Inwiefern ist das nun "besser"? Mathematischer ist das natürlich, aber ist es wirklich performanter als die Typumwandlung? Oder gibt es tatsächlich Fälle, in denen len(str(n)) ein falsches Ergebnis liefert..?
Rechne doch einfach mal
long->str ist ab einer gewissen Länge nicht mehr vernachlässigbar langsamer als „mathematische“ Methode. Da ist irgendwo der Wurm drin, der Rechenaufwand steigt jedenfalls nicht linear mit der Anzahl der Stellen. Wobei die, wie man in Numerix Beispiel sieht, mit gewissen Rundungsfehlern behaftet ist.
Verfasst: Montag 12. Oktober 2009, 21:53
von karolus
Hallo
scheint besser.
Gruß Karo
Verfasst: Montag 12. Oktober 2009, 22:21
von BlackJack
@numerix: Ich hatte ja auch die Variante von karolus vorgeschlagen. Ist schon wichtig *wo* man die 1 addiert.

Verfasst: Montag 12. Oktober 2009, 22:43
von karolus
Hallo
Ich hatte ja auch die Variante von karolus vorgeschlagen. Ist schon wichtig *wo* man die 1 addiert.
Wo hast du die vorgeschlagen?
macht keinen Unterschied, aber
tut das erwartete!
Gruß Karo
Verfasst: Montag 12. Oktober 2009, 23:36
von derdon
http://paste.pocoo.org/show/144605/
Ich weiß aber leider nicht, wie man das global-Statement vermeiden kann (das ist ja sowas von superböse

). Der einzige Weg, der mir eingefallen ist, ist ein Keyword-Argument mit dem Startwert 1 in der Funktion zu benutzen. Das macht aber keinen Sinn, weil man nie einen anderen Startwert als 1 haben möchte.
Jemand eine Idee?
Verfasst: Montag 12. Oktober 2009, 23:44
von Defnull
Code: Alles auswählen
def count_digits(num):
quotient = divmod(num, 10)[0]
if quotient:
return 1 + count_digits(quotient)
return 1
Verfasst: Montag 12. Oktober 2009, 23:49
von derdon
Oh Mann! Wie konnte ich nur so blöd sein!
Ab in die Ecke mit mir.
Verfasst: Montag 12. Oktober 2009, 23:50
von EyDu
Code: Alles auswählen
def count_digits(num):
quotient = abs(num) / 10
return 1 + (count_digits(quotient) if quotient else 0)
Man könnte die Klammern auch weglassen und die 0 durch eine 1 ersetzen, das macht es aber nicht gerade übersichtlich. Bei der Division musst du ggf. // verwenden.
Verfasst: Dienstag 13. Oktober 2009, 00:03
von str1442
Alternativ zu den obgien (für dieses Problem schöneren) Lösungen hättest du auch eine innere Funktion nutzen können, dann hat die Funktion keine Abhängigkeiten im globalen Namensraum mehr und "num_of_digits" bliebe "Implementationsdetail":
Code: Alles auswählen
def count_digits(num):
actual_num_of_digits = [1]
def do_digit_count(num):
quotient, remainder = divmod(num, 10)
if quotient:
actual_num_of_digits[0] += 1
do_digit_count(quotient)
do_digit_count(num)
return actual_num_of_digits[0]
Für Python größer 2.6 dann nonlocal statt der Liste. Closures sind öfter mal praktisch für sowas.
Verfasst: Dienstag 13. Oktober 2009, 00:14
von EyDu
str1442 hat geschrieben:Closures sind öfter mal praktisch für sowas.
Und gleich viel kürzer als eine einfache iterative Lösung ^^
Verfasst: Dienstag 13. Oktober 2009, 00:27
von derdon
str1442 hat geschrieben:Closures sind öfter mal praktisch für sowas.
Ich habe auch schon an Closures gedacht, wusste aber nicht wirklich, wie ich es umsetzen sollte.
Edit: 46 Zeichen
Verfasst: Dienstag 13. Oktober 2009, 01:24
von derdon
EyDu hat geschrieben:str1442 hat geschrieben:Closures sind öfter mal praktisch für sowas.
Und gleich viel kürzer als eine einfache iterative Lösung ^^
Dein Snippet (und auch meines) ist
rekursiv, nicht
iterativ
Verfasst: Dienstag 13. Oktober 2009, 03:15
von Defnull
Function-Calls sind durch den Kontext-Wechsel vergleichsweise teuer. Iterative Lösungen sind eigentlich immer der Rekursiven vor zu ziehen. Das spart Stack-Bewegungen und Speicher und lässt sich vom Compiler auch einfacher optimieren.
Code: Alles auswählen
def count_digits(num, base=10, count=1):
num = abs(num)
while num >= base:
count += 1
num = num//base
return count
Und hier nochmal ne Idee, die einfach der Vollständigkeit halber erwähnt werden sollte

Man kann es eben auch anders herum machen
Code: Alles auswählen
def count_digits(num, base=10, count=1):
num = abs(num)
while num >= base**count:
count += 1
return count
Verfasst: Dienstag 13. Oktober 2009, 07:09
von numerix
Im ersten Teil des Threads wurde der Aspekt "Geschwindigkeit" erwähnt ...
In der Praxis dürfte die log-Variante dennoch die günstigste sein, man muss halt zu der +1 noch einen "kleinen" Wert beigeben, um auf der (einigermaßen) sicheren Seite zu sein.
Code: Alles auswählen
>>> delta = 1 + 1e-8
>>> int(log(1000,10)+delta)
4
>>> int(log(999,10)+delta)
3