일반적인 FizzBuzz 구현은 % 15에 대한 확인을 사용하고 있습니다. “FizzBuzz”인쇄
이 방식에 잘못된 점이 있거나 더 나은 점이 있으면 알려주시겠습니까?
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)); } } } }
댓글
- 사용중인 버전이 컴퓨터에 최적화되어 있습니다. Winston ‘ 버전은 사람에게 최적화되어 있습니다. 코드를 다시 셔플하고 최적화하는 데 좋은 역할을 할 수있는 컴파일러를 신뢰해야합니다. 또한 단일 io 문이 두 개의 개별 문보다 빠르게 작동 할 수 있습니다. 다른 버전과 경쟁해야합니다.
- 제가 ‘ 내 버전이 더 좋은 이유를 알려 드릴 것입니다 (인터뷰 맥락에서만) 1) 유지 관리- Fizz와 Buzz를 문자열로 외부화 할 수 있습니다. ‘ FizzBuzz를 외부화 할 필요가 없습니다. 2) DRY, 위 코드를 작성하는 사용자는 자신을 반복하지 않는다고 생각합니다.
- +1하지만 100 * System.out.println (..)이 아닌 StringBuilder 를 사용하고 Integer.valueOf (i) 를 사용합니다.
- @ cl-r 감사합니다. 의견이 여전히 이해 될 수 있도록 원본에 보관하겠습니다. 피드백에 감사드립니다.
- FizzBuzzEnterpriseEdition이 최고의 구현입니다. github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition ; D
답변
사용중인 버전을 % 15
버전과 비교해 보겠습니다.
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
버전은 sim입니다. pler하고 읽기 쉽습니다. 이 버전은 문제를 4 가지 사례로 깔끔하게 묘사하고 각 사례를 처리합니다. 대조적으로, 귀하의 버전은 부울 논리 플래그 (내가 중요한 안티 패턴이라고 생각 함)를 도입하고 if 문의 순서에 완전히 직관적이지 않은 종속성을 도입합니다.
댓글
- 15는 ” 매직 넘버입니다. “. 대신 ‘
(3 * 5)
를 사용하는 것이 좋습니다. 컴파일러는이를 15로 변환해야합니다 … - @Ron-이 기준에 따라
"FizzBuzz"
도 마찬가지입니다. 당신이 ‘ 반드시 틀렸다 는 것이 아니라 이러한 유형의 ‘ 퀴즈 ‘ 질문, 모든 것을 선언해야하는 필요성은 일반적으로 약간 완화됩니다. 예를 들어 엄밀히 말하면 프로덕션 수준 코드의 경우 모든 여기서 참조 / 인쇄 된 숫자와 문자열은 명명 된 상수로 캡처되어야합니다. - @ X-Zero, 캡처 하시겠습니까? 3을 상수라고 부르시겠습니까?
- @Winston-일반적인 문제는 ‘ 기본적으로 ‘ 조작 된 ‘ 예제이므로 좋은 이름을 얻기 어려울 수 있습니다. 특히 테스트에 포함 된 것은 본질적으로 ‘ 여러 개의 ‘ 검사입니다. 계속해서 분해하면 일종의 배열 / 테이블 구조가 될 수 있습니다. (예 … 알아요 … 우주 건축으로 향합니다). 그렇다면
MATCH
일까요?CONDITION
? 어흐. 이것이 바로 이러한 종류의 테스트에 대해 이러한 종류의 규칙이 완화 된 이유입니다. 테스트는 기본 코딩 능력에 대한 것이지 (필수적으로) 완전한 추상화 기술이 아닙니다. - 가능한 경우 if-for-15를 if-for-5 내부로 이동합니다. ‘ 숫자를 3으로 나누지 말고 ‘ 15로 나누지 않고 모든 숫자에 대한 검사의 4/5를 절약 할 수 있습니다.
답변
괜찮아 보입니다. String.valueOf()
는 불필요합니다. System.out.println(i)
는 동일하게 인쇄하지만 여전히 괜찮습니다. 이 테스트는 인터뷰 대상자가 링크 된 사이트에 나와있는대로 코드를 작성할 수 있는지 확인하는 데만 사용됩니다.
이런 종류의 질문은 훌륭한 프로그래머를 식별하지 못합니다. 그러나 그것은 약한 것을 식별 할 것입니다. 그리고 이는 확실히 올바른 방향으로 나아가는 단계입니다.
답변
그들은 확실히 완벽하지는 않지만 테스트를 최적화하는 몇 가지 방법을 시도했습니다. 여기 결과 (좋은 응답을 추적 할 수있는 숫자가 있습니다). StringBuilder를 사용하여 출력 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; } } }
및 출력 :
------ 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
댓글
- StringBuilder는
char[16]
를 기반으로합니다. 초기 용량을 만들려는 문자열보다 큰 값으로 설정해야합니다. 그렇지 않으면 StringBuilder가 커져야하므로 성능이 저하됩니다. (새char[]
할당, 이전 데이터 복사). [System.nanoTime()
] (stas-blogspot.blogspot.com /2012/02/what-is-behind-systemnanotime.html) 작동하고 문제가 있습니다.그리고 ‘ 더 많은 반복 작업을 수행하도록 조언합니다. 지금은 주로System.out.println()
에 대한 시간을 측정한다고 생각합니다. 다른 작업은 시스템 호출에 비해 시간이 거의 걸리지 않을 것입니다. - 말해보세요. 오버 엔지니어링 …이라는 개념에 대해 들어 보셨나요?
- @Max 그렇습니다.하지만이 사이트는 일부 연구를위한 것입니까? 편안한 눈을위한 것입니까, 아니면 어떤 방법이 더 나은지 이해하려고 노력합니까? 따라서 더 빠르고 읽기 쉬운 코드를 선택하고 비교하거나 코드를 조사하여 누락 된 최적화를 찾을 수 있습니다.
- 최적화 규칙 을 참조하세요. . IMHO Winston Ewert가 최고의 답변을 제공했습니다.
- withTable 코드로 수학을 설명해주세요.
답변
여기에 “(IMHO) 버전이 있습니다. 귀하보다 사람에게 조금 더 좋고 컴퓨터에도 더 좋습니다.
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
당 하나의 System.out.println
호출 만 있습니다.
편집 : 이것은 윙윙 거리는 또 다른 방법입니다 (초점 : 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); } } }
편집 2 : 또 다른 , 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); } }
댓글
- StringBuilder가 더 빠른 접근 방식 인 것 같습니다. 이 경우 + =보다
- @EranMedan-확실합니까?
StringBuilder
는N
가 추가되기 때문에 좋습니다. 하지 않습니다
N
할당으로 이어지지는 않지만 여기서N
는 최대 2 개입니다. ‘ 차이를 만들지 않을만큼 충분히 작은 숫자이고 ‘ 1 일 가능성이 매우 높습니다. 즉, 모두 (또는 더 나쁠 수 있습니다.StringBuilder
는 아마도 해당 1 개의 추가에 대해 추가 할당을 수행하기 때문입니다).
+=
는 ” 시끄러운 ” (Integer.toString()
)이며 ‘ 최대 2 개의 연결에서 ‘만큼 큰 차이를 만들지 않았습니다. . Btw .: 나는 ‘ 다음과 같이 StringBuilder를 사용했을 것입니다. 루프 전에 선언하고, 8
로 설정된 용량으로 초기화, 삭제 및 재사용 되풀이. 루프 내부에 생성하면 ‘별로 개선되지 않습니다. StringBuilder
를 사용하는 다른 버전은 위를 참조하세요. System.out.println(...)
에 대한 한 번의 호출.