JavaScript Anzahl der unterschiedlichen Android Pin Muster berechnen?

BeBur schrieb:
Da gibt es auch nichts zu verteidigen
Ich verteidige das nicht, aber das kannst du als Anfänger und ohne Background nicht beurteilen, insofern ist es auch müßig, darüber zu diskutieren

Es ist sinnlos und kontraproduktiv, für temporäre, lokale Variablen lange Namen zu wählen, weil sich die Lesbarkeit dadurch verschlechtert... Ähnlich wie bei Anfängern, die jede Zeile mit einem Kommentar versehen

Aber das lernst du später noch, ca. 2 Jahre

Unabhängig davon würde ich aber gerne herausfinden, wo der Fehler im Code ist
 
Diese pseudo herablassende Art weil du dich gekränkt fühlst ist vor allem im Kontext des Threads hier völlig unauthentisch. Mit besserer Code Qualität wäre die Aufgabe überhaupt kein Thema, aber ich sehe, hier ist gerade leider keine Einsicht zu erwarten.
 
  • Gefällt mir
Reaktionen: gummiwipfel, KitKat::new() und Powl_0
n/a schrieb:
Aber das lernst du später noch, ca. 2 Jahre
Starke Aussage für jemanden, der gerade an 30 Zeilen Algorithmus versagt.

Ich bin sicherlich kein Anfänger mehr und finde solchen Code trotzdem grausig zu lesen.

Klar kann ich erarbeiten, was welche Variable sein soll und macht, aber das ist grundlos mühsam. Einmal sinnvoll benannt und alle werden glücklich.
 
  • Gefällt mir
Reaktionen: KitKat::new()
Der Streit um Lesbarkeit und sauberen Code brachte mich auf einen Gedanken. Ist Pseudocode heute noch ein Thema? Lesbarer geht Code in dem Zusammenhang kaum und der EdT setzt sich dabei derart mit dem Problem und dessen Lösung auseinander, dass sichergestellt werden kann, dass oder aus welchem Grund nicht der Lösungsansatz zu einem korrekten Ergebnis führt.

Dem könnten sogar Nichtprogrammierer wie ich folgen.
 
dx1 schrieb:
Ist Pseudocode heute noch ein Thema?
An der Uni in Algorithmen und Datenstrukturen (1 und 2) musst du Pseudocode in den Prüfungen schreiben (mit Papier, Stift und Hand...), also ja

Beides liegt bei mir schon etwas zurück :)

BeBur schrieb:
Diese pseudo herablassende Art weil du dich gekränkt fühlst
Ich fühle mich nur gekränkt, wenn zu Unrecht behauptet würde, mein Code sei sch****
 
dx1 schrieb:
da steckt hoffentlich mit drin, dass man [...] auch von der 1 zur 6 kommt. Also, auf direktem Pfad zwischen 4 und 5 hindurch.
Kommt man bei den Android-Entsperrmustern aber nicht.
Edit: Jetzt hab ich tatsächlich den editierten Post zititiert, aber das Zitat nicht richtig gelesen. Ja, man kommt von der 1 zur 6, aber nicht zur 7 oder zur 3.

Für die Muster gilt:
  • Es gibt die neun im Quadrat angeordneten Punkte
  • Jeder Punkt kann maximal einmal verwendet werden (also zyklenfrei)
  • Es sind mindestens 4 Punkte zu verwenden
  • Punkte in direkter Linie können nicht übersprungen werden.

D.h. man kommt von 1 zu 8, aber man kommt von 1 nur dann zu 7, wenn die 4 bereits im Muster enthalten ist.
Tatsächlich reduziert dir dieses Verschatten deine Anzahl an Mustern der Länge 9 auch recht drastisch:
Während es ohne das Verschatten 9! = 362 880 Permutationen gibt, gibt es mit der Verschattung nur noch 140 704 mögliche Muster der Länge 9.

Insgesamt gibt es 389 112 Android-Entsperrmuster. Und zwar:
140 704 der Länge 9,
140 704 der Länge 8,
72 912 der Länge 7,
26 016 der Länge 6,
7 152 der Länge 5,
1 624 der Länge 4,

