source

파이썬이 정수 나눗셈을 음의 무한대로 반올림하기로 선택한 수학적 이유는 무엇입니까?

factcode 2023. 5. 29. 11:12
반응형

파이썬이 정수 나눗셈을 음의 무한대로 반올림하기로 선택한 수학적 이유는 무엇입니까?

나는 파이썬을 알아요.//의 경우 C++의 경우/0을 향해 반올림하여 자릅니다.

제가 지금까지 알고 있는 것은 다음과 같습니다.

               |remainder|
-12 / 10  = -1,   - 2      // C++
-12 // 10 = -2,   + 8      # Python

12 / -10  = -1,     2      // C++
12 // -10 = -2,   - 8      # Python

12 / 10  = 1,      2       // Both
12 // 10 = 1,      2

-12 / -10 = 1,    - 2      // Both
          = 2,    + 8

C++:
1. m%(-n) == m%n
2. -m%n == -(m%n)
3. (m/n)*n + m%n == m

Python:
1. m%(-n) == -8 == -(-m%n)
2. (m//n)*n + m%n == m

하지만 왜 파이썬인가요?//음의 무한대를 향해 반올림하는 것을 선택합니까?저는 그것을 설명하는 어떤 자료도 찾지 못했지만, 사람들이 모호하게 "수학적 이유로"라고 말하는 것을 발견하고 들을 뿐입니다.

예를 들어, C++에서는 -1/2가 0으로 평가되는 이유에서 Python에서는 -1이 평가되는 이유는 무엇입니까?:

추상적으로 이러한 것들을 다루는 사람들은 음의 무한대를 향해 반올림하는 것이 더 말이 된다고 느끼는 경향이 있습니다(그것은 수학에서 정의된 모듈로 함수와 호환된다는 것을 의미합니다, %는 다소 재미있는 의미를 가지고 있습니다).

하지만 C++의 것은 보이지 않습니다./모듈로 기능과 호환되지 않습니다. C++에서는,(m/n)*n + m%n == m또한 적용됩니다.

그렇다면 파이썬이 음의 무한대를 향해 반올림을 선택한 (수학적) 이유는 무엇입니까?


이 주제에 대한 Guido van Rossum의 이전 블로그 게시물도 참조하십시오.

하지만 왜 파이썬인가요?//음의 무한대를 향해 반올림하는 것을 선택합니까?

이 선택이 처음에 이루어진 이유가 어디에 기록되어 있는지는 잘 모르겠지만(내가 아는 한, 일부 PEP에서 매우 자세히 설명될 수 있지만), 우리는 그것이 타당한 이유를 다양하게 제시할 수 있습니다.

한 가지 이유는 단순히 음수(또는 양수!) 무한대를 향해 반올림하는 것은 모든 숫자가 같은 방식으로 반올림된다는 것을 의미하는 반면, 0을 향해 반올림하는 것은 0을 특별하게 만든다는 것입니다.이것을 말하는 수학적 방법은 -π를 향해 반올림하는 것은 변환 불변이다. 즉, 다음 방정식을 만족시킵니다.

round_down(x + k) == round_down(x) + k

x 정수 및 든 정 수 모k예를 들어, 0을 향해 반올림하면 다음과 같은 이유로 반올림하지 않습니다.

round_to_zero(0.5 - 1) != round_to_zero(0.5) - 1

여러분이 (을 바탕으로 도 존재합니다.%연산자(행동하기 위해) — 아래에 자세히 설명합니다.

정말로, 여기서 진짜 질문은 왜 파이썬이int()함수는 음의 무한대를 향해 부동 소수점 인수를 반올림하도록 정의되지 않습니다.m // n과 같음int(m / n) 이유가 의심됩니다.)한편, 파이썬이 적어도 가지고 있기 때문에 그렇게 큰 문제는 아닙니다.math.floor()그것은 만족합니다.m // n == math.floor(m / n).


하지만 C++의 것은 보이지 않습니다./모듈로 기능과 호환되지 않습니다. C++에서는,(m/n)*n + m%n == m또한 적용됩니다.

맞아요, 하지만 그 정체성을 유지하면서도/하려면 0을 정의해야 .%음의 숫자에 대해 어색한 방식으로.특히, 우리는 파이썬의 다음과 같은 유용한 수학적 특성을 모두 잃었습니다.%:

  1. 0 <= m % n < nm 긍정적인 것.n그리고
  2. (m + k * n) % n == m % n 정수에 m,n그리고.k.

이러한 속성은 의 주요 용도 중 하나이기 때문에 유용합니다.% "로위니" "합다이" "wrapping나동" "를자"" "다▁around니" 입니다mn.


예를 들어, 방향을 계산하려고 한다고 가정해 보겠습니다.heading우리의 현재 나침반이 도 단위로 향하고 있습니까(정북에서 시계 방향으로, 그리고0 <= heading < 360) 는 회전 의 새로운 ) 그리고회후우에우리새의로표원다계합를니기산.angle서 도(위))angle > 0만약 우리가 시계방향으로 돌면, 또는angle < 0만약 우리가 시계 반대 방향으로 돌립니다.Python 하기 »%연산자, 우리는 간단하게 다음과 같이 우리의 새로운 표제를 계산할 수 있습니다.

