Является ли нахождение эквивалентности двух функций неразрешимым? - PullRequest
31 голосов
/ 15 июля 2009

Разве невозможно узнать, эквивалентны ли две функции? Например, автор компилятора хочет определить, выполняют ли две функции, написанные разработчиком, одну и ту же операцию, какие методы он может использовать, чтобы выяснить это? Или что мы можем сделать, чтобы узнать, что два ТМ идентичны? Есть ли способ нормализовать машины?

Редактировать: Если общий случай неразрешим, сколько информации вам нужно иметь, чтобы правильно сказать, что две функции эквивалентны?

Ответы [ 9 ]

45 голосов
/ 15 июля 2009

Учитывая произвольную функцию, f , мы определяем функцию f ', которая возвращает 1 на входе n , если f останавливается на входе n . Теперь для некоторого числа x мы определяем функцию g , которая на входе n возвращает 1 , если n = x , а иначе вызывает f '(n).

Если функциональная эквивалентность была разрешимой, то решение о том, является ли g идентичным f ', решает, останавливается ли f на входе x . Это решило бы проблему остановки . С этим обсуждением связана теорема Райса .

Вывод: функциональная эквивалентность неразрешима.


Ниже обсуждается обоснованность этого доказательства. Итак, позвольте мне подробнее рассказать о том, что делает доказательство, и привести пример кода на Python.

  1. Доказательство создает функцию f ', которая на входе n начинает вычислять f (n) . Когда это вычисление заканчивается, f 'возвращает 1. Таким образом, f' (n) = 1 тогда и только тогда, когда f останавливается на входе n и f ' не останавливается на n если f не останавливается. Python:

    def create_f_prime(f):
        def f_prime(n):
            f(n)
            return 1
        return f_prime
    
  2. Затем мы создаем функцию g , которая принимает n в качестве входных данных и сравнивает ее с некоторым значением x . Если n = x , то g (n) = g (x) = 1 , иначе g (n) = f '(n) . Python:

    def create_g(f_prime, x):
        def g(n):
            return 1 if n == x else f_prime(n)
        return g
    
  3. Теперь хитрость в том, что для всех n! = X мы имеем g (n) = f '(n) . Кроме того, мы знаем, что g (x) = 1 . Итак, если g = f ', то f' (x) = 1 и, следовательно, f (x) останавливается. Аналогично, если g! = F ', то обязательно f' (x)! = 1 , что означает, что f (x) не останавливается. Таким образом, решение о том, g = f ' эквивалентно решению о том, останавливается ли f на входе x . Используя немного другое обозначение для двух вышеупомянутых функций, мы можем суммировать все это следующим образом:

    def halts(f, x):
        def f_prime(n): f(n); return 1
        def g(n): return 1 if n == x else f_prime(n)
        return equiv(f_prime, g) # If only equiv would actually exist...
    

Я также добавлю иллюстрацию доказательства в Haskell (GHC выполняет некоторое обнаружение петли, и я не совсем уверен, является ли использование seq в этом случае доказательством дурака, но в любом случае):

-- Tells whether two functions f and g are equivalent.
equiv :: (Integer -> Integer) -> (Integer -> Integer) -> Bool
equiv f g = undefined -- If only this could be implemented :)

-- Tells whether f halts on input x
halts :: (Integer -> Integer) -> Integer -> Bool
halts f x = equiv f' g
  where
    f' n = f n `seq` 1
    g  n = if n == x then 1 else f' n
7 голосов
/ 15 июля 2009

Да, это неразрешимо. Это форма проблемы остановки .

Обратите внимание, что я имею в виду, что это неразрешимо для общего случая. Точно так же, как вы можете определить остановку для достаточно простых программ, вы можете определить эквивалентность для достаточно простых функций, и не исключено, что это может быть полезным для приложения. Но вы не можете сделать общий метод для определения эквивалентности любых двух возможных функций.

4 голосов
/ 15 июля 2009

Общий случай неразрешим по теореме Райса, как уже говорили другие (теорема Райса по существу говорит, что любое нетривиальное свойство формализма по Тьюрингу неразрешимо).

Существуют особые случаи, когда эквивалентность разрешима, наиболее известным примером, вероятно, является эквивалентность автоматов конечного состояния. Если я правильно помню, эквивалентность автоматов уже не может быть решена путем сведения к проблеме корреспонденции Поста.

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

2 голосов
/ 26 марта 2015

Обратите внимание, что проблема остановки разрешима для линейных ограниченных автоматов. Реальные компьютеры всегда ограничены, и программы для них всегда возвращаются к предыдущей конфигурации после достаточно большого количества шагов. Если вы используете неограниченный (воображаемый) компьютер для отслеживания конфигураций, вы можете обнаружить этот цикл и принять его во внимание.

2 голосов
/ 06 октября 2009

Это зависит от того, что вы подразумеваете под "функцией".

Если функции, о которых вы говорите, гарантированно завершатся - например, потому что они написаны на языке, на котором завершаются все функции - и работают в конечных доменах, это «легко» (хотя это все равно может занять очень, очень долгое время): две функции эквивалентны тогда и только тогда, когда они имеют одинаковое значение в каждой точке их общего домена.

Это называется "экстенсиональной" эквивалентностью, чтобы отличать ее от синтаксической или "интенциональной" эквивалентности. Две функции эквивалентны в экстенсивном смысле, если они эквивалентны в целенаправленном порядке, но обратное утверждение не выполняется.

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

2 голосов
/ 15 июля 2009

В общем случае неразрешимо, имеют ли две машины Тьюринга всегда одинаковый выход для одинакового ввода. Поскольку вы даже не можете решить, остановится ли tm на входе, я не понимаю, каким образом можно решить, будет ли и остановка И выводить один и тот же результат ...

0 голосов
/ 15 июля 2009

Разве невозможно узнать, эквивалентны ли две функции?

Нет. Можно знать, что две функции эквивалентны. Если у вас есть f (x), вы знаете, что f (x) эквивалентно f (x).

Если вопрос таков: «можно определить, эквивалентны ли f (x) и g (x), а f и g - любая функция и для всех функций g и f»), тогда ответ - нет.

Однако, если вопрос «может ли компилятор определить, что если f (x) и g (x) эквивалентны, что они эквивалентны?», То ответ «да», если они эквивалентны как в выходных данных, так и в побочных эффектах, и порядок побочных эффектов. Другими словами, если одно является преобразованием другого, которое сохраняет поведение, то компилятор достаточной сложности должен быть в состоянии обнаружить его. Это также означает, что компилятор может преобразовать функцию f в более оптимальную и эквивалентную функцию g, учитывая конкретное определение эквивалента. Будет еще веселее, если f включает неопределенное поведение, потому что тогда g также может включать неопределенное (но другое) поведение!

0 голосов
/ 15 июля 2009

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

0 голосов
/ 15 июля 2009

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

Даже если бы вы могли сделать вышеописанное, вам пришлось бы учитывать, какие глобальные значения изменяются внутри функции, какие объекты уничтожаются / изменяются в функции, которые не влияют на результат.

Вы действительно можете сравнить только скомпилированный код. Так скомпилировать скомпилированный код для рефакторинга?

Представьте себе время выполнения при попытке скомпилировать код с "этим" компилятором. Вы можете потратить много времени здесь, отвечая на вопросы, говоря: "занят компиляцией ...":)

...