1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| (* *)
(* Defi developpez.com : denombrement *)
(* *)
(* denombrement-defi4.ml (OCaml) *)
(* *)
(* (c) and left Egery 28/06/2008 *)
(* *)
(* Entree : n k *)
(* Sortie : nombre de matrices n*n *)
(* ayant k elements a 1 dans *)
(* chaque ligne et chaque *)
(* colonne (les autres a 0) *)
(* *)
(* Module precision arbritraire *)
#load "nums.cma";;
open Num;;
let big = num_of_int ;; (* Alias fonction ! *)
(* Pour chaque partie (sous-matrice), on recense *
* sa largeur et le nombre de 1 qu'elle contient en hauteur. *)
type partie = {taille: int ; cumul: int ; }
(* Fonction factorielle *)
let fact n =
let rec loop n acc =
assert (n>0) ;
match n with
| 0 | 1 -> acc
| _ -> loop (n-1) acc */ big n
in loop n (big 1) ;;
(* Donne le nombre de permutations pour une partition donnee *)
let evalue partition =
let rec loop partition i acc =
match partition with
| [] -> acc */ fact i
| hd::tl -> loop tl (i + hd.taille) (acc // fact hd.taille)
in loop partition 0 (big 1) ;;
(* Fontion de partitionnement, selon nombre jetons. Renvoie : *)
(* sous-partition, iterateur, jetons non places, cas ok, flag iteration *)
let insert n k partie jetons restants ligne =
let nb1 = min jetons partie.taille in assert (nb1 > 0) ;
let nb0 = partie.taille - nb1 in
match nb0, partie.cumul with
(* Cas 1 : Tous les 1 sont deja places *)
(* Cas 2 : Tous les 0 sont deja places, nb jetons suffisant *)
(* Cas 3 ; Tous les 0 sont deja places, nb jetons insuffisant *)
(* Cas 4 : Aucun 0 injecte *)
(* Cas 5 : Des 1 et des 0 injectes : subdivision en 2 matrices bloc *)
| _, c when c = k -> ([], 0, restants, false, true)
| _, c when (ligne-c = n-k) && (jetons >= partie.taille)
-> ( [{partie with cumul = c+1}], nb1-1, restants-nb1, true, true)
| _, c when (ligne-c = n-k) -> ([], 0, 0, false, false)
| 0, c -> ( [{partie with cumul=c+1}], nb1-1, restants-nb1, true, true)
| _, c -> ([{taille=nb1 ; cumul=c+1} ;
{taille=nb0 ; cumul=c }], nb1-1, restants-nb1, true, true)
(* Notre fonction principale, qui amorce le decompte *)
let denombre_matrices n k =
(* On construit les matrices convenables, *
* Et on decompte pour chaque le nombre de combinaison *)
let rec loop prefixe suffixe jetons_hd jetons_restant ligne acc =
match suffixe, jetons_hd, jetons_restant, ligne with
(* Cas 1 : matrice construite, on decompte *)
(* Cas 2 : Jetons tous places, on passe a ligne suivante *)
(* Cas 3 : Jetons restent a inserer, mais fin de ligne atteinte *)
(* Cas 4 : Jetons places dans partie suivante *)
(* Cas 5 : Sous partitionne selon jetons inseres, et itere cas suivant *)
| _ , _, _, l when l = n -> acc +/ evalue ( prefixe @ suffixe )
| _ , _, 0, _ -> loop [] (prefixe @ suffixe) k k (ligne+1) acc
| [], _, _, _ -> acc
| hd::tl, 0, jr, _ -> loop (prefixe @ [hd]) tl jr jr ligne acc
| hd::tl, jh, jr, _
-> let (hd', jh', jr', ok, itere) = insert n k hd jh jr ligne in
let acc2 = if ok then loop (prefixe@hd') tl jr' jr' ligne acc else acc
in if itere then loop prefixe suffixe jh' jr ligne acc2 else acc2
in loop [] [{taille = n ; cumul = 0}] k k 0 (big 0) ;;
let test n k = string_of_num (denombre_matrices n k) ;; |