heading = (heading + angle) % 360

그리고 이것은 모든 경우에 간단하게 효과가 있을 것입니다.

그러나, 만약 우리가 이 공식을 C++에서 사용하려고 한다면, 다른 반올림 규칙과 그에 상응하는 다른%교환, 우리는 랩어라운드가 항상 예상대로 작동하지 않는다는 것을 알게 될 것입니다!를 들어가 북서쪽을 한다면,heading = 315에서 시계방향으로 90°() 90° 회전합니다.angle = 90), 으로 향하게 될 heading = 45). 하지만 만약 그렇다면 90°를 시계 반대 방향으로 돌리려고 합니다.angle = -90의 경우), C++의 경우%는 우는돌지않교환원을리오아에서 않을 것입니다heading = 315 대로상예, 대에신에서.heading = -45!

C++를 은 다음과 같습니다.%연산자, 대신 공식을 다음과 같은 것으로 작성해야 합니다.

heading = (heading + angle) % 360;
if (heading < 0) heading += 360;

또는 다음과 같습니다.

heading = ((heading + angle) % 360) + 360) % 360;

공식 (간단한공은식)heading = (heading + angle + 360) % 360우리가 항상 보장할 수 있는 경우에만 효과가 있을 것입니다.heading + angle >= -360.)

나눗셈을 , 반올림 규칙이 있는 에 대해 하는 대가입니다.%교환입니다.

하지만 왜 파이썬인가요?//음의 무한대를 향해 반올림하는 것을 선택합니까?

python-history.blogspot.com Guido van Rossum에 따르면 이러한 행동을 선택했습니다.//