Wobei ich nicht tatsächlich an einem Handy getestet habe, was passiert, wenn man ein längeres Muster einzeichnet, als gesetzt wurde. (Wenn also 1234 gesetzt wurde, und ich 123456789 einzeichne.)
Die Anzahl von 389 112 Mustern findet man auch, wenn man im Netz sucht. Die in #13 zitierten 389 497 beziehen noch 385 (9+56+320) Muster der Länge 1-3 mit ein.

Python:
from typing import List, Tuple, Set
from copy import deepcopy

all_patterns = []

def get_usable_points(current_pattern: List[int]) -> Set[int]:
    all_points = (1, 2, 3, 4, 5, 6, 7, 8, 9)
    usable_points = set(all_points)
    for used in current_pattern:
        usable_points.remove(used)
    for shadowed in get_shadowed_points(current_pattern):
        usable_points.discard(shadowed)
    return usable_points

def get_shadowed_points(current_pattern: List[int]) -> Set[int]:
    try:
        current_point = current_pattern[-1]
    except IndexError:
        current_point = 0  
    shadowed = set([])
    if 1==current_point:
        if not 2 in current_pattern:
            shadowed.add(3)
        if not 4 in current_pattern:
            shadowed.add(7)
        if not 5 in current_pattern:
            shadowed.add(9)
    if 3==current_point:
        if not 2 in current_pattern:
            shadowed.add(1)
        if not 6 in current_pattern:
            shadowed.add(9)
        if not 5 in current_pattern:
            shadowed.add(7)
    if 7==current_point:
        if not 8 in current_pattern:
            shadowed.add(9)
        if not 4 in current_pattern:
            shadowed.add(1)
        if not 5 in current_pattern:
            shadowed.add(3)
    if 9==current_point:
        if not 8 in current_pattern:
            shadowed.add(7)
        if not 6 in current_pattern:
            shadowed.add(3)
        if not 5 in current_pattern:
            shadowed.add(1)
           
    if 2==current_point:
        if not 5 in current_pattern:
            shadowed.add(8)
    if 4==current_point:
        if not 5 in current_pattern:
            shadowed.add(6)
    if 6==current_point:
        if not 5 in current_pattern:
            shadowed.add(4)
    if 8==current_point:
        if not 5 in current_pattern:
            shadowed.add(2)
           
    return shadowed

def get_all_patterns(min_length: int):
    all_patterns = []
    max_length = 9
    for i in range(min_length, max_length+1):
        get_patterns_of_length(i)

def get_patterns_of_length(length: int):
    for i in get_usable_points([]):
        pattern = [i]
        recursively_get_patterns(pattern, length)
       
def recursively_get_patterns(current_pattern: List[int], target_length: int) -> None:
    if len(current_pattern) < target_length:
        for i in get_usable_points(current_pattern):
            new_pattern = deepcopy(current_pattern)
            new_pattern.append(i)
            recursively_get_patterns(new_pattern, target_length)
    else:
        all_patterns.append(current_pattern)
        return None



def main():
    get_all_patterns(4)
    print(len(all_patterns))

main()
 
  • Gefällt mir
Reaktionen: Powl_0, n/a und dx1
BeBur schrieb:
Ist halt offensichtlich falsch angewendet. n/a verwendet fast nur einzelne Buchstaben und Abkürzungen. r für Result zu schreiben ist syntaktisch möglich und semantisch nicht falsch aber von der Sache her (= hochwertigen Code produzieren) falsch - um mal nur ein Beispiel zu nennen.
Mal davon abgesehen ist der Linux coding style guide nicht das Maß aller Dinge. Generell kein Maß für irgendwas außerhalb des Kernels, soweit ich weiß.

Das "falsch" zu nennen ist ja erstmal auch nur eine Meinung, bei der nicht klar ist, warum die gültiger sein sollte als eine andere.

Der Unterschied zwischen i und j ist in der Tat sehr gering und das daher vielleicht nicht gut lesbar, aber i und k sind absolut üblich für Schleifenvariablen. Die Variablen n und m sind ja ganz sonnenklar die Dimensionen aus der Aufgabenstellung.

