Schreibmaschinendistanz

Code-Stücke können hier veröffentlicht werden.
Antworten
PyPankow
User
Beiträge: 9
Registriert: Dienstag 28. Dezember 2021, 16:02

Hallo zusammen,

ich hoffe, mir kann vielleicht jemand helfen. Ich beschäftige mich gerade mit der Analyse von Strings. Dabei bin auf die sog. Schreibmaschinendistanz gestoßen.
Diese drückt den Abstand von zwei Buchstaben auf der - im dt. QWERTZ-Tastaturbelegung - aus.

Beispielsweise sind die Buchstaben ‚u‘ und ‚i‘ direkt nebeneinander.
Sie haben den Abstand 1. Um vom ‚h‘ zum ‚k‘ zu gelangen, muss man über das ‚j‘ gehen. Der Abstand ist 2.

Hund wäre demnach: H - > U (1); U ->N (2); N->D (4) = Distanz 7.

Ich suche nun ein Snippet, welches die Schreibmaschinendistanz berechnen kann.
Ich bin leider bisher total erfolglos geblieben und suche daher Unterstützung.

Danke Euch!
Gerne auch für weitere Hinweise!
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du bist darauf gestoßen? Wozu willst du die denn verwenden? Oder hast du das als Aufgabe bekommen?

Zur Lösung könnte man zb die drei Zeilen der quertz-Belegung als Listen anlegen. Für zwei gegebene Buchstaben ist der Abstand dann die Differenz ihrer Listen Position. Sind die Listen nicht dieselben, muss man dann noch 1 oder 2 addieren, je nach dem in welcher Liste sie stecken.
PyPankow
User
Beiträge: 9
Registriert: Dienstag 28. Dezember 2021, 16:02

Zum Glück sind die Zeiten mit Aufgaben bekommen vorbei:) Ursprünglich habe ich mich mal mit Dubletten bei einem Verein beschäftigt und bin an dem Thema immer mal wieder hängengeblieben....

Ich verstehe Deinen Ansatz!

Aber wie ist das dann mit ü und ä. Diese sind ja eigentlich nur 1 entfernt. Mit der Listenaddition wären sie doch dann 2 entfernt, oder?

Danke Dir schon mal!
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die sind so weit entfernt wie du sie in die Listen packst. Letztlich ist dieses Maß ja nicht wirklich sinnvoll beziehungsweise schlecht definiert. Die Tasten sind ja eigentlich versetzt, aber die Distanz reflektiert das ja auch nicht.
PyPankow
User
Beiträge: 9
Registriert: Dienstag 28. Dezember 2021, 16:02

Ja. Stimmt. Ich dachte, es gibt irgendwo eine Norm dafür. Danke!
narpfel
User
Beiträge: 643
Registriert: Freitag 20. Oktober 2017, 16:10

@PyPankow: Es gibt eine Norm dafür: Die Reihen sind jeweils um ¼ Tastenbreite verschoben, bzw. ½ zwischen den Zahlen und der Q-Reihe, ¼ zwischen der Q- und A-Reihe und ½ zwischen A und Y. Weil die Hebel Platz brauchen. ;-)
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

Hmm, eigentlich spannend:

Mit networx (leider ohne die tatsächliche Entfernung zu erhalten)

Code: Alles auswählen

from itertools import pairwise
import networkx as nx

WORD = "HUND"

def setup_network():
    top = list("QWERTZUIOPÜ")
    middle = list("ASDFGHJKLÖÄ")
    bottom = list("YXCVBNM")
    keyboard = nx.Graph()
    keyboard.add_nodes_from(top)
    keyboard.add_nodes_from(middle)
    keyboard.add_nodes_from(bottom)
    keyboard.add_edges_from(pairwise(top))
    keyboard.add_edges_from(pairwise(middle))
    keyboard.add_edges_from(pairwise(bottom))
    keyboard.add_edges_from(zip(top, middle))
    keyboard.add_edges_from(zip(top[1:], middle))
    keyboard.add_edges_from(zip(middle, bottom))
    keyboard.add_edges_from(zip(middle[1:], bottom))
    return keyboard


