La implementación común de FizzBuzz que vi está usando una comprobación de% 15 para imprimiendo «FizzBuzz»

¿Me haría saber si hay algo mal / mejor con este enfoque?

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)); } } } } 

Comentarios

  • Su versión está optimizada para la computadora. La versión de Winston ‘ está optimizada para el ser humano. Tengo que confiar en un compilador que puede hacer un buen trabajo al reorganizar y optimizar el código. Además, es posible que una sola instrucción io funcione más rápido que dos separadas. Debes competir con tu versión contra la otra.
  • Yo ‘ te diré por qué me gusta más mi versión (solo para el contexto de la entrevista) 1) mantenibilidad – puedes externalizar Fizz y Buzz como cadenas, no ‘ no necesitas externalizar FizzBuzz 2) DRY, un usuario que escribe el código anterior está pensando en términos de no repetirse, lo cual me gusta
  • +1 pero, use un StringBuilder y no 100 * System.out.println (..), y Integer.valueOf (i)
  • @ cl-r gracias, lo mantendré en el original para que los comentarios aún tengan sentido, gracias por los comentarios
  • FizzBuzzEnterpriseEdition es la mejor implementación que existe: github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition ; D

Respuesta

Comparemos su versión con la % 15 versión:

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)); } } } } 

La % 15 la versión es sim más pálido y más fácil de leer. Esta versión delinea cuidadosamente el problema en los 4 casos diferentes y maneja cada caso. Por el contrario, su versión introduce una bandera lógica booleana (que considero un anti-patrón significativo) y una dependencia no del todo intuitiva del orden de las declaraciones if.

Comentarios

  • 15 es un » número mágico » aquí. Yo ‘ sugiero usar (3 * 5) en su lugar. El compilador debería convertirlo a 15 …
  • @Ron – Así es, "FizzBuzz", según este criterio. No es que ‘ esté equivocado necesariamente, pero para este tipo de ‘ trivia ‘ preguntas, la necesidad de declarar todo suele relajarse un poco. Por ejemplo, estrictamente hablando, para el código de nivel de producción, todos los números y cadenas a los que se hace referencia / impresos aquí deben capturarse como constantes con nombre.
  • @ X-Zero, ¿capturaría el 3 como una constante, ¿cómo lo llamarías?
  • @Winston – El problema general con esto es que ‘ es básicamente un ‘ ‘ ejemplo artificial, por lo que puede ser difícil conseguir buenos nombres para esto. Especialmente porque lo que tiene la prueba es esencialmente ‘ múltiples ‘ comprobaciones; continuar descomponiendo esto probablemente resultaría en algún tipo de estructura de matriz / tabla (sí … lo sé … dirigiéndonos a la arquitectura del espacio exterior). Entonces, MATCH ¿quizás? CONDITION? Urgh. Es por eso que este tipo de convenciones se relajan para este tipo de pruebas; la prueba es para la capacidad básica de codificación, no (necesariamente) habilidades de abstracción total.
  • Me movería if-for-15 dentro if-for-5, si puedes ‘ t dividir un número entre 3, ciertamente no puede ‘ t dividir por 15, guardar 4/5 de los cheques para todos los números

Respuesta

Se ve bien. String.valueOf() no es necesario, System.out.println(i) imprimiría lo mismo pero aún está bien. Esta prueba se usa solo para asegurarse de que el entrevistado pueda escribir código como dice el sitio vinculado:

Este tipo de pregunta no identificará a grandes programadores, pero identificará a los débiles. Y ese es definitivamente un paso en la dirección correcta.

Responder

Ellos ciertamente no son perfectos, pero he intentado algunas formas de optimizar la prueba, aquí el resultado (he tenido números para no perder de vista las buenas respuestas), y utilizo un StringBuilder para evitar la inicialización de la salida IO:

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; } } } 

Y el resultado:

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

Comentarios

  • StringBuilder se basa en un char[16]. Debe establecer la capacidad inicial en un valor mayor que el String que desea crear, o empeorará el rendimiento, ya que StringBuilder tiene que crecer (asigne nuevos char[], copie los datos antiguos). También debe saber cómo [System.nanoTime()] (stas-blogspot.blogspot.com /2012/02/what-is-behind-systemnanotime.html) funciona y los problemas que tiene.Y yo ‘ le aconsejaría que hiciera muchas más iteraciones. Creo que usted mide principalmente el tiempo para System.out.println() en este momento, las otras operaciones probablemente toman muy poco tiempo en comparación con una llamada al sistema.
  • Dígame, ¿alguna vez ¿escuchaste sobre el concepto sobre-ingeniería …?
  • @Max Lo hago, pero ¿este sitio está hecho para algunas investigaciones o no? ¿Es para ojos cómodos o para tratar de entender cómo algunas formas son mejores? Por lo tanto, puede elegir y comparar el código más rápido, el más fácil de leer o interrogar el código para encontrar la optimización faltante.
  • Consulte las Reglas de optimización . En mi humilde opinión, Winston Ewert proporcionó la mejor respuesta.
  • ¿Explique las matemáticas en el código de la tabla, por favor?

Responder

Aquí hay una versión que (en mi humilde opinión) es un poco mejor para los humanos que la suya y también mejor para las computadoras:

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); } } } 

Los comentarios brindan información para los humanos (pero aún podían verlo por sí mismos) y solo hay una System.out.println llamada por i.

EDIT: Esta es otra forma de fizz-buzz (enfoque: 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); } } } 

EDICIÓN 2: otra más, usando StringBuilder, DRY también:

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); } } 

Comentarios

  • StringBuilder parece un enfoque más rápido que + = en este caso
  • @EranMedan – ¿Estás seguro de esto? StringBuilder es bueno porque N agrega no

t dan lugar aNasignaciones, sin embargo, aquíNes como máximo 2, que es un un número lo suficientemente pequeño como para que probablemente no ‘ haga una diferencia, y ‘ es muy probable que sea 1, lo que significa que no hay diferencia en todo (o quizás peor, ya queStringBuilderprobablemente hace una asignación adicional para ese 1 anexo).

  • @asveikau: probablemente tengas razón, supongo que esto será sobre optimización para averiguar exactamente cuál es más rápido 🙂
  • Yo ‘ estoy al tanto de StringBuilder, que utilizo todo el tiempo en el código pesado de cadenas, y pensé bien las líneas de asveikau . += es menos » ruidoso » (también es la razón por la que evito Integer.toString()) y no ‘ no hará una gran diferencia ya que hay ‘ s como máximo dos concatenaciones . Por cierto: ‘ he usado StringBuilder así: declarar antes del ciclo, inicializar con la capacidad establecida en 8, borrar y reutilizar según iteración. Crearlo dentro del bucle no ‘ mejoraría mucho.
  • ver arriba para otra versión usando StringBuilder – pero solo una llamada a System.out.println(...).
  • Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *