Limplementazione FizzBuzz comune che ho visto utilizza un controllo per% 15 per stampare “FizzBuzz”
Mi faresti sapere se cè qualcosa di sbagliato / migliore in questo approccio?
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)); } } } }
Commenti
- La tua versione è ottimizzata per il computer. La versione di Winston ‘ è ottimizzata per gli umani. Devo fidarmi di un compilatore che può fare un buon lavoro nel rimescolare e ottimizzare il codice. Inoltre, è possibile che una singola istruzione io funzioni più velocemente di due separate. Dovresti gareggiare con la tua versione contro laltra.
- Io ‘ ti dirò perché mi piace di più la mia versione (solo per il contesto dellintervista) 1) manutenibilità – puoi esternare Fizz e Buzz come stringhe, non ‘ bisogno di esternare FizzBuzz 2) DRY, un utente che scrive il codice sopra sta pensando in termini di non ripetersi, cosa che mi piace
- +1 ma, usa StringBuilder e non 100 * System.out.println (..) e Integer.valueOf (i)
- @ cl-r grazie, lo manterrò alloriginale così i commenti avranno ancora senso, grazie per il feedback
- FizzBuzzEnterpriseEdition è la migliore implementazione che ci sia: github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition ; D
Risposta
Confrontiamo la tua versione con la % 15
versione:
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)); } } } }
% 15
la versione è sim più semplice e facile da leggere. Questa versione delinea chiaramente il problema in 4 diversi casi e gestisce ogni caso. Al contrario, la tua versione introduce un flag logico booleano (che considero un anti-pattern significativo) e una dipendenza non del tutto intuitiva dallordine delle istruzioni if.
Commenti
- 15 è un ” numero magico ” qui. ‘ suggerirei di utilizzare
(3 * 5)
. Il compilatore dovrebbe convertirlo in 15 … - @Ron – Quindi
"FizzBuzz"
, secondo questo criterio. Non che tu ‘ sbagliato necessariamente, ma per questi tipi di ‘ curiosità ‘ domande, la necessità di dichiarare tutto di solito è leggermente rilassata. Ad esempio, in senso stretto, per il codice a livello di produzione, tutti i numeri e le stringhe a cui si fa riferimento / stampati qui dovrebbero essere catturati come costanti con nome. - @ X-Zero, cattureresti il 3 come costante, come lo chiameresti?
- @Winston – Il problema generale con questo è che ‘ è fondamentalmente un ‘ inventato ‘ esempio, quindi può essere difficile ottenere nomi validi per questo. Soprattutto perché ciò che il test ha è essenzialmente ‘ più ‘ controlli – continuare a scomporre questo probabilmente risulterebbe in una sorta di struttura di matrice / tabella (sì … lo so … andando verso larchitettura dello spazio esterno). Quindi,
MATCH
forse?CONDITION
? Urgh. Ecco perché questi tipi di convenzioni sono rilassati per questi tipi di test; il test è per labilità di codifica di base, non (necessariamente) completa capacità di astrazione. - Vorrei spostare if-for-15 allinterno if-for-5, se puoi ‘ t divide un numero per 3, certamente può ‘ t dividere per 15, salvo 4/5 di controlli per tutti i numeri
Risposta
Sembra a posto. String.valueOf()
non è necessario, System.out.println(i)
stampa lo stesso ma è ancora OK. Questo test viene utilizzato solo per assicurarsi che lintervistato possa scrivere il codice come dice il sito collegato:
Questo tipo di domanda non identifica i grandi programmatori, ma identificherà i deboli. E questo è sicuramente un passo nella giusta direzione.
Answer
Loro non sono certo perfetti, ma ho provato alcuni modi per ottimizzare il test, qui il risultato (ho avuto numeri per tenere traccia di buone risposte), e utilizzo uno StringBuilder per evitare linizializzazione dellIO di output:
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; } } }
E loutput:
------ 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
Commenti
- StringBuilder si basa su un
char[16]
. Dovresti impostare la capacità iniziale su un valore maggiore della stringa che desideri creare o peggiorare le prestazioni, poiché StringBuilder deve crescere (alloca il nuovochar[]
, copia i vecchi dati). Dovresti anche essere consapevole di come [System.nanoTime()
] (stas-blogspot.blogspot.com /2012/02/what-is-behind-systemnanotime.html) funziona e i problemi che ha.E ti ‘ ti consiglio di fare molte più iterazioni. Penso che tu misuri principalmente il tempo perSystem.out.println()
in questo momento, le altre operazioni probabilmente richiedono pochissimo tempo rispetto a una chiamata di sistema. - Dimmi, hai mai sentito parlare del concetto di over-engineering …?
- @Max lo faccio, ma questo sito è fatto per alcune ricerche o no? È per occhi comodi o per cercare di capire come alcuni modi siano migliori? In questo modo puoi scegliere e confrontare il più veloce, più facile da leggere o interrogare il codice per trovare lottimizzazione mancante.
- Vedi le Regole per lottimizzazione . IMHO Winston Ewert ha fornito la risposta migliore.
- spiegare la matematica con il codice della tabella per favore?
Risposta
Ecco “una versione che (IMHO) è un po migliore per gli esseri umani della tua e anche per i 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); } } }
I commenti forniscono informazioni agli umani (ma potrebbero ancora vederlo da soli) e cè solo una System.out.println
chiamata per i
.
MODIFICA: Questo è un altro modo per fare il ronzio (focus: 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); } } }
MODIFICA 2: ancora un altro, utilizzando StringBuilder
, anche 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); } }
Commenti
- StringBuilder sembra un approccio più veloce di + = in questo caso
- @EranMedan – Sei sicuro di questo?
StringBuilder
va bene perchéN
aggiunge non
t porta aN
allocazioni, tuttavia quiN
è al massimo 2, che è un numero abbastanza piccolo da vincere ‘ per fare la differenza, ed è molto probabile che ‘ sia 1, il che significa nessuna differenza tutto (o forse peggio, dato cheStringBuilder
probabilmente fa unallocazione extra per quellappendice).
+=
è meno ” rumoroso ” (anche il motivo per cui ho evitato Integer.toString()
) e ‘ non farà molta differenza in quanto ‘ al massimo due concatenazioni . Btw .: I ‘ d ho utilizzato StringBuilder in questo modo: dichiara prima del ciclo, inizializza con capacità impostata su 8
, cancellato e riutilizzato per iterazione. Crearlo allinterno del ciclo non ‘ migliorerebbe molto. StringBuilder
– ma solo una chiamata a System.out.println(...)
.