Si puede enumerar el dominio de la función y comparar elementos del rango para determinar la igualdad, puede hacerlo de una manera bastante sencilla. Por enumerar me refiero a tener una lista de todos los elementos disponibles. Me quedaré con Haskell, ya que no sé Ocaml (ni siquiera cómo capitalizarlo correctamente ;-)
Lo que desea hacer es ejecutar los elementos del dominio y ver si son iguales al elemento del rango que está tratando de invertir, y tomar el primero que funcione:
inv :: Eq b => [a] -> (a -> b) -> (b -> a)
inv domain f b = head [ a | a <- domain, f a == b ]
Como ha dicho que f
es una biyección, es probable que haya uno y solo uno de esos elementos. El truco, por supuesto, es asegurarse de que su enumeración del dominio alcance realmente todos los elementos en un tiempo finito . Si está tratando de invertir una biyección de Integer
a Integer
, el uso [0,1 ..] ++ [-1,-2 ..]
no funcionará ya que nunca obtendrá los números negativos. Concretamente,inv ([0,1 ..] ++ [-1,-2 ..]) (+1) (-3)
nunca rendirá un valor.
Sin embargo, 0 : concatMap (\x -> [x,-x]) [1..]
funcionará, ya que se ejecuta a través de los números enteros en el siguiente orden [0,1,-1,2,-2,3,-3, and so on]
. De hecho inv (0 : concatMap (\x -> [x,-x]) [1..]) (+1) (-3)
regresa rápidamente-4
!
El paquete Control.Monad.Omega puede ayudarlo a ejecutar listas de tuplas, etcétera, de una buena manera; Estoy seguro de que hay más paquetes como ese, pero no los conozco.
Por supuesto, este enfoque es bastante sencillo y de fuerza bruta, ¡sin mencionar que es feo e ineficiente! Así que terminaré con algunos comentarios sobre la última parte de su pregunta, sobre cómo 'escribir' bijecciones. El sistema de tipos de Haskell no está preparado para demostrar que una función es una biyección (realmente quieres algo como Agda para eso) pero está dispuesto a confiar en ti.
(Advertencia: sigue un código no probado)
Entonces, ¿puede definir un tipo de datos de Bijection
s entre tipos a
y b
:
data Bi a b = Bi {
apply :: a -> b,
invert :: b -> a
}
junto con tantas constantes (donde puede decir '¡ Sé que son biyecciones!') como desee, como:
notBi :: Bi Bool Bool
notBi = Bi not not
add1Bi :: Bi Integer Integer
add1Bi = Bi (+1) (subtract 1)
y un par de combinadores inteligentes, como:
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)
Creo que entonces podrías hacer invert (mapBi add1Bi) [1,5,6]
y conseguir [0,4,5]
. Si elige sus combinadores de una manera inteligente, creo que la cantidad de veces que tendrá que escribir unBi
constante a mano podría ser bastante limitada.
Después de todo, si sabe que una función es una biyección, es de esperar que tenga un bosquejo de prueba de ese hecho en su cabeza, que el isomorfismo de Curry-Howard debería poder convertir en un programa :-)
f x = 1
, el inverso de 1 es un conjunto de números enteros y el inverso de cualquier otra cosa es un conjunto vacío. Independientemente de lo que digan algunas respuestas, la función no ser biyectiva no es el mayor problema.