Java가 10에서 99까지의 모든 숫자의 곱이 0이라고 생각하는 이유는 무엇입니까?


131

다음 코드 블록은 출력을 0으로 제공합니다.

public class HelloWorld{

    public static void main(String []args){
        int product = 1;
        for (int i = 10; i <= 99; i++) {
            product *= i;
        }
        System.out.println(product);
    }
}

왜 이런 일이 발생했는지 설명해 주시겠습니까?


106
정수 오버플로가 발생했을 가능성이 큽니다.
TheLostMind

68
제품의 주요 요소를 고려하면 2약 90 회 나타납니다. 즉, 0이 아닌 출력을 얻으려면 최소 90 비트의 변수가 필요합니다. 32와 64는 둘 다 90보다 작습니다. 모국어보다 큰 정수를 계산하려면 선택한 언어에서 사용할 수있는 큰 정수 클래스를 사용해야합니다.
kasperd

62
기술적으로 이것은 10에서 98까지의 숫자의 곱입니다.
AShelly

45
뭐? 왜이 질문은 질문의 중복으로 폐쇄되었다 의 중복으로 폐쇄되고 문제 ?
Salman A

82
99 문제와 2147483648 aint 1을
받았습니다

답변:


425

각 단계에서 프로그램이 수행하는 작업은 다음과 같습니다.

          1 * 10 =          10
         10 * 11 =         110
        110 * 12 =        1320
       1320 * 13 =       17160
      17160 * 14 =      240240
     240240 * 15 =     3603600
    3603600 * 16 =    57657600
   57657600 * 17 =   980179200
  980179200 * 18 =   463356416
  463356416 * 19 =   213837312
  213837312 * 20 =   -18221056
  -18221056 * 21 =  -382642176
 -382642176 * 22 =   171806720
  171806720 * 23 =  -343412736
 -343412736 * 24 =   348028928
  348028928 * 25 =   110788608
  110788608 * 26 = -1414463488
-1414463488 * 27 =   464191488
  464191488 * 28 =   112459776
  112459776 * 29 = -1033633792
-1033633792 * 30 =  -944242688
 -944242688 * 31 =   793247744
  793247744 * 32 =  -385875968
 -385875968 * 33 =   150994944
  150994944 * 34 =   838860800
  838860800 * 35 =  -704643072
 -704643072 * 36 =   402653184
  402653184 * 37 =  2013265920
 2013265920 * 38 =  -805306368
 -805306368 * 39 = -1342177280
-1342177280 * 40 = -2147483648
-2147483648 * 41 = -2147483648
-2147483648 * 42 =           0
          0 * 43 =           0
          0 * 44 =           0
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
          0 * 97 =           0
          0 * 98 =           0

일부 단계에서 곱셈은 더 작은 숫자 (980179200 * 18 = 463356416) 또는 잘못된 부호 (213837312 * 20 = -18221056)를 초래하여 정수 오버플로가 있음을 나타냅니다. 그러나 0은 어디에서 오는가? 읽어.

것을 염두에두고 int데이터 타입이 서명 한 32 비트이며 , 2의 보수 정수, 여기에 각 단계에 대한 설명입니다 :

Operation         Result(1)     Binary Representation(2)                                           Result(3)
----------------  ------------  -----------------------------------------------------------------  ------------
          1 * 10            10                                                               1010            10
         10 * 11           110                                                            1101110           110
        110 * 12          1320                                                        10100101000          1320
       1320 * 13         17160                                                    100001100001000         17160
      17160 * 14        240240                                                 111010101001110000        240240
     240240 * 15       3603600                                             1101101111110010010000       3603600
    3603600 * 16      57657600                                         11011011111100100100000000      57657600
   57657600 * 17     980179200                                     111010011011000101100100000000     980179200
  980179200 * 18   17643225600                               100 00011011100111100100001000000000     463356416
  463356416 * 19    8803771904                                10 00001100101111101110011000000000     213837312
  213837312 * 20    4276746240                                   11111110111010011111100000000000     -18221056
  -18221056 * 21    -382642176  11111111111111111111111111111111 11101001001100010101100000000000    -382642176
 -382642176 * 22   -8418127872  11111111111111111111111111111110 00001010001111011001000000000000     171806720
  171806720 * 23    3951554560                                   11101011100001111111000000000000    -343412736
 -343412736 * 24   -8241905664  11111111111111111111111111111110 00010100101111101000000000000000     348028928
  348028928 * 25    8700723200                                10 00000110100110101000000000000000     110788608
  110788608 * 26    2880503808                                   10101011101100010000000000000000   -1414463488
