在像Haskell这样的纯函数式语言中,是否存在一种算法来获取函数的逆,(编辑)何时是双射的?是否有一种特定的方式来编程你的功能呢?
答案 0 :(得分:90)
在某些情况下,是的!有一篇名为Bidirectionalization for Free!的漂亮论文讨论了一些案例 - 当你的函数具有足够的多态性时 - 可以完全自动地导出反函数。 (它还讨论了当函数不是多态时,是什么使问题变得困难。)
在你的函数是可逆的情况下你得到的是反函数(带有伪输入);在其他情况下,您将获得一个尝试“合并”旧输入值和新输出值的函数。
答案 1 :(得分:31)
不,一般情况下都不可能。
证明:考虑类型的双射函数
type F = [Bit] -> [Bit]
与
data Bit = B0 | B1
假设我们有一个inv :: F -> F
的转换器inv f . f ≡ id
。假设我们已经通过确认
f = id
inv f (repeat B0) -> (B0 : ls)
由于输出中的第一个B0
必须在一段有限的时间之后出现,我们在n
实际评估我们的测试输入的深度上都有一个上限inv
获得此结果,以及它可以调用f
的次数。现在定义一系列函数
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<j≤n
,g j
是一个双射,实际上是自反。所以我们应该能够确认
inv (g j) (replicate (n+j) B0 ++ B1 : repeat B0) -> (B1 : ls)
但为了实现这一目标,inv (g j)
需要
g j (B1 : repeat B0)
深度为n+j > n
head $ g j l
至少n
个与replicate (n+j) B0 ++ B1 : ls
到目前为止,g j
中至少有一个与f
无法区分,并且由于inv f
未完成这些评估,inv
无法进行可能已经分开了 - 没有自己做一些运行时测量,这只能在IO Monad
中进行。
⬜
答案 2 :(得分:16)
你可以在维基百科上查找它,它被称为Reversible Computing。
一般情况下,你不能这样做,并且没有一种功能语言具有该选项。例如:
f :: a -> Int
f _ = 1
此功能没有反转。
答案 3 :(得分:13)
不是在大多数函数式语言中,而是在逻辑编程或关系编程中,您定义的大多数函数实际上不是函数而是“关系”,并且这些函数可以在两个方向上使用。参见例如prolog或kanren。
答案 4 :(得分:8)
这样的任务几乎总是不可判定的。您可以为某些特定功能提供解决方案,但不是一般的。
在这里,您甚至无法识别哪些函数具有反转。引用Barendregt, H. P. The Lambda Calculus: Its Syntax and Semantics. North Holland, Amsterdam (1984):
如果一组lambda-terms既不是空集也不是全集,那么它们是非常重要的。如果A和B是两个非平凡的,不相交的lambda项集合在(beta)相等下关闭,那么A和B是递归不可分割的。
让我们把A作为代表可逆函数的lambda项的集合,然后是其余的B.两者都是非空的,并且在beta等式下关闭。所以不可能决定一个函数是否可逆。
(这适用于无类型的lambda演算.TBH我不知道当我们知道要反转的函数的类型时,参数是否可以直接适应类型化的lambda演算。但我很确定它会是类似的。)
答案 5 :(得分:8)
如果你可以枚举函数的域并且可以比较范围的元素是否相等,你可以 - 以一种相当简单的方式。通过枚举我的意思是有一个可用的所有元素的列表。我会坚持Haskell,因为我不知道Ocaml(甚至如何正确地利用它; - )
你想要做的是运行域的元素,看看它们是否等于你试图反转的范围的元素,并采取第一个有效的元素:
inv :: Eq b => [a] -> (a -> b) -> (b -> a)
inv domain f b = head [ a | a <- domain, f a == b ]
既然你已经声明f
是一个双射,那么肯定只有一个这样的元素。当然,诀窍是确保域的枚举实际上在有限的时间内到达所有元素。如果您尝试将投射从Integer
转换为Integer
,则使用[0,1 ..] ++ [-1,-2 ..]
将无法正常工作,因为您永远无法获得负数。具体而言,inv ([0,1 ..] ++ [-1,-2 ..]) (+1) (-3)
永远不会产生价值。
然而,0 : concatMap (\x -> [x,-x]) [1..]
将起作用,因为它按以下顺序[0,1,-1,2,-2,3,-3, and so on]
运行整数。确实inv (0 : concatMap (\x -> [x,-x]) [1..]) (+1) (-3)
会立即返回-4
!
Control.Monad.Omega包可以帮助您以一种好的方式运行元组等列表;我相信还有更多这样的套餐 - 但我不知道。
当然,这种方法相当低调和蛮力,更不用说丑陋和低效!因此,我将在你的问题的最后一部分,关于如何'写'双射的几句话结束。 Haskell的类型系统无法证明函数是一个双向函数 - 你真的想要像Agda这样的东西 - 但它愿意相信你。
(警告:未经测试的代码如下)
因此,您可以在Bijection
和a
类型之间定义b
的数据类型:
data Bi a b = Bi {
apply :: a -> b,
invert :: b -> a
}
以及尽可能多的常数(你可以说'我知道他们是双射!'),如:[/ p>
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)
我认为你可以做invert (mapBi add1Bi) [1,5,6]
并获得[0,4,5]
。如果你以聪明的方式选择组合子,我认为手动编写Bi
常数的次数可能非常有限。
毕竟,如果你知道一个函数是一个双射函数,那么你希望在你的头脑中有一个关于这个事实的证明草图,Curry-Howard同构应该能够变成一个程序: - )
答案 6 :(得分:4)
我最近一直处理这样的问题,不,我会说(a)在许多情况下并不困难,但(b)根本没有效率。
基本上,假设你有f :: a -> b
,而f
确实是一个b ..您可以用非常愚蠢的方式计算逆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
。
具有Bounded
和Enum
实例的类型可以轻而易举地RecursivelyEnumerable
。成对的Enumerable
类型也可以Enumerable
:
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
同样适用于Enumerable
类型的分离:
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
我们可以为(,)
和Either
执行此操作这一事实可能意味着我们可以为任何代数数据类型执行此操作。
答案 7 :(得分:3)
并非每个函数都有反转。如果将讨论限制为一对一函数,则反转任意函数的能力可以破解任何密码系统。我们有点希望这是不可行的,即使在理论上也是如此!
答案 8 :(得分:2)
不,并非所有功能都有反转。例如,这个函数的反函数是什么?
f x = 1
答案 9 :(得分:0)
在某些情况下,可以通过将双射函数转换为符号表示来求逆。基于this example,我编写了这个Haskell程序来查找一些简单多项式函数的逆:
bijective_function x = x*2+1
main = do
print $ bijective_function 3
print $ inverse_function invertible_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
inverse X = X
inverse (Const a) = Const a
inverse (Plus (Const a) b) = (Subtract (inverse b) (Const a))
inverse (Plus b (Const a)) = inverse (Plus (Const a) b)
inverse (Mult (Const a) b) = (Div (inverse b) (Const a))
inverse (Mult b (Const a)) = inverse (Mult (Const a) b)
inverse (Negate a) = Negate $ inverse a
inverse (Asin x) = Sin $ inverse x
inverse (Acos x) = Cos $ inverse x
inverse (Atan x) = Tan $ inverse x
inverse_function x = with_function inverse x
此示例仅适用于算术表达式,但可能可以推广为也适用于列表。