수학적으로 타당한 이유가 있습니다.정수 나눗셈 연산(//)과 그 동형인 모듈로 연산(%)은 함께 사용되며 수학적 관계를 만족시킵니다(모든 변수는 정수임).

a/b = q(잔여 r 포함)

할 정도로

b*q + r = a 및 0 < = r < b

(a 및 b는 >= 0).

관계가 음의 a(b 양의 유지)에 대해 확장되기를 원한다면, 두 가지 선택지가 있습니다: 만약 q를 0으로 자르면, r은 음이 되고, 그래서 불변량은 0 <= abs(r) < 로 바뀔 수 있고, 그렇지 않으면 불변량은 0 < = r < b(...)로 유지될 수 있습니다.수학자들은 항상 후자의 선택을 선호합니다.Python의 경우, a의 부호가 흥미롭지 않은 모듈로 연산의 흥미로운 응용 프로그램이 있기 때문에 같은 선택을 했습니다.POSIX 타임스탬프(1970년 시작 이후 몇 초)를 가져와 시간으로 변환하는 것을 고려해 보십시오.하루에 24*3600 = 86400초가 있으므로 이 계산은 단순히 %86400초입니다.하지만 만약 우리가 마이너스 숫자를 사용하여 1970년 이전의 시간을 표현한다면, "제로를 향한 자르기" 규칙은 무의미한 결과를 줄 것입니다!바닥 규칙을 사용하면 모든 것이 잘 해결됩니다.제가 생각한 다른 응용 프로그램은 컴퓨터 그래픽의 픽셀 위치 계산입니다.분명히 더 있을 거예요.

그래서 요약하면,//행동 선택은 그것을 일관되게 유지하기 때문입니다.%동작, 후자는 음(1970년 시작 전) 타임스탬프와 픽셀로 작업하는 데 유용하기 때문에 선택되었습니다.

정수와 실수 산술 모두 n과 d의 모든 값에 대해 다음 동등성이 유지되도록 나눗셈 연산자를 정의합니다.

(n+d)/d = (n/d)+1
(-n)/d = -(n/d)

불행히도, 정수 산술은 둘 다 유지되는 방식으로 정의될 수 없습니다.많은 목적에서 첫 번째 동등성은 두 번째 동등성보다 더 유용하지만 코드가 두 개의 값을 나누는 대부분의 상황에서는 다음 중 하나가 적용됩니다.

  1. 두 값 모두 양수이며, 이 경우 두 번째 동등성은 관련이 없습니다.

  2. 배당금은 두 동등성이 동시에 유지될 수 있는 정확한 정수 배수입니다.

역사적으로 음수와 관련된 나눗셈을 처리하는 가장 쉬운 방법은 정확히 한 피연산자가 음수인지 여부를 관찰하고 부호를 삭제하고 나눗셈을 수행한 다음, 정확히 한 피연산자가 음수인 경우 결과를 음수로 만드는 것이었습니다.이것은 두 가지 공통적인 상황 모두에서 요구되는 대로 행동할 것이며, 배당자가 정확히 배당의 배수일 때만이 아니라 모든 경우에서 첫 번째 동등성을 유지하는 접근법을 사용하는 것보다 더 저렴할 것입니다.

Python은 하위 의미론을 사용하는 것으로 간주되어서는 안 되며, 중요한 경우에 일반적으로 우수한 의미론이 정확한 의미론이 중요하지 않은 경우에도 분할을 약간 느리게 할 가치가 있다고 결정해야 합니다.

라운딩 모드가 그대로 선택된 이유/방법에 대한 공식적인 정의를 제공할 수는 없지만, 다음과의 호환성에 대한 인용문%당신이 포함한 연산자는 당신이 그것을 고려할 때 말이 됩니다.%C++와 Python에서는 완전히 동일하지 않습니다.

C++에서는 나머지 연산자인 반면, Python에서는 계수 연산자입니다. 두 피연산자가 다른 부호를 가질 때, 이것들이 반드시 동일한 것은 아닙니다.다음에 대한 답변에는 이러한 운영자 간의 차이에 대한 몇 가지 세부적인 설명이 있습니다."mod"와 "remainer"의 차이점은 무엇입니까?

이제, 이 차이를 고려하여, 정수 나눗셈에 대한 반올림(절단) 모드는 두 언어에서 그대로여야 하며, 인용한 관계를 보장하기 위해,(m/n)*n + m%n == m계속 유효합니다.

다음은 이것을 실제로 보여주는 두 가지 짧은 프로그램입니다. (나의 다소 순진한 파이썬 코드를 용서해 주십시오. 나는 그 언어에 초보자입니다.)

C++:

#include <iostream>

int main()
{
    int dividend, divisor, quotient, remainder, check;
    std::cout << "Enter Dividend: ";                        // -27
    std::cin >> dividend;
    std::cout << "Enter Divisor: ";                         // 4
    std::cin >> divisor;

    quotient = dividend / divisor;
    std::cout << "Quotient = " << quotient << std::endl;    // -6
    remainder = dividend % divisor;
    std::cout << "Remainder = " << remainder << std::endl;  // -3

    check = quotient * divisor + remainder;
    std::cout << "Check = " << check << std::endl;          // -27
    return 0;
}

파이썬:

print("Enter Dividend: ")             # -27
dividend = int(input())
print("Enter Divisor: ")              # 4
divisor = int(input())
quotient = dividend // divisor;
print("Quotient = " + str(quotient))  # -7
modulus = dividend % divisor;
print("Modulus = " + str(modulus))    # 1
check = quotient * divisor + modulus; # -27
print("Check = " + str(check))

서로 다른 부호(-27 및 4)의 지정된 입력의 경우, 백분율과 나머지/모수 모두 언어 간에 다르지만 복원된 값은 두 경우 모두 정확합니다.

"수학적인 이유로"

X 좌표가 음수일 수 있는 문제(비디오 게임에서 충분히 일반적)를 고려하여 타일 좌표(16x16 타일로 가정)와 타일 내 오프셋으로 변환합니다.

파이의%오프셋을 직접 제공합니다./타일을 직접 제공합니다.

>>> -35 // 16 # If we move 35 pixels left of the origin...
-3
>>> -35 % 16 # then we are 13 pixels from the left edge of a tile in row -3.
13

(그리고divmod둘 다 한 번에 제공합니다.)

Python은 표준(ASCII) 수학적 표기법으로 // b = floor(a/b)입니다.(독일에서는 바닥(x)에 대해 Gauss의 표기법 [x]가 일반적입니다.)바닥 기능은 매우 인기가 있습니다(종종 유용하게 사용됨; 수백만 개의 예를 보려면 구글을 사용하십시오).첫 번째는 아마도 간단하고 자연스럽기 때문일 것입니다: "가장 큰 정수 ≤ x".결과적으로, 그것은 다음과 같은 많은 훌륭한 수학적 특성을 누립니다.

  • 정수 k에 의한 변환: 바닥(x + k) = 바닥(x) + k.
  • 유클리드 나눗셈: 주어진 x와 y에 대해 0 ≤ r < q : = 바닥(x/y)을 갖는 x = y · q + r.

내가 생각할 수 있는 "제로를 향한 라운드" 함수의 정의는 훨씬 더 "인공적"일 것이고 if-then의 정의를 포함할 것입니다(절대값 |.| 또는 유사한 것으로 감춰질 수 있음).저는 "제로를 향한 반올림" 함수를 소개하는 수학 책을 전혀 모릅니다.그것은 이미 다른 협약보다 이 협약을 채택하기에 충분한 충분한 이유입니다.

다른 답변에 자세히 설명된 "모듈로 작동과의 호환성" 주장에 대해서는 오래 언급하지 않겠지만, 물론 유효한 주장이기도 하고, 위의 "번역" 공식과 연결되어 있기 때문에 반드시 언급해야 합니다.예를 들어, trig 함수에서 각도 모듈로 2θ가 필요할 때 필요한 것은 분명 이 눈금입니다.

저는 여에기, 씁다니는라고 씁니다.div 및 정수나셈의경우및자산연에 대한mod나머지 연산자에 대해.

div그리고.mod 정의해야 합니다.a,b이 0이 닌 아 수b,우리는 가지고 있다.

a == (a div b)*b + (a mod b)

은 종종 당이원하경우는신경우▁often▁want를 원합니다.mod 0에서 0(으) .b의 (단독)a를 들어, .를들면예,a버퍼로의 일 수 , 순환버인될수있다습니가덱스퍼의▁into▁be▁could다▁an▁index있.b(양의) 크기일 수 있습니다.그리고나서a mod b할 수 .a음입니다.사실, 사용하기a == -1마지막 버퍼 요소를 참조하는 것이 매우 인기가 있습니다.

이것이 Python이 음수 무한대를 향해 할당량을 반올림하는 이유일 수 있습니다.따라서 나머지는 배당의 부호에 관계없이 0이거나 제수의 부호를 가집니다.다음은 고정 소수점에 대한 문자 기반 그림입니다.

   ^ y = x mod 5
 4 |   *    *    *    *    *
   |  *    *    *    *    *
   | *    *    *    *    *    *
   |*    *    *    *    *    *
 0 +----*----*----*----*----*->
       -5    0    5   10   15 x

C/C++에서는 정수의 너비가 제한되어 있기 때문에 사물이 조금 더 복잡해집니다.

가정하다a == INT_MIN둘의 comple은의 2의 2의 2음 2거 2곱 2제 2이 2듭 2고 2서 2그 2것 2에 2현 2표 2가 2평 2의 2' 2곱 2이 2고 2▁of 2▁which 2제 2, 2, 2의 2▁two 2▁power 2s 2▁some 2▁in 2▁represent 2ment 2ationb == 3만약 우리가 그와 같은 수량을 반올림한다면,a mod b > 0,그리고나서(a div b)*b 작야 합니다보다 .INT_MIN서명된 서명이 있는 오버플로를 구성합니다.그러면 효과가 구현 정의됩니다. GCC의 GCC로 할 때 실행을 -ftrapv例택.하기 위한 이었습니다.그러나 정수 나눗셈과 나머지 연산의 동작을 구체화하는 근거는 그러한 불확실성을 제거하기 위한 것이었습니다.

따라서 C/C++에 남은 유일한 선택은 0을 향해 지수를 반올림하는 것이었습니다.따라서 0이 아닐 경우 나머지는 배당의 부호를 가집니다.

단점은 고정 지수에 대한 그림이 덜 규칙적으로 보인다는 것입니다.

   ^ y = x mod 5
 4 |             *    *    *
   |            *    *    *
   |           *    *    *    *
   |          *    *    *    *
 0 |    *    *    *    *    *
   |   *    *
   |  *    *
   | *    *
-4 |*    *
   +----+----+----+----+----+->
       -5    0    5   10   15 x

결적으로과,로,mod 버퍼 크기가 음수 인덱스 값을 처리하지 않습니다.프로그래밍 측면에서, 나는 이 결정을 좋아하지 않는다, 비록 나는 그것을 이행해야 하는 이유를 이해할 수 있지만.a == (a div b)*b + (a mod b)극단적인 경우에도

파이썬이 정수 나눗셈을 음의 무한대로 반올림하기로 선택한 수학적 이유는 수학적으로 가장 일관된 옵션이기 때문입니다.Python에서 두 정수를 나누면 결과는 항상 부동 소수점 번호가 됩니다.이 숫자는 가장 가까운 정수로 반올림되며 양수는 반올림되고 음수는 반올림됩니다.이러한 일관된 반올림 동작은 음의 무한대 동작을 향한 반올림으로 이어지는 것입니다.

파이썬이 음의 무한대를 향해 정수 나눗셈을 반올림하는 수학적 이유는 양의 무한대를 향해 반올림하는 것보다 더 일관된 결과를 제공하기 때문입니다.예를 들어 다음 두 가지 식을 생각해 보십시오.

3 / 4

-3 / 4

첫 번째 식은 0.75 값이 되고 두 번째 식은 -0.75 값이 됩니다.첫 번째 식은 양의 무한대를 향해 회전하고 두 번째 식은 음의 무한대를 향해 회전하기 때문입니다.

언급URL : https://stackoverflow.com/questions/70730831/whats-the-mathematical-reason-behind-python-choosing-to-round-integer-division

반응형