-1414463488 * 27  -38190514176  11111111111111111111111111110111 00011011101010110000000000000000     464191488
  464191488 * 28   12997361664                                11 00000110101101000000000000000000     112459776
  112459776 * 29    3261333504                                   11000010011001000000000000000000   -1033633792
-1033633792 * 30  -31009013760  11111111111111111111111111111000 11000111101110000000000000000000    -944242688
 -944242688 * 31  -29271523328  11111111111111111111111111111001 00101111010010000000000000000000     793247744
  793247744 * 32   25383927808                               101 11101001000000000000000000000000    -385875968
 -385875968 * 33  -12733906944  11111111111111111111111111111101 00001001000000000000000000000000     150994944
  150994944 * 34    5133828096                                 1 00110010000000000000000000000000     838860800
  838860800 * 35   29360128000                               110 11010110000000000000000000000000    -704643072
 -704643072 * 36  -25367150592  11111111111111111111111111111010 00011000000000000000000000000000     402653184
  402653184 * 37   14898167808                                11 01111000000000000000000000000000    2013265920
 2013265920 * 38   76504104960                             10001 11010000000000000000000000000000    -805306368
 -805306368 * 39  -31406948352  11111111111111111111111111111000 10110000000000000000000000000000   -1342177280
-1342177280 * 40  -53687091200  11111111111111111111111111110011 10000000000000000000000000000000   -2147483648
-2147483648 * 41  -88046829568  11111111111111111111111111101011 10000000000000000000000000000000   -2147483648
-2147483648 * 42  -90194313216  11111111111111111111111111101011 00000000000000000000000000000000             0
          0 * 43             0                                                                  0             0
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
          0 * 98             0                                                                  0             0
  1. 는 IS 올바른 결과
  2. 결과의 내부 표현 (64 비트가 설명에 사용됨)
  3. 하위 32 비트의 2의 보수로 표현되는 결과

숫자에 짝수를 곱하면 다음과 같습니다.

  • 비트를 왼쪽으로 이동하고 0 비트를 오른쪽으로 추가
  • 짝수의 결과

따라서 기본적으로 프로그램은 짝수에 다른 숫자를 반복하여 곱하여 오른쪽에서 시작하여 결과 비트를 0으로 만듭니다.

추신 : 곱셈에 홀수 만 포함 된 경우 결과는 0이되지 않습니다.


15
16 진 표현은 여기서 일어나고있는 일에 대해 내 머리를 이해하는 데 도움이되었습니다. 설명해 주셔서 감사합니다!

1
예, 긴 목록의 16 진수 값을 인쇄하도록 프로그램을 수정하면 더 유익 할 것입니다.
핫 릭

