Die häufig verwendete FizzBuzz -Implementierung verwendet eine Prüfung auf% 15 für Drucken von „FizzBuzz“
Würden Sie mich wissen lassen, ob bei diesem Ansatz etwas falsch / besser ist?
public class FizzBuzz { public static void main(String[] args) { for (int i = 1; i <= 100; i++) { boolean fizzOrBuzz = false; if (i % 3 == 0) { System.out.print("Fizz"); fizzOrBuzz = true; } if (i % 5 == 0) { System.out.print("Buzz"); fizzOrBuzz = true; } if (fizzOrBuzz) { System.out.println(); } else { System.out.println(String.valueOf(i)); } } } }
Kommentare
- Ihre Version ist für den Computer optimiert. Die Version von Winston ‚ ist für den Menschen optimiert. Ich muss einem Compiler vertrauen, dass er beim erneuten Mischen und Optimieren des Codes gute Arbeit leisten kann. Es ist auch möglich, dass eine einzelne io-Anweisung schneller funktioniert als zwei separate. Sie sollten Ihre Version gegen die andere antreten.
- Ich ‚ werde Ihnen sagen, warum mir meine Version besser gefällt (nur für den Interviewkontext) 1) Wartbarkeit – Sie können Fizz und Buzz als Zeichenfolgen externalisieren. Sie müssen ‚ FizzBuzz nicht externalisieren. 2) DRY, ein Benutzer, der den obigen Code schreibt, denkt daran, sich nicht zu wiederholen, was mir gefällt
- +1 Verwenden Sie jedoch einen StringBuilder und nicht 100 * System.out.println (..) und Integer.valueOf (i)
- @ cl-r danke, ich werde es beim Original behalten, damit die Kommentare immer noch Sinn machen. Danke für das Feedback.
- FizzBuzzEnterpriseEdition ist die beste Implementierung, die es gibt: github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition ; D
Antwort
Vergleichen wir Ihre Version mit der % 15
-Version:
public class FizzBuzz { public static void main(String[] args) { for (int i = 1; i <= 100; i++) { if (i % 15 == 0) { System.out.println("FizzBuzz") } else if (i % 3 == 0) { System.out.println("Fizz"); } else if (i % 5 == 0) { System.out.println("Buzz"); } else { System.out.println(String.valueOf(i)); } } } }
Die % 15
Version ist sim einfacher und leichter zu lesen. Diese Version beschreibt das Problem in 4 verschiedenen Fällen und behandelt jeden Fall. Im Gegensatz dazu führt Ihre Version ein boolesches Logik-Flag (das ich als signifikantes Anti-Pattern betrachte) und eine nicht ganz intuitive Abhängigkeit von der Reihenfolge der if-Anweisungen ein.
Kommentare
- 15 ist eine “ magische Zahl “ hier. Ich ‚ würde vorschlagen, stattdessen
(3 * 5)
zu verwenden. Der Compiler sollte es nach diesen Kriterien in 15 konvertieren … - @Ron –
"FizzBuzz"
. Nicht dass Sie ‚ notwendigerweise falsch sind, aber für diese Arten von ‚ Trivia ‚ Fragen, die Notwendigkeit, alles zu deklarieren, wird normalerweise etwas gelockert. Genau genommen sollten für Code auf Produktionsebene alle alle Zahlen und Zeichenfolgen, auf die hier verwiesen / gedruckt wird, als benannte Konstanten erfasst werden. - @ X-Zero, würden Sie erfassen Die 3 als Konstante, wie würden Sie sie nennen?
- @Winston – Das allgemeine Problem dabei ist, dass ‚ im Grunde eine hat ein ‚ Beispiel erfunden, daher kann es schwierig sein, gute Namen dafür zu finden. Insbesondere da der Test im Wesentlichen ‚ mehrere ‚ Überprüfungen ist – eine weitere Zerlegung würde wahrscheinlich zu einer Art Array- / Tabellenstruktur führen (Ja … ich weiß … auf dem Weg zur Weltraumarchitektur). Also,
MATCH
vielleicht?CONDITION
? Urgh. Aus diesem Grund werden diese Konventionen für diese Art von Tests gelockert. Der Test bezieht sich auf grundlegende Codierungsfähigkeiten und nicht (notwendigerweise) auf vollständige Abstraktionsfähigkeiten. - Ich würde if-for-15 in if-for-5 verschieben, wenn Sie ‚ eine Zahl nicht durch 3 teilen, kann sicherlich ‚ nicht durch 15 teilen, 4/5 Schecks für alle Zahlen
speichern
Antwort
Es sieht gut aus. String.valueOf()
ist nicht erforderlich, System.out.println(i)
würde dasselbe drucken, aber es ist immer noch in Ordnung. Dieser Test wird nur verwendet, um sicherzustellen, dass der Befragte Code schreiben kann, wie auf der verlinkten Site angegeben:
Diese Art von Frage identifiziert keine großartigen Programmierer. aber es wird die Schwachen identifizieren. Und das ist definitiv ein Schritt in die richtige Richtung.
Antwort
Sie sind sicherlich keine Perfekte, aber ich habe einige Möglichkeiten ausprobiert, um den Test zu optimieren, hier das Ergebnis (ich hatte Zahlen, um gute Antworten zu verfolgen), und ich verwende einen StringBuilder, um die Initialisierung der Ausgabe-E / A zu vermeiden:
package exercices; import java.util.Hashtable; import org.memneuroo.outils.communs.utilitaires.EnvPrm; public class FizzBuzz { // time for cum with nbIter=30 > 300; 30 ~= 3000 static final int nbIter = 30; static final String sep = "_"; static long ifNested() { final StringBuilder sb = new StringBuilder(); final long t = System.nanoTime(); for (int i = 0; i < nbIter; i++) { sb.append(// i % 15 == 0 // ? "FizzBuzz" // : (i % 3 == 0 // ? "Fizz"// : (i % 5 == 0// ? "Buzz" // : i)));// sb.append(sep); } final long totT = System.nanoTime() - t; System.out.format("ifNested\t%20d\n", totT); // sb.append(EnvPrm.NEWLINE); System.out.println(sb.toString()); return totT; } static long stringPlus() { final StringBuilder sb = new StringBuilder(); final long t = System.nanoTime(); for (int i = 0; i < nbIter; i++) { String x = ""; x += (i % 3 == 0) ? "Fizz" : ""; x += (i % 5 == 0) ? "Buzz" : ""; if (x.isEmpty()) { // MODIF x += Integer.toString(i); } sb.append(x);// sb.append(sep); } final long totT = System.nanoTime() - t; System.out.format("stringPlus\t%20d\n", totT); // sb.append(EnvPrm.NEWLINE); System.out.println(sb.toString()); return totT; } static long withIf() { final StringBuilder sb = new StringBuilder(); final long t = System.nanoTime(); for (int i = 0; i < nbIter; i++) { if (i % 3 == 0) { sb.append("Fizz"); if (i % 5 == 0) { sb.append("Buzz"); } } else if (i % 5 == 0) { sb.append("Buzz"); } else { sb.append(i); }// sb.append(sep); } final long totT = System.nanoTime() - t; System.out.format("withIf\t\t%20d\n", totT); // sb.append(EnvPrm.NEWLINE);System.out.println(sb.toString()); return totT; } static long withArray() { final String[] lis = {"FizzBuzz", "", "", "Fizz", "", "Buzz", "Fizz", "", "", "Fizz", "Buzz", "", "Fizz", "", "",}; final StringBuilder sb = new StringBuilder(); final long t = System.nanoTime(); for (int i = 0; i < nbIter; i++) { final String pos = lis[i % 15]; sb.append(((0 == pos.length()) ? i : pos));// sb.append(sep); } final long totT = System.nanoTime() - t; System.out.format("withArray\t%20d\n", totT); // sb.append(EnvPrm.NEWLINE); System.out.println(sb.toString()); return totT; } static long withTable() { final Hashtable<Integer, String> ht = new Hashtable<>(8); ht.put(0, "FizzBuzz"); ht.put(3, "Fizz"); ht.put(5, "Buzz"); ht.put(6, "Fizz"); ht.put(9, "Fizz"); ht.put(10, "Buzz"); ht.put(12, "Buzz"); final StringBuilder sb = new StringBuilder(); final long t = System.nanoTime(); for (int i = 0; i < nbIter; i++) { final String s = ht.get(i % 15); // MODIF // http://www.developpez.net/forums/d1196563-2/java/general-java/if-null-object-if-objet-null/#post6561766 // sb.append((null == s ? i : s));// sb.append(sep); if (null == s) { sb.append(i); } else { sb.append(s); } } final long totT = System.nanoTime() - t; System.out.format("withTable\t%20d\n", totT); // sb.append(EnvPrm.NEWLINE); System.out.println(sb.toString()); return totT; } static int recursive(final StringBuilder sb, final int n) { if (0 == n) { return 1; } if (n % 3 == 0) { sb.insert(0, "Fizz"); if (n % 5 == 0) { sb.insert(0, "Buzz"); } } else if (n % 5 == 0) { sb.insert(0, "Buzz"); } else { sb.insert(0, n); } return n + recursive(sb, n - 1); } static long recursive() { final StringBuilder sb = new StringBuilder(""); final long t = System.nanoTime(); recursive(sb, nbIter); final long totT = System.nanoTime() - t; System.out.format("recursive\t%20d\n", totT); sb.append(EnvPrm.NEWLINE); System.out.println(sb.toString()); return totT; } /*** @param args */ public static void main(final String[] args) { long cum = 0L, cum2 = 0L; for (int i = 0; i < 5; i++) { System.out.println("------ " + i + " -----"); final long totSb = stringPlus(); final long totIn = ifNested(); final long totWi = withIf(); final long totWa = withArray(); final long totWt = withTable(); final long totRe = recursive(); System.out.format("... stringPlus/withIf :%5d\n", (totSb * 100) / totWi); System.out.format("... ifNested/withIf :%5d\n", (totIn * 100) / totWi); System.out.format("... withArray/withIf :%5d\n", (totWa * 100) / totWi); System.out.format("... withTable/withIf :%5d\n", (totWt * 100) / totWi); System.out.format("... recursive/withIf :%5d\n", (totRe * 100) / totWi); cum += totIn + totSb + totWi + totWa + totWt + totRe; System.out.println("CUMUL (SECOND) == " + cum / 100000000 + "." + cum % 100000000 + "\t , diff: " + (cum - cum2)); cum2 = cum; } } }
Und die Ausgabe:
------ 0 ----- stringPlus 529397 ifNested 643657 withIf 27657 withArray 43581 withTable 40788 recursive 87441 12Fizz4BuzzFizz78FizzBuzz11Fizz1314BuzzFizz1617Fizz19BuzzFizz2223FizzBuzz26Fizz2829BuzzFizz ... stringPlus/withIf : 1914 ... ifNested/withIf : 2327 ... withArray/withIf : 157 ... withTable/withIf : 147 ... recursive/withIf : 316 CUMUL (SECOND) == 0.1372521 , diff: 1372521 ------ 1 ----- stringPlus 345295 ifNested 88280 withIf 88279 withArray 88838 withTable 101689 recursive 93308 12Fizz4BuzzFizz78FizzBuzz11Fizz1314BuzzFizz1617Fizz19BuzzFizz2223FizzBuzz26Fizz2829BuzzFizz ... stringPlus/withIf : 391 ... ifNested/withIf : 100 ... withArray/withIf : 100 ... withTable/withIf : 115 ... recursive/withIf : 105 CUMUL (SECOND) == 0.2178210 , diff: 805689 ------ 2 ----- stringPlus 380216 ifNested 36597 withIf 20953 withArray 60063 withTable 91352 recursive 111467 12Fizz4BuzzFizz78FizzBuzz11Fizz1314BuzzFizz1617Fizz19BuzzFizz2223FizzBuzz26Fizz2829BuzzFizz ... stringPlus/withIf : 1814 ... ifNested/withIf : 174 ... withArray/withIf : 286 ... withTable/withIf : 435 ... recursive/withIf : 531 CUMUL (SECOND) == 0.2878858 , diff: 700648 ------ 3 ----- stringPlus 489168 ifNested 29613 withIf 22070 withArray 27099 withTable 27378 recursive 91911 12Fizz4BuzzFizz78FizzBuzz11Fizz1314BuzzFizz1617Fizz19BuzzFizz2223FizzBuzz26Fizz2829BuzzFizz ... stringPlus/withIf : 2216 ... ifNested/withIf : 134 ... withArray/withIf : 122 ... withTable/withIf : 124 ... recursive/withIf : 416 CUMUL (SECOND) == 0.3566097 , diff: 687239 ------ 4 ----- stringPlus 143035 ifNested 24025 withIf 15924 withArray 23187 withTable 26819 recursive 87162 12Fizz4BuzzFizz78FizzBuzz11Fizz1314BuzzFizz1617Fizz19BuzzFizz2223FizzBuzz26Fizz2829BuzzFizz ... stringPlus/withIf : 898 ... ifNested/withIf : 150 ... withArray/withIf : 145 ... withTable/withIf : 168 ... recursive/withIf : 547 CUMUL (SECOND) == 0.3886249 , diff: 320152
Kommentare
- StringBuilder basiert auf einem
char[16]
. Sie sollten die Anfangskapazität auf einen Wert einstellen, der größer ist als der String, den Sie erstellen möchten – oder Sie verschlechtern die Leistung, da StringBuilder wachsen muss (Neuechar[]
zuweisen, alte Daten kopieren). Sie sollten auch wissen, wie [System.nanoTime()
] (stas-blogspot.blogspot.com) /2012/02/what-is-behind-systemnanotime.html) funktioniert und die Probleme, die es hat.Und ich ‚ würde Ihnen raten, viel mehr Iterationen durchzuführen. Ich denke, Sie messen derzeit hauptsächlich die Zeit fürSystem.out.println()
. Die anderen Vorgänge benötigen im Vergleich zu einem Systemaufruf wahrscheinlich nur sehr wenig Zeit. - Sagen Sie mir, haben Sie jemals Ich habe von dem Konzept Over-Engineering gehört …?
- @Max Ich mache, aber wird diese Seite für einige Forschungen erstellt oder nicht? Ist für bequeme Augen oder um zu verstehen, wie manche Wege besser sind? So können Sie den schnelleren, einfach zu lesenden Code auswählen und vergleichen oder ihn abfragen, um fehlende Optimierungen zu finden.
- Siehe Optimierungsregeln . IMHO Winston Ewert lieferte die beste Antwort.
- Erklären Sie Mathe bitte mit Tabellencode?
Antwort
Hier ist eine Version, die (IMHO) für Menschen etwas besser ist als Ihre und auch für Computer:
public class FizzBuzz { public static void main(String[] args) { for (int i = 1; i <= 100; i++) { String value; switch (i % 15) { case 3: case 6: case 9: case 12: // divisible by 3, print Fizz value = "Fizz"; break; case 5: case 10: // divisible by 5, print Buzz value = "Buzz"; break; case 0: // divisible by 3 and by 5, print FizzBuzz value = "FizzBuzz"; break; default: // else print the number value = Integer.toString(i); } System.out.println(value); } } }
Die Kommentare enthalten Informationen für Menschen (aber sie konnten es immer noch selbst sehen) und es gibt nur einen System.out.println
Aufruf pro i
.
BEARBEITEN: Dies ist eine weitere Möglichkeit zum Fizz-Buzz (Fokus: DRY):
public class FizzBuzz { public static void main(String[] args) { final String EMPTY = ""; for (int i = 1; i <= 100; i++) { String value = EMPTY; if (i % 3 == 0) value += "Fizz"; if (i % 5 == 0) value += "Buzz"; if (value == EMPTY) value += i; System.out.println(value); } } }
BEARBEITEN 2: noch eine weitere unter Verwendung von StringBuilder
, auch DRY:
public class FizzBuzz { public static void main(String[] args) { StringBuilder builder = new StringBuilder(1000); for (int i = 1; i <= 100; i++) { final int length = builder.length(); if (i % 3 == 0) builder.append("Fizz"); if (i % 5 == 0) builder.append("Buzz"); if (length == builder.length()) builder.append(i); builder.append("\n"); } System.out.println(builder); } }
Kommentare
- StringBuilder scheint ein schnellerer Ansatz zu sein als + = in diesem Fall
- @EranMedan – Sind Sie sich dessen sicher?
StringBuilder
ist gut, daN
angehängt wird nicht
führt nicht zuN
Zuweisungen, hier istN
jedoch höchstens 2, was a ist klein genug, dass es wahrscheinlich keinen Unterschied macht, ‚, und es ist ziemlich wahrscheinlich, dass ‚ 1 ist, was keinen Unterschied bei bedeutet all (oder vielleicht noch schlimmer, daStringBuilder
wahrscheinlich eine zusätzliche Zuordnung für diesen 1 Anhang vornimmt).
+=
ist weniger “ laut “ (auch der Grund für meine Vermeidung von Integer.toString()
) und es wird ‚ keinen großen Unterschied machen, da ‚ höchstens zwei Verkettungen gibt . Übrigens: Ich ‚ habe StringBuilder wie folgt verwendet: Vor der Schleife deklarieren, mit der auf 8
eingestellten Kapazität initialisieren, löschen und per wiederverwenden Wiederholung. Das Erstellen innerhalb der Schleife würde ‚ nicht wesentlich verbessern. StringBuilder
– aber nur ein Aufruf von System.out.println(...)
.