Есть ли в Mathematica "нормальная" функция EqualQ? - PullRequest
9 голосов
/ 13 февраля 2011

На странице документации для Equal мы читаем, что

Приблизительные числа с машиной точность или выше считаются равны, если они отличаются максимум последние семь двоичных цифр (примерно их последние две десятичные цифры).

Вот примеры (32-битная система; для 64-битной системы добавьте еще несколько нулей в середине):

In[1]:= 1.0000000000000021 == 1.0000000000000022
1.0000000000000021 === 1.0000000000000022

Out[1]= True

Out[2]= True

Мне интересно, есть ли "обычный" аналог функции Equal в Mathematica , который не пропускает последние 7 двоичных цифр?

Ответы [ 7 ]

14 голосов
/ 08 июля 2011

Благодаря недавней записи в официальной группе новостей Александра Распутинова, теперь я выучил две недокументированные функции, которые контролируют допуски Equal и SameQ: $EqualTolerance и $SameQToleranceMathematica версии 5 и более ранних версиях эти функции находятся в контексте Experimental` и хорошо документированы: $ EqualTolerance , $ SameQTolerance .Начиная с версии 6, они перемещаются в контекст Internal` 1016 * и становятся недокументированными, но все еще работают и даже имеют встроенные диагностические сообщения, которые появляются, когда кто-то пытается присвоить им недопустимые значения:

In[1]:= Internal`$SameQTolerance = a

During evaluation of In[2]:= Internal`$SameQTolerance::tolset: 
Cannot set Internal`$SameQTolerance to a; value must be a real 
number or +/- Infinity.

Out[1]= a

Ссылаясь на Александра Распутинова:

Internal` $ EqualTolerance ... принимает действительное значение машины, указывающее число допусков десятичных цифр, которые должны быть применены, т.е. Log [2] / Log [В 10] раз меньше младших разрядов, которые один хочет игнорировать.

Таким образом, установка Internal`$EqualTolerance в ноль заставит Equal считать числа равными только тогда, когда они идентичны во всех двоичных файлахцифры (не считая 10 * Precision цифр):

In[2]:= Block[{Internal`$EqualTolerance = 0}, 
           1.0000000000000021 == 1.0000000000000022]
Out[2]= False

In[5]:= Block[{Internal`$EqualTolerance = 0}, 
           1.00000000000000002 == 1.000000000000000029]
        Block[{Internal`$EqualTolerance = 0}, 
           1.000000000000000020 == 1.000000000000000029]
Out[5]= True
Out[6]= False

Обратите внимание на следующий случай:

In[3]:= Block[{Internal`$EqualTolerance = 0}, 
           1.0000000000000020 == 1.0000000000000021]
        RealDigits[1.0000000000000020, 2] === RealDigits[1.0000000000000021, 2]
Out[3]= True
Out[4]= True

В этом случае оба числа имеют MachinePrecision, что фактически равно

In[5]:= $MachinePrecision
Out[5]= 15.9546

(53*Log[10, 2]).С такой точностью эти числа идентичны во всех двоичных цифрах:

In[6]:= RealDigits[1.0000000000000020` $MachinePrecision, 2] === 
                   RealDigits[1.0000000000000021` $MachinePrecision, 2]
Out[6]= True

Увеличение точности до 16 делает их различными числами произвольной точности:

In[7]:= RealDigits[1.0000000000000020`16, 2] === 
              RealDigits[1.0000000000000021`16, 2]
Out[7]= False

In[8]:= Row@First@RealDigits[1.0000000000000020`16,2]
         Row@First@RealDigits[1.0000000000000021`16,2]
Out[9]= 100000000000000000000000000000000000000000000000010010
Out[10]= 100000000000000000000000000000000000000000000000010011

Но, к сожалению, Equal все еще не удаетсяразличайте их:

In[11]:= Block[{Internal`$EqualTolerance = 0}, 
 {1.00000000000000002`16 == 1.000000000000000021`16, 
  1.00000000000000002`17 == 1.000000000000000021`17, 
  1.00000000000000002`18 == 1.000000000000000021`18}]
Out[11]= {True, True, False}

Таких случаев бесконечно много:

In[12]:= Block[{Internal`$EqualTolerance = 0}, 
  Cases[Table[a = SetPrecision[1., n]; 
    b = a + 10^-n; {n, a == b, RealDigits[a, 2] === RealDigits[b, 2], 
     Order[a, b] == 0}, {n, 15, 300}], {_, True, False, _}]] // Length

Out[12]= 192

Интересно, что иногда RealDigits возвращает одинаковые цифры, тогда как Order показывает, что внутренние представления выраженийне идентичны:

In[13]:= Block[{Internal`$EqualTolerance = 0}, 
  Cases[Table[a = SetPrecision[1., n]; 
    b = a + 10^-n; {n, a == b, RealDigits[a, 2] === RealDigits[b, 2], 
     Order[a, b] == 0}, {n, 15, 300}], {_, _, True, False}]] // Length

Out[13]= 64

Но, похоже, что происходит противоположная ситуация:

