Functional programming 在纯函数语言中,是否有求逆函数的算法?
在像Haskell这样的纯函数语言中,当函数是双射函数时,是否有一种算法来求其逆(编辑)?有没有一种特殊的方法来编程你的函数呢?没有,不是所有的函数都有倒数。例如,这个函数的倒数是什么Functional programming 在纯函数语言中,是否有求逆函数的算法?,functional-programming,Functional Programming,在像Haskell这样的纯函数语言中,当函数是双射函数时,是否有一种算法来求其逆(编辑)?有没有一种特殊的方法来编程你的函数呢?没有,不是所有的函数都有倒数。例如,这个函数的倒数是什么 f x = 1 你可以在维基百科上查到,它叫做 一般来说,您不能这样做,而且没有一种函数式语言有这样的选项。例如: f :: a -> Int f _ = 1 此函数没有逆函数。并非每个函数都有逆函数。如果您将讨论限制在一对一函数上,则反转任意函数的能力将授予破解任何密码系统的能力。我们有点希望这是不可
f x = 1
你可以在维基百科上查到,它叫做 一般来说,您不能这样做,而且没有一种函数式语言有这样的选项。例如:
f :: a -> Int
f _ = 1
此函数没有逆函数。并非每个函数都有逆函数。如果您将讨论限制在一对一函数上,则反转任意函数的能力将授予破解任何密码系统的能力。我们有点希望这是不可行的,即使在理论上 在某些情况下,是的!有一篇漂亮的论文叫做,它讨论了一些情况——当你的函数足够多态时——在这些情况下,完全自动地导出一个反函数是可能的。(本文还讨论了当函数不是多态函数时,是什么使问题变得困难。)
如果你的函数是可逆的,你得到的结果是相反的(带有虚假输入);在其他情况下,您会得到一个函数,该函数尝试“合并”旧输入值和新输出值。不,通常不可能
证明:考虑类型
的双射函数type F = [Bit] -> [Bit]
与
假设我们有一个反相器inv::F->F
,这样inv F。F≡ id
。假设我们已经测试了它的函数f=id
,方法是确认
inv f (repeat B0) -> (B0 : ls)
由于输出中的第一个B0
一定是在某个有限的时间之后出现的,因此在inv
实际评估测试输入以获得该结果的深度以及调用f
的次数上,我们都有一个上限n
。现在定义一系列函数
g j (B1 : B0 : ... (n+j times) ... B0 : ls)
= B0 : ... (n+j times) ... B0 : B1 : ls
g j (B0 : ... (n+j times) ... B0 : B1 : ls)
= B1 : B0 : ... (n+j times) ... B0 : ls
g j l = l
显然,对于所有0 n
head$g j l
是否至少有n
不同的列表匹配replicate(n+j)B0++B1:ls
gj
与f
是无法区分的,而且由于inv f
没有做过这两个评估,inv
不可能把它区分开来——除非自己做一些运行时测量,这只有在IO Monad
中才可能
⬜ 像这样的任务几乎总是无法确定的。您可以为某些特定函数提供解决方案,但不是一般的解决方案 在这里,您甚至无法识别哪些函数具有逆函数。引述: 如果lambda项集既不是空的也不是满的,那么它就不是平凡的。如果A和B是在(beta)相等条件下闭合的两个非平凡、不相交的lambda项集,那么A和B是递归不可分的 让我们假设A是表示可逆函数的lambda项集,B是其余的。两者都是非空的,在beta等式下闭合。所以不可能决定一个函数是否可逆
(这适用于非类型lambda演算。当我们知道要反转的函数的类型时,我不知道参数是否可以直接适用于类型lambda演算。但我很确定它会类似。)我最近一直在处理类似的问题,不,我想说(a)在很多情况下这并不困难,但是(b)它根本没有效率 基本上,假设您有
f::a->b
,并且f
确实是一个对象。你可以用一种非常愚蠢的方式计算逆f':b->a
:
import Data.List
-- | Class for types whose values are recursively enumerable.
class Enumerable a where
-- | Produce the list of all values of type @a@.
enumerate :: [a]
-- | Note, this is only guaranteed to terminate if @f@ is a bijection!
invert :: (Enumerable a, Eq b) => (a -> b) -> b -> Maybe a
invert f b = find (\a -> f a == b) enumerate
如果f
是一个双射,并且enumerate
确实生成a
的所有值,那么您最终将命中a
,从而f a==b
具有有界
和枚举
实例的类型可以轻松地使递归可枚举
。也可以使成对的可枚举
类型成为可枚举
:
instance (Enumerable a, Enumerable b) => Enumerable (a, b) where
enumerate = crossWith (,) enumerate enumerate
crossWith :: (a -> b -> c) -> [a] -> [b] -> [c]
crossWith f _ [] = []
crossWith f [] _ = []
crossWith f (x0:xs) (y0:ys) =
f x0 y0 : interleave (map (f x0) ys)
(interleave (map (flip f y0) xs)
(crossWith f xs ys))
interleave :: [a] -> [a] -> [a]
interleave xs [] = xs
interleave [] ys = []
interleave (x:xs) ys = x : interleave ys xs
可枚举类型的析取也是如此:
instance (Enumerable a, Enumerable b) => Enumerable (Either a b) where
enumerate = enumerateEither enumerate enumerate
enumerateEither :: [a] -> [b] -> [Either a b]
enumerateEither [] ys = map Right ys
enumerateEither xs [] = map Left xs
enumerateEither (x:xs) (y:ys) = Left x : Right y : enumerateEither xs ys
事实上,我们可以对(,)
和中的任何一种进行此操作,这可能意味着我们可以对任何代数数据类型进行此操作。如果您可以枚举函数的域,并可以比较范围中的元素是否相等,您可以-以一种相当简单的方式。我所说的枚举是指有一个所有可用元素的列表。我将坚持使用Haskell,因为我不知道Ocaml(甚至不知道如何正确地利用它;-)
您要做的是遍历域的元素,看看它们是否等于您尝试反转的范围的元素,然后取第一个有效的元素:
inv :: Eq b => [a] -> (a -> b) -> (b -> a)
inv domain f b = head [ a | a <- domain, f a == b ]
以及您喜欢的任意多个常量(您可以说“我知道它们是双射!”),例如:
notBi :: Bi Bool Bool
notBi = Bi not not
add1Bi :: Bi Integer Integer
add1Bi = Bi (+1) (subtract 1)
idBi :: Bi a a
idBi = Bi id id
invertBi :: Bi a b -> Bi b a
invertBi (Bi a i) = (Bi i a)
composeBi :: Bi a b -> Bi b c -> Bi a c
composeBi (Bi a1 i1) (Bi a2 i2) = Bi (a2 . a1) (i1 . i2)
mapBi :: Bi a b -> Bi [a] [b]
mapBi (Bi a i) = Bi (map a) (map i)
bruteForceBi :: Eq b => [a] -> (a -> b) -> Bi a b
bruteForceBi domain f = Bi f (inv domain f)
和一些智能组合器,例如:
notBi :: Bi Bool Bool
notBi = Bi not not
add1Bi :: Bi Integer Integer
add1Bi = Bi (+1) (subtract 1)
idBi :: Bi a a
idBi = Bi id id
invertBi :: Bi a b -> Bi b a
invertBi (Bi a i) = (Bi i a)
composeBi :: Bi a b -> Bi b c -> Bi a c
composeBi (Bi a1 i1) (Bi a2 i2) = Bi (a2 . a1) (i1 . i2)
mapBi :: Bi a b -> Bi [a] [b]
mapBi (Bi a i) = Bi (map a) (map i)
bruteForceBi :: Eq b => [a] -> (a -> b) -> Bi a b
bruteForceBi domain f = Bi f (inv domain f)
我想你可以做反转(mapBi add1Bi)[1,5,6]
得到[0,4,5]
。如果你以一种聪明的方式选择你的组合符,我认为你必须手工编写一个Bi
常量的次数可能会非常有限
毕竟,如果你知道一个函数是一个双射函数,你很有希望在你的头脑中有一个证明这一事实的草图,Curry Howard同构应该能够将其转化为一个程序:-)不是在大多数函数式语言中,而是在逻辑编程或关系式编程中,你定义的大多数函数实际上不是函数,而是函数“关系”,它们可以在两个方向上使用。例如,请参见prolog或kanren。在某些情况下,可能
idBi :: Bi a a
idBi = Bi id id
invertBi :: Bi a b -> Bi b a
invertBi (Bi a i) = (Bi i a)
composeBi :: Bi a b -> Bi b c -> Bi a c
composeBi (Bi a1 i1) (Bi a2 i2) = Bi (a2 . a1) (i1 . i2)
mapBi :: Bi a b -> Bi [a] [b]
mapBi (Bi a i) = Bi (map a) (map i)
bruteForceBi :: Eq b => [a] -> (a -> b) -> Bi a b
bruteForceBi domain f = Bi f (inv domain f)
bijective_function x = x*2+1
main = do
print $ bijective_function 3
print $ inverse_function bijective_function (bijective_function 3)
data Expr = X | Const Double |
Plus Expr Expr | Subtract Expr Expr | Mult Expr Expr | Div Expr Expr |
Negate Expr | Inverse Expr |
Exp Expr | Log Expr | Sin Expr | Atanh Expr | Sinh Expr | Acosh Expr | Cosh Expr | Tan Expr | Cos Expr |Asinh Expr|Atan Expr|Acos Expr|Asin Expr|Abs Expr|Signum Expr|Integer
deriving (Show, Eq)
instance Num Expr where
(+) = Plus
(-) = Subtract
(*) = Mult
abs = Abs
signum = Signum
negate = Negate
fromInteger a = Const $ fromIntegral a
instance Fractional Expr where
recip = Inverse
fromRational a = Const $ realToFrac a
(/) = Div
instance Floating Expr where
pi = Const pi
exp = Exp
log = Log
sin = Sin
atanh = Atanh
sinh = Sinh
cosh = Cosh
acosh = Acosh
cos = Cos
tan = Tan
asin = Asin
acos = Acos
atan = Atan
asinh = Asinh
fromFunction f = f X
toFunction :: Expr -> (Double -> Double)
toFunction X = \x -> x
toFunction (Negate a) = \a -> (negate a)
toFunction (Const a) = const a
toFunction (Plus a b) = \x -> (toFunction a x) + (toFunction b x)
toFunction (Subtract a b) = \x -> (toFunction a x) - (toFunction b x)
toFunction (Mult a b) = \x -> (toFunction a x) * (toFunction b x)
toFunction (Div a b) = \x -> (toFunction a x) / (toFunction b x)
with_function func x = toFunction $ func $ fromFunction x
simplify X = X
simplify (Div (Const a) (Const b)) = Const (a/b)
simplify (Mult (Const a) (Const b)) | a == 0 || b == 0 = 0 | otherwise = Const (a*b)
simplify (Negate (Negate a)) = simplify a
simplify (Subtract a b) = simplify ( Plus (simplify a) (Negate (simplify b)) )
simplify (Div a b) | a == b = Const 1.0 | otherwise = simplify (Div (simplify a) (simplify b))
simplify (Mult a b) = simplify (Mult (simplify a) (simplify b))
simplify (Const a) = Const a
simplify (Plus (Const a) (Const b)) = Const (a+b)
simplify (Plus a (Const b)) = simplify (Plus (Const b) (simplify a))
simplify (Plus (Mult (Const a) X) (Mult (Const b) X)) = (simplify (Mult (Const (a+b)) X))
simplify (Plus (Const a) b) = simplify (Plus (simplify b) (Const a))
simplify (Plus X a) = simplify (Plus (Mult 1 X) (simplify a))
simplify (Plus a X) = simplify (Plus (Mult 1 X) (simplify a))
simplify (Plus a b) = (simplify (Plus (simplify a) (simplify b)))
simplify a = a
inverse X = X
inverse (Const a) = simplify (Const a)
inverse (Mult (Const a) (Const b)) = Const (a * b)
inverse (Mult (Const a) X) = (Div X (Const a))
inverse (Plus X (Const a)) = (Subtract X (Const a))
inverse (Negate x) = Negate (inverse x)
inverse a = inverse (simplify a)
inverse_function x = with_function inverse x