def main():
    keyboard = setup_network()
    paths = [nx.shortest_path(keyboard, previous_letter, next_letter) for previous_letter, next_letter in pairwise(WORD)]
    print(paths)
    
    distances = [len(path) - 1 for path in paths]
    print(distances)

    total_distance = sum(distances)
    print(total_distance)

main()

"""
[['H', 'U'], ['U', 'H', 'N'], ['N', 'B', 'V', 'C', 'D']]
[1, 2, 4]
7
"""
Oder "Bruteforce" wie im Video erwähnt:

Code: Alles auswählen

import math
from itertools import product, pairwise

WORD = "HUND"


def main():
    full_distance = 19.05
    quarter_distance = 19.05 * 0.25
    half_distance = 19.05 * 0.5

    top = {letter: (num * full_distance, 0) for num, letter in enumerate("QWERTZUIOPÜ")}
    middle = {letter: (num * full_distance + quarter_distance, full_distance) for num, letter in enumerate("ASDFGHJKLÖÄ")}
    bottom = {letter: (num * full_distance + quarter_distance + half_distance, 2 * full_distance) for num, letter in enumerate("YXCVBNM")}

    keys = top | middle | bottom

    letter_distances = {(this, other): math.sqrt((tx - ox) ** 2 + (ty - oy) ** 2) for (this, (tx, ty)), (other, (ox, oy)) in product(keys.items(), repeat=2)}
    word_letter_distances = {
        (previous_letter, next_letter): letter_distances[(previous_letter, next_letter)] for previous_letter, next_letter in pairwise(WORD)
    }

    for (this, other), distance in word_letter_distances.items():
        print(f"{this} -> {other}: {distance:0.2f} mm")

    print(f"Total: {sum(word_letter_distances.values()):0.2f} mm")


main()
"""
H -> U: 23.81 mm
U -> N: 38.40 mm
N -> D: 69.34 mm
Total: 131.55 mm
"""
PyPankow
User
Beiträge: 9
Registriert: Dienstag 28. Dezember 2021, 16:02

Wow! Herzlichen herzlichen Dank! Sowohl für das Video plus Infos und für den Code! Das macht wirklich Freude! Danke!
PyPankow
User
Beiträge: 9
Registriert: Dienstag 28. Dezember 2021, 16:02

Hallo,

ich habe jetzt das Problem, dass ich pairwise in PyCharm nicht importieren kann. Ich habe bereits nach Alternativen recherchiert, leider alles ohne Erfolg.
Auch hier https://docs.python.org/2/library/itertools.html finde ich nichts dazu. Habt Ihr einen Tipp für mich? Herzlichen Dank!
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

PyCharm hat damit nichts zu tun, das ist nur die IDE. Du benutzt eine zu alte Pythonversion. `pairwise` gibt es erst ab Python 3.10.
Die Dokumentation, die Du verlinkt hast, ist für Python2 und das benutzt Du hoffentlich nicht mehr.
https://docs.python.org/3/library/itert ... s.pairwise
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ansonsten gibt es `pairwise()` für Python <3.10 auch im externen `more_itertools`-Modul.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
PyPankow
User
Beiträge: 9
Registriert: Dienstag 28. Dezember 2021, 16:02

Ach, manchmal! Tausend Dank!

Ich habe jetzt Python aktualisiert und über more_itertools pairwise importiert. Das klappt!

Ich knobel jetzt noch an der letzten Fehlermeldung, die ich in myCharm bekomme, wenn ich die Printbefehle nach def ergänze....

print(f"Total: {sum(word_letter_distances.values()):0.2f} mm")
print("Hier endet der Code")

Fehlermeldung:

n <module>
print(f"Total: {sum(word_letter_distances.values()):0.2f} mm")
NameError: name 'word_letter_distances' is not defined
Benutzeravatar
sparrow
User
Beiträge: 4164
Registriert: Freitag 17. April 2009, 10:28

Die Fehlermeldung sagt, dass kein Wert an den Namen gebunden wurde.
Mehr kann man nicht sagen, wenn du den Code nicht zeigst, der den Fehler verursacht.
PyPankow
User
Beiträge: 9
Registriert: Dienstag 28. Dezember 2021, 16:02

Danke! Das ist im Grunde der Code von oben, angepasst beim Import und eben die zwei Print-Befehle
Ohne die vorletzte Zeile gibt es keinen Fehler, aber auch kein Ergebnis......

