Si vous pouvez énumérer le domaine de la fonction et comparer les éléments de la plage pour l'égalité, vous pouvez - d'une manière assez simple. Par énumérer, j'entends avoir une liste de tous les éléments disponibles. Je m'en tiens à Haskell, car je ne connais pas Ocaml (ni même comment le capitaliser correctement ;-)
Ce que vous voulez faire, c'est parcourir les éléments du domaine et voir s'ils sont égaux à l'élément de la plage que vous essayez d'inverser, puis prenez le premier qui fonctionne:
inv :: Eq b => [a] -> (a -> b) -> (b -> a)
inv domain f b = head [ a | a <- domain, f a == b ]
Puisque vous avez déclaré que f
c'est une bijection, il y a forcément un et un seul élément de ce type. L'astuce, bien sûr, est de vous assurer que votre énumération du domaine atteint réellement tous les éléments dans un temps fini . Si vous essayez d'inverser une bijection de Integer
en Integer
, l'utilisation [0,1 ..] ++ [-1,-2 ..]
ne fonctionnera pas car vous n'obtiendrez jamais les nombres négatifs. Concrètement,inv ([0,1 ..] ++ [-1,-2 ..]) (+1) (-3)
ne donnera jamais de valeur.
Cependant, 0 : concatMap (\x -> [x,-x]) [1..]
cela fonctionnera, car cela parcourt les entiers dans l'ordre suivant [0,1,-1,2,-2,3,-3, and so on]
. En effet inv (0 : concatMap (\x -> [x,-x]) [1..]) (+1) (-3)
revient rapidement -4
!
Le package Control.Monad.Omega peut vous aider à parcourir des listes de tuples, etc. d'une bonne manière; Je suis sûr qu'il y a plus de paquets comme ça - mais je ne les connais pas.
Bien sûr, cette approche est plutôt discrète et brutale, pour ne pas mentionner laide et inefficace! Je terminerai donc par quelques remarques sur la dernière partie de votre question, sur la manière d '«écrire» des bijections. Le système de types de Haskell n'est pas à la hauteur de prouver qu'une fonction est une bijection - vous voulez vraiment quelque chose comme Agda pour cela - mais il est prêt à vous faire confiance.
(Attention: le code non testé suit)
Alors pouvez-vous définir un type de données de Bijection
s entre les types a
et b
:
data Bi a b = Bi {
apply :: a -> b,
invert :: b -> a
}
avec autant de constantes (où vous pouvez dire `` Je sais que ce sont des bijections! '') que vous le souhaitez, telles que:
notBi :: Bi Bool Bool
notBi = Bi not not
add1Bi :: Bi Integer Integer
add1Bi = Bi (+1) (subtract 1)
et quelques combinateurs intelligents, tels que:
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)
Je pense que vous pourriez alors faire invert (mapBi add1Bi) [1,5,6]
et obtenir [0,4,5]
. Si vous choisissez vos combinateurs de manière intelligente, je pense que le nombre de fois que vous devrez écrire une Bi
constante à la main pourrait être assez limité.
Après tout, si vous savez qu'une fonction est une bijection, vous aurez, espérons-le, un croquis de preuve de ce fait dans votre tête, que l'isomorphisme de Curry-Howard devrait pouvoir transformer en programme :-)
f x = 1
, l'inverse de 1 est un ensemble d'entiers et l'inverse de toute autre chose est un ensemble vide. Indépendamment de ce que disent certaines réponses, la fonction n'étant pas bijective n'est pas le plus gros problème.