BeBur schrieb:
Mal ganz davon abgesehen, dass es in Java Iteratoren bzw. list-iteration gibt.

Was jetzt Java-Iteratoren mit einer JavaScript-Fragestellung zu tun haben, erschließt sich auch nicht.

xpad.c
 
  • Gefällt mir
Reaktionen: n/a
xpad.c schrieb:
Das "falsch" zu nennen ist ja erstmal auch nur eine Meinung, bei der nicht klar ist, warum die gültiger sein sollte als eine andere.
Habe mich da ziemlich unklar ausgedrückt. Ich meinte damit nur, dass es viele nicht-Schleifenvariablen gibt, die sehr kurze bzw. einstellige Variablennamen haben.

xpad.c schrieb:
Was jetzt Java-Iteratoren mit einer JavaScript-Fragestellung
Ändert nichts an der Aussage, auch bei Javascript gibt es Iteratoren und zugehörige Schleifenkonstrukte.

xpad.c schrieb:
Der Unterschied zwischen i und j ist in der Tat sehr gering und das daher vielleicht nicht gut lesbar
Das würde ich sagen ist eher ein User-Problem der dann einen falschen Font eingestellt hat. Meistens stellen sich solche Fragen aber gar nicht erst, weil man gar nicht so viele generische Schleifenvariablen benötigt.
 
Vielleicht, um dich etwas milder zu stimmen, habe ich anfangs keine Iteratoren verwendet, weil ich noch nicht wusste, ob ich die Indices später noch brauchen werde oder nicht, und diese sind dann als Relikte übrig geblieben

Persönlich finde ich es nicht falsch, Iteratoren nicht zu verwenden, aber enhanced fors sollte man schon einsetzen...

(später muss man natürlich wissen, wie Schleifen auch ohne Iteratoren funktionieren, das ist klar)

BeBur schrieb:
Ich meinte damit nur, dass es viele nicht-Schleifenvariablen gibt, die sehr kurze bzw. einstellige Variablennamen haben.
Damit liegst du richtig... Es wäre besser gewesen, anstatt r res oder sum zu verwenden

Und natürlich ... immer kleine, testbare Funktionen verwenden

(wobei es dafür nach Clean Code aber keinen festen "Komplexitäts"wert gibt, aber 200 Zeilen in einer Funktion wären zum Beispiel zu viel)
 
  • Gefällt mir
Reaktionen: BeBur
n/a schrieb:
Es ist sinnlos und kontraproduktiv, für temporäre, lokale Variablen lange Namen zu wählen, weil sich die Lesbarkeit dadurch verschlechtert... Ähnlich wie bei Anfängern, die jede Zeile mit einem Kommentar versehen

Es kommt drauf an. Wenn es wirklich nur ein Counter ist der irgendwann die Schleife beendet ok (denke so meint es der Linux Guid auch ohne mich jemals mit Linux Code und Style auseinandergesetzt zu haben), wenn aber was damit gemacht wird, sei es in Strings verwurstet wird oder als Array Index benutzt wird, dann ist was sprechenderes deutlich besser lesbar und wieder auffindbar. Such mal im Code nach i oder nach index_of_xaxis_array...

Ich find Deinen Code auch sehr schlecht nachvollziehbar und lesbar und habe auch keine Lust mich damit auseinanderzusetzen was a, n, m, arr etc. sein sollen...

Aber gut, wenns an der Uni so gelehrt wird...
 
Drexel schrieb:
Such mal im Code nach i oder nach index_of_xaxis_array...

Also mal im Ernst, wer im Jahr 2025 keine Entwicklungsumgebung benutzt, bei der man nach verwendeten Variablen suchen kann (auch wenn sie einfach nur mit 'i' benannt wurden), der hat wohl andere Probleme als triviale Variablennamen.

Und zu "index_of_xaxis_array" sei noch der andere Satz aus dem "Linux Coding Style"-Dokument genannt (ja, ich weiß, dass wir nicht von C reden):
C is a Spartan language, and so should your naming be. Unlike Modula-2 and Pascal programmers, C programmers do not use cute names like ThisVariableIsATemporaryCounter. A C programmer would call that variable tmp, which is much easier to write, and not the least more difficult to understand.