Code: Alles auswählen

#from itertools import pairwise
from itertools import product
from more_itertools import pairwise
import math


WORD = "HUND"


def main():
    full_distance = 19.05
    quarter_distance = 19.05 * 0.25
    half_distance = 19.05 * 0.5

    top = {letter: (num * full_distance, 0) for num, letter in enumerate("QWERTZUIOPÜ")}
    middle = {letter: (num * full_distance + quarter_distance, full_distance) for num, letter in enumerate("ASDFGHJKLÖÄ")}
    bottom = {letter: (num * full_distance + quarter_distance + half_distance, 2 * full_distance) for num, letter in enumerate("YXCVBNM")}

    keys = top | middle | bottom

    letter_distances = {(this, other): math.sqrt((tx - ox) ** 2 + (ty - oy) ** 2) for (this, (tx, ty)), (other, (ox, oy)) in product(keys.items(), repeat=2)}
    word_letter_distances = {
        (previous_letter, next_letter): letter_distances[(previous_letter, next_letter)] for previous_letter, next_letter in pairwise(WORD)
    }

    for (this, other), distance in word_letter_distances.items():
        print(f"{this} -> {other}: {distance:0.2f} mm")

    print(f"Total: {sum(word_letter_distances.values()):0.2f} mm")


print(f"Total: {sum(word_letter_distances.values()):0.2f} mm")
print("Hier endet der Code")
Benutzeravatar
sparrow
User
Beiträge: 4164
Registriert: Freitag 17. April 2009, 10:28

Weil es auf Modulebene kein "word_letter_distances" gibt. Und das ist auch richtig so.
Wenn das dein ganzes Script ist, wo genau rufst du da die Funktion main() auf?
In der Regel sieht der Eintiegspunkt dafür am Ende des Scripts so aus:

Code: Alles auswählen

if __name__ == "__main__":
    main()
Auf Modulebene (also ohne Einrückung) stehen nur Importe, die Definition von Konstanten, Klassen und Funktionen und eben am Ende des Scripts dieser Zweizeiler um die Funktion main aufzurufen.
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@PyPankow: `word_letter_distances` ist *in* der Funktion definiert. Du versuchst ausserhalb der Funktion darauf zuzugreifen wo es halt undefiniert ist. Und in der Funktion steht diese Zeile doch bereits‽

Was bei dem Code fehlt ist der Aufruf der `main()`-Funktion. Wenn man Funktionen nicht aufruft, dann passiert auch nix. Also am Ende statt der `print()`-Zeilen:

Code: Alles auswählen

if __name__ == "__main__":
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
PyPankow
User
Beiträge: 9
Registriert: Dienstag 28. Dezember 2021, 16:02

Erneut ein Danke! Ich habe jetzt getüftelt und in myCharm die Main Funktion eingebaut. Sie greift auch auf das Modul/Funktion distanz zu.

Der Code dafür sieht so aus. Ja.. Einstiegsniveau... :)

Code: Alles auswählen

import distanz

if __name__ == "__main__":
   distanz.WORD
   print(distanz.WORD)
   print(distanz.pairwise("fgdgf"))
   print(distanz.distanz())
Die Fehlermeldung hat sich sich jetzt verändert und meldet nicht unterstützte Operatoren. Da bin ich ehrlich gesagt überfragt. Frage mich, warum der Fehler bei mir kommt. Stichwort Versionen oder fehlt eine lib oder oder oder.... Die Operatoren müssten doch unterstützt werden...

Code: Alles auswählen

line 20, in distanz
    keys = top | middle | bottom
TypeError: unsupported operand type(s) for |: 'dict' and 'dict'
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

noe, der |-Operator ist zumindest auch bei mir fuer dicts nicht unterstuetzt. Ich habe Python 3.8. Kann sein, dass neuere Pythons den unterstuetzen. Den kanst du dir aber auch trivial nachbauen, durch ein leeres dict und zwei update calls. Das ist ja nur Kombination aus allen Eintraegen.
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

PyPankow
User
Beiträge: 9
Registriert: Dienstag 28. Dezember 2021, 16:02

Vielen vielen Dank! Jetzt funktioniert es! Super!!!
Antworten