Stellen einer Zahl ermitteln

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Twilo
User
Beiträge: 109
Registriert: Mittwoch 10. Januar 2007, 19:17
Wohnort: Berlin
Kontaktdaten:

Hallo,

gibt es eine "bessere" Methode als diese?

Code: Alles auswählen

len(str(10000))
mfg
Twilo
[url=http://www.farb-tabelle.de/][b]Farbtabelle[/b][/url]
BlackJack

Ja:

Code: Alles auswählen

In [9]: int(math.log(10000, 10) + 1)
Out[9]: 5
Allerdings musst Du mit der 0 aufpassen.
Redprince
User
Beiträge: 128
Registriert: Freitag 22. Oktober 2004, 09:22
Wohnort: Salzgitter
Kontaktdaten:

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..?
I am not part of the allesburner. I am the [url=http://allesburner.de]allesburner[/url].
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.
Benutzeravatar
gkuhl
User
Beiträge: 600
Registriert: Dienstag 25. November 2008, 18:03
Wohnort: Hong Kong

Wobei man von negative Zahlen nur den Betrag betrachten sollte und auch für Null den Fehler abfangen muss.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Code: Alles auswählen

>>> int(log(100,10))+1
3
>>> int(log(1000,10))+1
3
>>> len(str(1000))
4
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

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

Code: Alles auswählen

len(str(2**43112609-1))
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.
Zuletzt geändert von Darii am Montag 12. Oktober 2009, 21:56, insgesamt 1-mal geändert.
karolus
User
Beiträge: 141
Registriert: Samstag 22. August 2009, 22:34

Hallo

Code: Alles auswählen

int(math.log10(1000)+1)
4
scheint besser.

Gruß Karo
BlackJack

@numerix: Ich hatte ja auch die Variante von karolus vorgeschlagen. Ist schon wichtig *wo* man die 1 addiert. :-)
karolus
User
Beiträge: 141
Registriert: Samstag 22. August 2009, 22:34

Hallo
Ich hatte ja auch die Variante von karolus vorgeschlagen. Ist schon wichtig *wo* man die 1 addiert.
Wo hast du die vorgeschlagen?

Code: Alles auswählen

int(math.log(1000,10))+1
3
int(math.log(1000,10)+1)
3
macht keinen Unterschied, aber

Code: Alles auswählen

int(math.log10(1000)+1)
4
tut das erwartete!

Gruß Karo
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

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 :roll: ). 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?
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

derdon hat geschrieben:http://paste.pocoo.org/show/144605/
Ich weiß aber leider nicht, wie man das global-Statement vermeiden kann. Jemand eine Idee?

Code: Alles auswählen

def count_digits(num):
    quotient = divmod(num, 10)[0]
    if quotient:
        return 1 + count_digits(quotient)
    return 1
Bottle: Micro Web Framework + Development Blog
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Oh Mann! Wie konnte ich nur so blöd sein! :oops:

Ab in die Ecke mit mir.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

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.
Das Leben ist wie ein Tennisball.
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

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.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

str1442 hat geschrieben:Closures sind öfter mal praktisch für sowas.
Und gleich viel kürzer als eine einfache iterative Lösung ^^
Das Leben ist wie ein Tennisball.
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

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

Code: Alles auswählen

def c(n):_=abs(n)/10;return 1+c(_) if _ else 1
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

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
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

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 :D

Code: Alles auswählen

def count_digits(num, base=10, count=1):
    num = abs(num)
    while num >= base**count:
        count += 1
    return count
Bottle: Micro Web Framework + Development Blog
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

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
Antworten