xpad.c
 
  • Gefällt mir
Reaktionen: n/a
Drexel schrieb:
Ich find Deinen Code auch sehr schlecht nachvollziehbar und lesbar und habe auch keine Lust mich damit auseinanderzusetzen was a, n, m, arr etc. sein sollen
a kommt doch gar nicht vor...

Mein Gott, ich wollte aus dieser Frage Keinen Clean Code-Wettbewerb machen und gewinnen...

An der Uni ist nicht verständlicher Code übrigens ein Punktabzug ... Es geht darum, lokale Variablenbezeichner (manchmal auch Wegwerfvariablen genannt ) nicht zu kurz und nicht zu lang zu wählen
 
@xpad.c Die Kernel-Menschen haben womöglich gute Gründe für ihren Guide. Außerhalb des Kernels sollte man sich aber nicht an das halten, was die da geschrieben haben. Längere Namen sind in aller Regel zu bevorzugen. Sie sollten keinesfalls "spartanisch" sein und nirgendwo werden längere Variablannamen als "cute" angesehen.
Beispielhaft mal hier der Google Styleguide für javascript, ich habe aber bevor du den Kernel Guide erwähnt hast noch nie gesehen, dass irgendein style guide einer Programmiersprache davon grob abweicht:
Give as descriptive a name as possible, within reason. Do not worry about saving horizontal space as it is far more important to make your code immediately understandable by a new reader. Do not use abbreviations that are ambiguous orunfamiliar to readers outside your project, and do not abbreviate by deletingletters within a word.


errorCount // No abbreviation.
dnsConnectionIndex // Most people know what "DNS" stands for.
referrerUrl // Ditto for "URL".
customerId // "Id" is both ubiquitous and unlikely to be misunderstood.

Disallowed:


n // Meaningless.
nErr // Ambiguous abbreviation.
nCompConns // Ambiguous abbreviation.
wgcConnections // Only your group knows what this stands for.
pcReader // Lots of things can be abbreviated "pc".
cstmrId // Deletes internal letters.
kSecondsPerDay // Do not use Hungarian notation.

Exception: Variables that are in scope for 10 lines or fewer, includingarguments that are not part of an exported API, may use short (e.g. singleletter) variable names.
Und ja, ich sehe die exception. Die erlaubt ggf. kurze Variablennamen wenn der scope klein ist. Das der style guide etwas erlaubt heißt aber noch lange nicht, dass das beliebig in einem code review durch geht, sondern erlaubt etwas Flexibilität in Einzelfällen (z.B. das eine Variable die nur über 3 Zeilen verwendet wird tmp genannt wird). Der erste Satz des Guides sagt ganz klar:
Give as descriptive a name as possible, within reason.
Und nennt auch klar die Gründe dafür, die gelten in aller Regel natürlich auch für Variablen mit kleinem scope:
Do not worry about saving horizontal space as it is far more important to make your code immediately understandable by a new reader.

n/a schrieb:
An der Uni ist nicht verständlicher Code übrigens ein Punktabzug
Dann war das bei euch besser geregelt als bei uns. Gerade in der Einführung in C wurde schlecht lesbarer Code seitens Dozent und Tutoren tendenziell eher als "cool" angesehen. Da gab es keine Überlegungen, ob das irgendwie gut verständlich ist.
 
Zuletzt bearbeitet:
BeBur schrieb:
Und ja, ich sehe die exception. Die erlaubt ggf. kurze Variablennamen wenn der scope klein ist.

Ich sehe schon, wir kommen bei dieser Grundsatzdiskussion nicht zusammen.

Aber, nur zur Erinnerung, der "Scope" hier IST klein. Die Funktionen, bei denen ihr hier über i, j, n, m und r diskutiert, sind 18 bzw. 20 (!) Zeilen lang. Mithin ist das GENAU das, was mit "LOCAL variables" gemeint ist, bei denen eine Verwechslung ausgeschlossen ist und es keinen Sinn hat, irgendwelche sprechenden Namen zu vergeben.

xpad.c
 
Kann bitte noch einmal jemand hierüber schauen?