In[14]:= 
Block[{Internal`$EqualTolerance = 0}, 
  Cases[Table[a = SetPrecision[1., n]; 
    b = a + 10^-n; {n, a == b, RealDigits[a, 2] === RealDigits[b, 2], 
     Order[a, b] == 0}, {n, 15, 3000}], {_, _, False, True}]] // Length

Out[14]= 0
6 голосов
/ 13 февраля 2011

Попробуйте:

realEqual[a_, b_] := SameQ @@ RealDigits[{a, b}, 2, Automatic]

Выбор базы 2 крайне важен для сравнения внутренних представлений.

In[54]:= realEqual[1.0000000000000021, 1.0000000000000021]
Out[54]= True

In[55]:= realEqual[1.0000000000000021, 1.0000000000000022]
Out[55]= False

In[56]:= realEqual[
           1.000000000000000000000000000000000000000000000000000000000000000022
         , 1.000000000000000000000000000000000000000000000000000000000000000023
         ]
Out[56]= False
6 голосов
/ 13 февраля 2011
In[12]:= MyEqual[x_, y_] := Order[x, y] == 0

In[13]:= MyEqual[1.0000000000000021, 1.0000000000000022]

Out[13]= False

In[14]:= MyEqual[1.0000000000000021, 1.0000000000000021]

Out[14]= True

Этот тест проверяет, идентичны ли два объекта, поскольку 1.0000000000000021 и 1.000000000000002100 отличаются по точности, они не будут считаться идентичными

4 голосов
/ 13 февраля 2011

Я предлагаю стратегию, которая использует RealDigits для сравнения фактических цифр чисел. Единственный сложный момент - убрать конечные нули.

trunc = {Drop[First@#, Plus @@ First /@ {-Dimensions@First@#, 
         Last@Position[First@#, n_?(# != 0 &)]}], Last@#} &@ RealDigits@# &;
exactEqual = SameQ @@ trunc /@ {#1, #2} &;

In[1]  := exactEqual[1.000000000000000000000000000000000000000000000000000111,
                     1.000000000000000000000000000000000000000000000000000111000]
Out[1] := True
In[2]  := exactEqual[1.000000000000000000000000000000000000000000000000000111,
                     1.000000000000000000000000000000000000000000000000000112000]
Out[2] := False
4 голосов
/ 13 февраля 2011

Я не знаю уже определенного оператора.Но вы можете определить, например:

longEqual[x_, y_] := Block[{$MaxPrecision = 20, $MinPrecision = 20},
                            Equal[x - y, 0.]]  

Например:

longEqual[1.00000000000000223, 1.00000000000000223]
True
longEqual[1.00000000000000223, 1.00000000000000222]
False   

Редактировать

Если вы хотите обобщить произвольное числоиз цифр, вы можете сделать, например:

longEqual[x_, y_] :=
 Block[{
   $MaxPrecision =  Max @@ StringLength /@ ToString /@ {x, y},
   $MinPrecision =  Max @@ StringLength /@ ToString /@ {x, y}},
   Equal[x - y, 0.]]

Так что ваш контрпример в вашем комментарии также работает.

HTH!

2 голосов
/ 14 февраля 2011

Я думаю, что вам действительно нужно указать, что вы хотите ... нет способа сравнить приблизительные реальные числа, которые удовлетворят всех в любой ситуации.

В любом случае, вот еще пара вариантов:

In[1]:= realEqual[lhs_,rhs_,tol_:$MachineEpsilon] := 0==Chop[lhs-rhs,tol]

In[2]:= Equal[1.0000000000000021,1.0000000000000021]
        realEqual[1.0000000000000021,1.0000000000000021]
Out[2]= True
Out[3]= True

In[4]:= Equal[1.0000000000000022,1.0000000000000021]
        realEqual[1.0000000000000022,1.0000000000000021]
Out[4]= True
Out[5]= False

Поскольку точность обоих чисел возрастает, их всегда можно различить, если установить tol достаточно высоко.

Обратите внимание, что вычитание выполняется с точностью до наименьшего издва числа.Вы можете сделать это с точностью до большего числа (что кажется немного бессмысленным), выполнив что-то вроде

maxEqual[lhs_, rhs_] := With[{prec = Max[Precision /@ {lhs, rhs}]}, 
  0 === Chop[SetPrecision[lhs, prec] - SetPrecision[rhs, prec], 10^-prec]]

, возможно, использование минимальной точности имеет больше смысла

minEqual[lhs_, rhs_] := With[{prec = Min[Precision /@ {lhs, rhs}]}, 
  0 === Chop[SetPrecision[lhs, prec] - SetPrecision[rhs, prec], 10^-prec]]
1 голос
/ 13 февраля 2011

Еще один способ определить такую ​​функцию - использовать SetPrecision:

MyEqual[a_, b_] := SetPrecision[a, Precision[a] + 3] == SetPrecision[b, Precision[b] + 3]

Это работает во всех случаях, но мне все еще интересно, есть ли встроенная функция. Уродливо использовать высокоуровневые функции для такой примитивной задачи ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...