4
이 답변은 정확하지만 혼란 많습니다. 마지막 다섯 줄은 그것의 핵심이며, 정확히 어디에서 어떻게 작동하는지 설명하지 않습니다. (하지만 거대한 테이블에서 퍼즐
Rex Kerr

6
다시 말하면, 2의 인수를 누적합니다. 어떤 숫자는 12, 16, 20과 같이 스스로 2의 여러 요소를 나타냅니다. 2의 모든 요소는 모든 후속 결과의 모든 비트를 오른쪽으로 이동하여 0을 그대로 둡니다. 자리 표시 자. 32 번 오른쪽으로 이동하면 32 자리 자리 표시 자 0 만 남게됩니다.
Keen

2
기본 10에서도 비슷한 효과를 볼 수 있습니다. 10으로 나눌 수있는 숫자를 곱할 때마다 제품 끝에 최소 하나의 0을 추가하면 0을 제거 할 수 없습니다. 정수를 곱하여 곱을 구합니다. 어떤 시점에서, n 번째 최하위 자릿수는 모두 0으로 채워지며 모듈로 10 ** m에 산술을 수행하는 경우 (m 번째 최하위 숫자를 제외한 모든 것을 자르는 효과가 있음), 그러면 0이됩니다. 다른 기지와 마찬가지로.
Lie Ryan

70

컴퓨터 곱셈은 실제로 모듈로 2 ^ 32입니다. 곱하기에 2의 거듭 제곱이 충분히 쌓이면 모든 값이 0이됩니다.

여기에 시리즈에 모든 짝수가 있고, 숫자를 나누는 2의 최대 거듭 제곱과 2의 누적 거듭 제곱이 있습니다.

num   max2  total
10    2     1
12    4     3
14    2     4
16    16    8
18    2     9
20    4    11
22    2    12
24    8    15
26    2    16
28    4    18
30    2    19
32    32   24
34    2    25
36    4    27
38    2    28
40    8    31
42    2    32

최대 42의 곱은 x * 2 ^ 32 = 0 (mod 2 ^ 32)과 같습니다. 2의 거듭 제곱의 순서는 회색 코드와 관련이 있으며 https://oeis.org/A001511로 나타납니다 .

편집 :이 질문에 대한 다른 응답이 불완전한 이유를 보려면 홀수 정수로만 제한된 동일한 프로그램이 모든 오버플로에도 불구하고 0으로 수렴 하지 않는다는 사실을 고려하십시오 .


예이! 마지막으로 정답입니다. 사람들은이 답변에 더 주목해야합니다!
Rex Kerr

이것이 유일한 정답입니다. 다른 모든 사람들은 이유를 설명하지 않습니다 .
Olivier Grégoire

5
@ OlivierGrégoire 동의하지 않습니다. 나는 대답이 정확하고 완벽하게 좋은 설명을한다고 생각합니다. 이것은 더 직접적입니다.
David Z

1
더 많은 사람들이이 답변을 볼 수 있기를 바랍니다. 근본 원인이 여기에 표시됩니다!
lanpa

1
@DavidZ : 합의; 대답은 대부분 정확합니다. 내 게시물은 실제로 내 첫 문장의 "왜"를 다루지 않습니다. 하지만 허용 대답의 마지막은 "왜 제로"에 대한 답변에 가장 가까운 일이지만, 그것은 설명 할 수 없다 "왜 42"- 10과 42 사이의 16 짝수가
user295691

34

정수 오버플로 처럼 보입니다 .

이것 좀 봐

BigDecimal product=new BigDecimal(1);
for(int i=10;i<99;i++){
    product=product.multiply(new BigDecimal(i));
}
System.out.println(product);

산출:

25977982938941930515945176761070443325092850981258133993315252362474391176210383043658995147728530422794328291965962468114563072000000000000000000000

출력은 더 이상 int값이 아닙니다 . 그런 다음 오버플로로 인해 잘못된 값을 얻습니다.

오버플로가 발생하면 최소값으로 돌아가서 계속 진행됩니다. 언더 플로가 발생하면 최대 값으로 돌아가서 계속 진행합니다.

더 많은 정보

편집 .

다음과 같이 코드를 변경하자

int product = 1;
for (int i = 10; i < 99; i++) {
   product *= i;
   System.out.println(product);
}

넣어 :

10
110
1320
17160
240240
3603600
57657600
980179200
463356416
213837312
-18221056
-382642176
171806720
-343412736
348028928
110788608
-1414463488
464191488
112459776
-1033633792
-944242688
793247744
-385875968
150994944
838860800
-704643072
402653184
2013265920
-805306368
-1342177280
-2147483648
-2147483648>>>binary representation is 11111111111111111111111111101011 10000000000000000000000000000000 
 0 >>> here binary representation will become 11111111111111111111111111101011 00000000000000000000000000000000 
 ----
 0

22

정수 오버플로 때문입니다. 많은 짝수를 곱하면 이진수는 많은 후행 0을 얻습니다. 에 대한 후행 0이 32 개가 넘으면로 int롤오버됩니다 0.

이를 시각화하는 데 도움이되도록 오버플로되지 않는 숫자 유형에 대해 16 진수로 곱한 값이 있습니다. 후행 0이 느리게 커지는 방법 int을 확인하고 마지막 8 자리 숫자로 구성된 것을 주목하십시오 . 42 (0x2A)를 곱한 후 32 비트의 모든 int0은 0입니다!

                                     1 (int: 00000001) * 0A =
                                     A (int: 0000000A) * 0B =
                                    6E (int: 0000006E) * 0C =
                                   528 (int: 00000528) * 0D =
                                  4308 (int: 00004308) * 0E =
                                 3AA70 (int: 0003AA70) * 0F =
                                36FC90 (int: 0036FC90) * 10 =
                               36FC900 (int: 036FC900) * 11 =
                              3A6C5900 (int: 3A6C5900) * 12 =
                             41B9E4200 (int: 1B9E4200) * 13 =
                            4E0CBEE600 (int: 0CBEE600) * 14 =
                           618FEE9F800 (int: FEE9F800) * 15 =
                          800CE9315800 (int: E9315800) * 16 =
                         B011C0A3D9000 (int: 0A3D9000) * 17 =
                        FD1984EB87F000 (int: EB87F000) * 18 =
                      17BA647614BE8000 (int: 14BE8000) * 19 =
                     25133CF88069A8000 (int: 069A8000) * 1A =
                    3C3F4313D0ABB10000 (int: ABB10000) * 1B =
                   65AAC1317021BAB0000 (int: 1BAB0000) * 1C =
                  B1EAD216843B06B40000 (int: 06B40000) * 1D =
                142799CC8CFAAFC2640000 (int: C2640000) * 1E =
               25CA405F8856098C7B80000 (int: C7B80000) * 1F =
              4937DCB91826B2802F480000 (int: 2F480000) * 20 =
             926FB972304D65005E9000000 (int: E9000000) * 21 =
           12E066E7B839FA050C309000000 (int: 09000000) * 22 =
          281CDAAC677B334AB9E732000000 (int: 32000000) * 23 =
         57BF1E59225D803376A9BD6000000 (int: D6000000) * 24 =
        C56E04488D526073CAFDEA18000000 (int: 18000000) * 25 =
      1C88E69E7C6CE7F0BC56B2D578000000 (int: 78000000) * 26 =
     43C523B86782A6DBBF4DE8BAFD0000000 (int: D0000000) * 27 =
    A53087117C4E76B7A24DE747C8B0000000 (int: B0000000) * 28 =
  19CF951ABB6C428CB15C2C23375B80000000 (int: 80000000) * 29 =
 4223EE1480456A88867C311A3DDA780000000 (int: 80000000) * 2A =
AD9E50F5D0B637A6610600E4E25D7B00000000 (int: 00000000)

1
이것은 약간 오해의 소지가 있습니다. 왜 0이되는지 정확하게 설명하지만 각 값은 곱셈 후에 32 비트 정수로 유지되므로 각 단계 후에 잘 려야합니다. 답을 쓴 방법은 for 루프가 종료 될 때까지 잘리지 않는다는 것을 의미합니다. 각 단계마다 마지막 8 자리 만 표시하는 것이 좋습니다.
RyNo

@KamikazeScotsman 귀하의 제안에 따라 답변을 개선했습니다. 중복 제로가 적고 32 비트 int 가시성이 향상됩니다.
Tim S.

1
각 단계에서 실제 값과 32 비트 값을 비교하여 +1, 값이 잘리고 있음을 강조 표시합니다.
kwah

14

가운데 어딘가에 0제품이 있습니다. 따라서 전체 제품은 0이됩니다.

귀하의 경우 :

for (int i = 10; i < 99; i++) {
    if (product < Integer.MAX_VALUE)
        System.out.println(product);
    product *= i;
}
// System.out.println(product);

System.out.println(-2147483648 * EvenValueOfi); // --> this is the culprit (Credits : Kocko's answer )

O/P :
1
10
110
1320
17160
240240
3603600
57657600
980179200
463356416
213837312
-18221056
-382642176
171806720
-343412736
348028928
110788608
-1414463488
464191488
112459776
-1033633792
-944242688
793247744
-385875968
150994944
838860800
-704643072
402653184
2013265920
-805306368
-1342177280  --> Multiplying this and the current value of `i` will also give -2147483648 (INT overflow)
-2147483648  --> Multiplying this and the current value of `i` will also give -2147483648 (INT overflow)

-2147483648  ->  Multiplying this and the current value of 'i' will give 0 (INT overflow)
0
0
0

현재 값에 출력 i할 수있는 숫자 를 곱할 때마다0


P : - @KickButtowski 곱하기 숫자는 .. 당신은 왜 알
TheLostMind

@KickButtowski-0에 다른 숫자를 곱하면 오버플로가 어느 시점에서 0을 반환 한 후에도 계속해서 0이됩니다.
Mr Moose

나는했지만 다른 사람들도 배울 수 있도록 더 유익해야한다고 생각합니다
Kick Buttowski

@KickButtowski-답변을 업데이트했습니다. OP 부분을 확인하십시오.
TheLostMind

8
@KickButtowski : 오버플로 랩핑이 2의 거듭 제곱에서 발생하기 때문입니다. 기본적으로 OP는 {10 x 11 x 12 x ... x 98} 모듈로 2 ^ 32를 계산합니다. 해당 곱에서 2의 배수가 32 배 이상으로 나타나기 때문에 결과는 0입니다.
ruakh

12

기존의 많은 답변이 Java 및 디버그 출력의 구현 세부 사항을 가리 키므로 이진 곱셈의 수학을 살펴보고 그 이유를 실제로 살펴 보겠습니다.

@kasperd의 의견은 올바른 방향으로 진행됩니다. 숫자에 직접 곱하지 않고 그 숫자의 주요 요소를 대신한다고 가정하십시오. 많은 수보다 2가 주요 요소입니다. 이진수에서 이것은 왼쪽 이동과 같습니다. commutativity에 의해 우리는 2의 소수를 곱할 수 있습니다. 그것은 우리가 단지 왼쪽 교대를한다는 것을 의미합니다.

이항 곱셈 규칙을 살펴볼 때 1이 특정 숫자 위치를 만드는 유일한 경우는 두 피연산자 값이 모두 1 인 경우입니다.

따라서 왼쪽 시프트의 효과는 결과를 더 곱할 때 1의 가장 낮은 비트 위치가 증가한다는 것입니다.

정수는 최하위 비트 만 포함하기 때문에 소수 인자 2가 결과에서 자주 충분히 cotnain 될 때 모두 0으로 설정됩니다.

곱셈 결과의 부호는 결과 수와 독립적으로 계산 될 수 있기 때문에 2의 보수 표현은이 분석에 중요하지 않습니다. 즉, 값이 오버플로되고 음수가되면 최하위 비트는 1로 표시되지만 곱셈 중에 다시 0으로 처리됩니다.


7

이 코드를 실행하면 내가 얻는 것-

          1 * 10 =          10
         10 * 11 =         110
        110 * 12 =        1320
       1320 * 13 =       17160
      17160 * 14 =      240240
     240240 * 15 =     3603600
    3603600 * 16 =    57657600
   57657600 * 17 =   980179200
  980179200 * 18 =   463356416 <- Integer Overflow (17643225600)
  463356416 * 19 =   213837312
  213837312 * 20 =   -18221056
  -18221056 * 21 =  -382642176
 -382642176 * 22 =   171806720
  171806720 * 23 =  -343412736
 -343412736 * 24 =   348028928
  348028928 * 25 =   110788608
  110788608 * 26 = -1414463488
-1414463488 * 27 =   464191488
  464191488 * 28 =   112459776
  112459776 * 29 = -1033633792
-1033633792 * 30 =  -944242688
 -944242688 * 31 =   793247744
  793247744 * 32 =  -385875968
 -385875968 * 33 =   150994944
  150994944 * 34 =   838860800
  838860800 * 35 =  -704643072
 -704643072 * 36 =   402653184
  402653184 * 37 =  2013265920
 2013265920 * 38 =  -805306368
 -805306368 * 39 = -1342177280
-1342177280 * 40 = -2147483648
-2147483648 * 41 = -2147483648
-2147483648 * 42 =           0 <- produce 0 
          0 * 43 =           0

정수 오버플로 원인-

980179200 * 18 =   463356416 (should be 17643225600)

17643225600 : 10000011011100111100100001000000000 <-Actual
MAX_Integer :     1111111111111111111111111111111
463356416   :     0011011100111100100001000000000 <- 32 bit Integer

0 원인 생성-

-2147483648 * 42 =           0 (should be -90194313216)

-90194313216: 1010100000000000000000000000000000000 <- Actual
MAX_Integer :       1111111111111111111111111111111
0           :      00000000000000000000000000000000 <- 32 bit Integer

6

결국 계산이 오버플로되고 결국 해당 오버플로로 인해 곱이 0이됩니다. 때 product == -2147483648와 발생합니다 i == 42. 이 코드를 사용하여 직접 확인하십시오 (또는 여기 에서 코드를 실행 하십시오 ).

import java.math.BigInteger;

class Ideone {
    public static void main (String[] args) throws java.lang.Exception {
        System.out.println("Result: " + (-2147483648 * 42));
    }
}

0이되면 물론 0으로 유지됩니다. 여기에 (당신이 코드를 실행할 수있는보다 정확한 결과를 몇 가지 코드의 여기가 ) :

import java.math.BigInteger;

class Ideone {
    public static void main (String[] args) throws java.lang.Exception {
        BigInteger p = BigInteger.valueOf(1);
        BigInteger start = BigInteger.valueOf(10);
        BigInteger end = BigInteger.valueOf(99);
        for(BigInteger i = start; i.compareTo(end) < 0; i = i.add(BigInteger.ONE)){
            p = p.multiply(i);
            System.out.println("p: " + p);
        }
        System.out.println("\nProduct: " + p);
    }
}

그것은 42 번째 반복 이전에 (단어의 정확한 의미로) 넘쳐납니다 – 19에서 f (19) <f (18)
user295691

예, 그러나 오버플로는 42 번 반복 될 때까지 0의 곱을 초래하거나 초래하지 않습니다.
Trevor

내가 얻는 것은 "왜"를 다루지 않는다는 것입니다. 왜 누적 제품이 0을 통과합니까? Tim S.의 대답은 그 이유를 보여 주지만 실제 대답은 모듈 식 산술에 있습니다.
user295691

이 질문은 왜 제품이 0을 통과하는지 묻지 않습니다. 코드가 왜 0을 생성하는지 묻습니다. 다시 말해, OP가 모듈 식 산술보다 Java의 역학에 더 관심이 있다고 생각하지만 아마도 틀렸을 것입니다. 내가 누군가의 질문을 잘못 해석 한 것은 이번이 처음이 아닙니다.
Trevor

예를 들어,이 프로그램이 모든 홀수의 곱을 11에서 99까지 가져간 경우 0에 도달 하지 않습니다 . 당신의 대답은 실제로 이것이 일어날 이유를 다루지 않습니다.
user295691

1

정수 오버플로입니다.

int 데이터 형식은 4 바이트 또는 32 비트입니다. 따라서 2 ^ (32-1)-1 (2,147,483,647)보다 큰 숫자는이 데이터 유형에 저장할 수 없습니다. 숫자 값이 올바르지 않습니다.

매우 큰 수의 경우 클래스를 가져 와서 사용하려고합니다. java.math.BigInteger:

BigInteger product = BigInteger.ONE;
for (long i = 10; i < 99; i++) 
    product = product.multiply(BigInteger.valueOf(i));
System.out.println(product.toString());

참고 : 여전히 int 데이터 유형에 비해 너무 크지 만 8 바이트 (2 ^ (64-1)-1 이하의 절대 값) 내에 들어가기에 충분히 작은 숫자 값의 경우 long기본 요소를 사용해야합니다 .

알고리즘 연습 섹션 ( https://www.hackerrank.com/domains/algorithms/warmup ) 과 같은 HackerRank의 연습 문제 (www.hackerrank.com) 에는 방법에 대한 좋은 연습을 제공하는 몇 가지 매우 유용한 질문이 포함되어 있습니다. 사용할 적절한 데이터 유형에 대해 생각하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.