Die ersten drei Ausgaben sollten stimmen, aber die nächsten/letzten drei nicht... Ich komme da nur auf 10296 Muster für 3x3. Ich glaube, ich habe die Nachbarschaftslogik noch nicht verstanden bzw. wann ein nächster Knoten nicht gewählt werden darf

Irgendwas stimmt nicht

Javascript:
function calcAllPatterns(n) {
  let totalPatterns = 0;
  for (let len = 2; len <= n * n; len++) {
    totalPatterns += calcPatterns1(n, len);
  }
  console.log(totalPatterns);
  return totalPatterns;
}

function calcPatterns(n, minLen) {
  let totalPatterns = calcPatterns1(n, minLen);
  console.log(totalPatterns);
  return totalPatterns;
}

function calcPatterns1(n, len, last = -1, visited = new Set()) {
  if (visited.size == len) {
    return 1;
  }
  let sum = 0;
  if (last == -1) {
    for (let i = 0; i < n * n; i++) {
      visited.add(i); // Start from each cell
      for (let j = 0; j < n * n; j++) {
        if (!visited.has(j) && !hasPredecessor(i, j, n)) {
          visited.add(j);
          sum += calcPatterns1(n, len, i, visited);
          visited.delete(j);
        }
      }
      visited.delete(i);
    }
    return sum;
  }
  for (let i = 0; i < n * n; i++) {
    if (!visited.has(i) && !hasPredecessor(last, i, n)) {
      visited.add(i);
      sum += calcPatterns1(n, len, i, visited);
      visited.delete(i);
    }
  }
  return sum;
}

function hasPredecessor(origin, target, n) {
  let x1 = parseInt(origin / n);
  let y1 = origin % n;
  let x2 = parseInt(target / n);
  let y2 = target % n;
  if (x1 == x2) {
    return Math.abs(y1 - y2) > 1; // Same row, not adjacent columns
  }
  if (y1 == y2) {
    return Math.abs(x1 - x2) > 1; // Same column, not adjacent rows
  }
  return Math.abs(x1 - x2) > 1 || Math.abs(y1 - y2) > 1; // Diagonal adjacency check
}

calcPatterns(2, 2); // Example for 2x2 grid with min length 2 -> 12
calcPatterns(2, 3); // Example for 2x2 grid with min length 3 -> 24
calcPatterns(3, 2); // Example for 3x3 grid with min length 2
// 12 14 15 21 23 24 25 26 32 35
// 36 41 42 45 47 48 51 52 53 54
// 56 57 58 59 62 63 65 68 69 74
// 75 78 84 85 86 87 89 95 96 98 -> 40
calcPatterns(3, 3); // -> 160 ?
calcAllPatterns(2); // -> 60 ?
calcAllPatterns(3); // -> 10296 ?
 
  • Gefällt mir
Reaktionen: BeBur
n/a schrieb:
@BeBur Du solltest auch auf dem Schirm haben, dass JS oft minifiziert und obfuskiert wird, also wir sind noch nicht am Ende der Fahnenstange
Der an den Client ausgelieferte Produktivcode wird minifiziert. Sobald man debuggen (= den Code lesen) möchte schaltet man minifizierung ja aus. Oder meintest du das im Sinne "im Vergleich mit minifiziertem, obfuskiertem Code war das doch richtig gut lesbar ;p"?
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: n/a
n/a schrieb:
Example for 3x3 grid with min length [...] 40
Ein 3x3 Grid mit exakt Länge zwei kommt bei mir auf 56 Möglichkeiten. Dier fehlen bspw. 1-6, 1-8, also die 16 Kombinationen im 30° Winkel, die in #7 angesprochen wurden.

Wenn man dann das ganze auf 4x4 erweitert, darf man übrigens wieder schön aufpassen, weil ja dann tatsächlich bei 30° auch eine Verschattung auftritt. Und wenn man statt auf quadratische Gitter auf rechteckige geht, wird's noch spaßiger. Zumal man sich dann sicher für einen praktischen EInsatz (Entsperrmuster auf einem Gerät) einen Toleranzwinkel oder -abstand überlegen müsste, ab dem man die Knoten auf einer Linie snappt.
 
  • Gefällt mir
Reaktionen: n/a
Zurück
Oben