programing

연관성 수학 : (a + b) + c! = a + (b + c)

projobs 2021. 1. 19. 20:58
반응형

연관성 수학 : (a + b) + c! = a + (b + c)


최근 에 Eric Lippert오래된 블로그 게시물을 살펴 보았는데 , 연관성에 대해 글을 쓰는 동안 그는 C #에서 a, b, c의 특정 값과 (a + b) + c동일하지 않다고 언급했습니다 a + (b + c).

나는 어떤 유형과 범위의 산술 값 이 참일 수 있고 그 이유를 알 수 없습니다.


double유형 의 범위 :

double dbl1 = (double.MinValue + double.MaxValue) + double.MaxValue;
double dbl2 = double.MinValue + (double.MaxValue + double.MaxValue);

첫 번째는 double.MaxValue이고 두 번째는double.Infinity

double유형 의 정밀도 :

double dbl1 = (double.MinValue + double.MaxValue) + double.Epsilon;
double dbl2 = double.MinValue + (double.MaxValue + double.Epsilon);

이제 dbl1 == double.Epsilon, 동안 dbl2 == 0.

그리고 말 그대로 질문을 읽는 것에 :-)

에서 checked모드 :

checked
{
    int i1 = (int.MinValue + int.MaxValue) + int.MaxValue;
}

i1 이다 int.MaxValue

checked
{
    int temp = int.MaxValue;
    int i2 = int.MinValue + (temp + temp);
}

( temp변수 사용에 유의하십시오 . 그렇지 않으면 컴파일러가 직접 오류를 발생시킵니다 ... 기술적으로 이것은 다른 결과가 될 것입니다. :-) 올바르게 컴파일하고 컴파일하지 않습니다.)

이것은 던졌습니다 OverflowException... 결과는 다릅니다 :-) ( int.MaxValueException)


한 가지 예

a = 1e-30
b = 1e+30
c = -1e+30

극단의 작은 숫자와 큰 숫자로 다른 결과를 얻는 방법을 보여주는 다른 답변을 확장하면 실제 일반 숫자를 가진 부동 소수점이 다른 답변을 제공하는 예가 있습니다.

이 경우 극도의 정밀도 한계에서 숫자를 사용하는 대신 단순히 많은 추가 작업을 수행합니다. 차이점은 수행 (((...(((a+b)+c)+d)+e)...또는...(((a+b)+(c+d))+((e+f)+(g+h)))+...

여기서 파이썬을 사용하고 있지만 C #으로 작성하면 동일한 결과를 얻을 수 있습니다. 먼저 모두 0.1 인 백만 개의 값 목록을 만듭니다. 왼쪽에서 추가하면 반올림 오류가 중요해집니다.

>>> numbers = [0.1]*1000000
>>> sum(numbers)
100000.00000133288

이제 다시 추가하되 이번에는 쌍으로 추가합니다 (중간 스토리지를 덜 사용하는 훨씬 더 효율적인 방법이 있지만 여기서는 구현을 간단하게 유지했습니다).

>>> def pair_sum(numbers):
    if len(numbers)==1:
        return numbers[0]
    if len(numbers)%2:
        numbers.append(0)
    return pair_sum([a+b for a,b in zip(numbers[::2], numbers[1::2])])

>>> pair_sum(numbers)
100000.0

이번에는 반올림 오류가 최소화됩니다.

완전성을 위해 편집 하십시오. 여기에 더 효율적이지만 쌍별 합계 구현을 따르기가 쉽지 않습니다. pair_sum()위와 같은 대답을 제공합니다 .

def pair_sum(seq):
    tmp = []
    for i,v in enumerate(seq):
        if i&1:
            tmp[-1] = tmp[-1] + v
            i = i + 1
            n = i & -i
            while n > 2:
                t = tmp.pop(-1)
                tmp[-1] = tmp[-1] + t
                n >>= 1
        else:
            tmp.append(v)
    while len(tmp) > 1:
        t = tmp.pop(-1)
        tmp[-1] = tmp[-1] + t
    return tmp[0]

다음은 C #으로 작성된 간단한 pair_sum입니다.

using System;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static double pair_sum(double[] numbers)
        {
            if (numbers.Length==1)
            {
                return numbers[0];
            }
            var new_numbers = new double[(numbers.Length + 1) / 2];
            for (var i = 0; i < numbers.Length - 1; i += 2) {
                new_numbers[i / 2] = numbers[i] + numbers[i + 1];
            }
            if (numbers.Length%2 != 0)
            {
                new_numbers[new_numbers.Length - 1] = numbers[numbers.Length-1];
            }
            return pair_sum(new_numbers);
        }
        static void Main(string[] args)
        {
            var numbers = new double[1000000];
            for (var i = 0; i < numbers.Length; i++) numbers[i] = 0.1;
            Console.WriteLine(numbers.Sum());
            Console.WriteLine(pair_sum(numbers));
        }
    }
}

출력 포함 :

100000.000001333
100000

이는 일반 값 유형 (int, long 등)이 고정 된 양의 바이트를 사용하여 저장된다는 사실에서 기인합니다. 따라서 두 값의 합이 바이트 저장 용량을 초과하면 오버플로가 발생할 수 있습니다.

C #에서는 이러한 종류의 문제를 피하기 위해 BigInteger사용할 수 있습니다 . BigInteger는 크기가 임의적이므로 오버플로를 생성하지 않습니다.

BigInteger는 .NET 4.0 이상 (VS 2010+)에서만 사용할 수 있습니다.


짧은 대답은 (a + b) + c == a + (b + c)수학적인 것이지만 반드시 계산적인 것은 아닙니다.

컴퓨터는 실제로 이진으로 작동한다는 것을 기억하고, 단순한 십진수라도 내부 형식으로 변환 할 때 반올림 오류가 발생할 수 있습니다.

언어에 따라 더하기 만해도 반올림 오류가 발생할 수 있으며 위의 예에서의 반올림 오류는의 반올림 오류와 a+b다를 수 있습니다 b+c.

한 가지 놀라운 범죄자는 JavaScript 0.1 + 0.2 != 0.3입니다. 반올림 오차는 소수점 이하로 멀지 만 실제적이고 문제가 있습니다.

작은 부분을 먼저 추가하여 반올림 오차를 줄이는 것이 일반적인 원칙입니다. 이렇게하면 더 많은 숫자에 압도되기 전에 누적 될 수 있습니다.


유사한 몇 가지 예 :

static void A(string s, int i, int j)
{
  var test1 = (s + i) + j;
  var test2 = s + (i + j);
  var testX = s + i + j;
}

다음 A("Hello", 3, 5)에 리드 test1testX동등 인 "Hello35"반면,이 test2될 것이다 "Hello8".

과:

static void B(int i, int j, long k)
{
  var test1 = (i + j) + k;
  var test2 = i + (j + k);
  var testX = i + j + k;
}

여기서 B(2000000000, 2000000000, 42L)리드 test1testX동등 인 -294967254L통상의 unchecked상태, 모드 test2가된다 4000000042L.

참조 URL : https://stackoverflow.com/questions/32004190/associativity-math-abcabc

반응형