(* * Copyright (c) 2005, 2006, 2007 Abram Hindle * * This file is part of CaptchaBreaker * CaptchaBreaker is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * Foobar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . *) type lapjv_struct = { mutable f: int; mutable h: float; mutable i: int; mutable k: int; mutable j: int; mutable f0: int; mutable i1: int; mutable j1: int; mutable j2: int; mutable u1: float; mutable u2: float; mutable min: float; mutable imin: int; mutable last: int; mutable low: int; mutable up: int; mutable first: bool; mutable res: float; x : int array ; (* cols -> row *) y : int array ; (* rows -> cols *) matches : int array; u : float array ; (* dual row vars *) v : float array ; (* dual column vars *) col : int array ; (* array of cols *) d : float array ; (* shortest path lengths *) free : int array ; (* unassigned rows *) pred : int array ; (* pred-array - shortest path tree *) };; let create_lapjv n = let q = { f = 0; h = 0.; i = 0; k = 0; j = 0; f0 = 0; i1 = 0; j1 = 0; j2 = 0; u1 = 0.; u2 = 0.; min = 0.; imin = 0; last = 0; low = 0; up = 0; first = true; res = 0.; x = Array.create n 0; (* cols -> row *) y = Array.create n 0; (* rows -> cols *) u = Array.create n 0.; (* dual row vars *) v = Array.create n 0.; (* dual column vars *) matches = Array.create n 0; (* array of cols *) col = Array.create n 0; (* array of cols *) d = Array.create n 0.; (* shortest path lengths *) free = Array.create n 0; (* unassigned rows *) pred = Array.create n 0; (* pred-array - shortest path tree *) } in q ;; exception Augment;; exception Zero_Error;; exception GotoAugment of int;; exception BadMatches;; exception LoopAgain of lapjv_struct ;; exception ReturnValue of float;; let zero_to n = let rec r = function k when k >= n -> [] | k when k >= 0 -> k :: (r (k+1)) | _ -> raise Zero_Error in r 0 ;; let s_lapjv n costmatrix q = let inf = max_float in let zero_to_n = zero_to n in let n_to_zero = List.rev zero_to_n in let cost x y = costmatrix.(x).(y) in (* let (f,h,i,j,k,f0,i1,j1,j2,u1,u2,min,last,low,up) *) (* for i = 0 to (n-1) do x.(i) <- 0 done *) (* COLUMN REDUCTION *) List.iter ( fun j_ -> (* q.col.(j) <- j; *) q.j <- j_; q.i1 <- 0; q.min <- cost 0 q.j; for i = 1 to (n-1) do (* #1 *) q.i <- i; if (cost q.i q.j) < q.h then begin q.h <- cost q.i q.j; q.i1 <- i; end; done; (* #1 *) q.v.(q.j) <- q.min; q.matches.(q.i1) <- q.matches.(q.i1) + 1; if (q.matches.(q.i1) = 1) then begin q.x.(q.i1) <- q.j; q.y.(q.j) <- q.i1; end else begin (* q.x.(q.i) <- (-1)* abs q.x.(q.i); *) q.y.(q.j) <- (-1); end; ) n_to_zero; prerr_endline "Inited"; for i = 0 to (n-1) do (* #2 *) q.i <- i; if (q.matches.(q.i) = 0) then begin q.free.(q.f) <- q.i; q.f <- q.f + 1; end (* else if (q.x.(i) < 0) then begin q.x.(i) <- (-1) * q.x.(i) end *) else if (q.matches.(i) = 1) then begin q.j1 <- q.x.(i); q.min <- inf; for j = 0 to (n-1) do (* #3 *) q.j <- j; if (not(q.j = q.j1)) then begin (* if c[i,j]-v[j]= q.u1 then begin q.u2 <- q.h; q.j2 <- q.j; end else begin q.u2 <- q.u1; q.u1 <- q.h; q.j2 <- q.j1; q.j1 <- q.j end; end; done; (* #4 *) q.i1 <- q.y.(q.j1) ; if (q.u1 < q.u2) then begin q.v.(q.j1) <- q.v.(q.j1) -. q.u2 +. q.u1 end else if (q.i1 >= 0) then begin q.j1 <- q.j2; q.i1 <- q.y.(q.j2) end; q.x.(q.i) <- q.j1; q.y.(q.j1) <- q.i; (* prerr_endline ((string_of_int q.k) ^ " " ^ (string_of_int q.i) ^ " " ^(string_of_float q.u1) ^ " " ^(string_of_float q.u2)); *) let fabs x = if (x >= 0.) then x else (-1.0) *. x in let near x y = (fabs (x -. y)) < ((fabs (x +. y)) *. 0.0000001) in (* let near x y = ((fabs (x -. y)) < 0.0000001) in *) if (q.i1 >= 0) then begin if ((not (near q.u1 q.u2)) && (q.u1 < q.u2)) then begin q.k <- q.k - 1; q.free.(q.k) <- q.i1; end (* if (q.u1 < q.u2) then begin q.k <- q.k - 1; q.free.(q.k) <- q.i1; end *) else begin q.f <- q.f + 1;q.free.(q.f) <- q.i1 ; end; (* from cpp *) end; done; (* WHILE#1 *) in (* block end *) (* repeat block twice *) block 0; block 1; prerr_endline "Blk2"; (* augmentation *) q.f0 <- q.f; for f = 0 to (q.f0 - 1) do (* #5 *) q.f <- f; q.i1 <- q.free.(q.f); for j_ = 0 to (n-1) (* #6 *) do q.j <- j_; q.d.(q.j) <- (cost q.i1 q.j) -. q.v.(q.j); q.pred.(q.j) <- q.i1 done; (* #6 *) (* might not need this initalization but didn't feel safe *) q.low <- 0; q.up <- 0; (* prerr_endline "Blk3"; *) try while (true) do (* WHILE #2 *) if (q.up = q.low) then begin q.last <- q.low - 1; if (q.up >= n) then raise (GotoAugment 0); q.min <- q.d.(q.col.(q.up)); q.up <- q.up + 1; for k_ = q.up to (n-1) do (* #7 *) q.k <- k_; q.j <- q.col.(q.k); q.h <- q.d.(q.j); if (q.h <= q.min) then begin if (q.h < q.min) then begin q.up <- q.low ; q.min <- q.h end; q.col.(q.k) <- q.col.(q.up); q.col.(q.up) <- q.j; q.up <- q.up + 1; end; done; (* #7 *) for r = q.low to (q.up - 1) do (* #8 *) if (q.y.(q.col.(r)) < 0) then raise (GotoAugment q.col.(r)); (* end of path *) done; (* #8 *) end; (* FYI Still in the while loop *) q.j1 <- q.col.(q.low); q.low <- q.low + 1; q.i <- q.y.(q.j1); q.h <- (cost q.i q.j1) -. q.v.(q.j1) -. q.min; (* prerr_endline "Blk4"; *) (* CHECK WAS THIS IN THE PAPER? q.u1 <- (cost q.i q.j1) -. q.v.(q.j1) -. q.min; *) for k_ = q.up to (n-1) do (* #9 *) q.k <- k_; q.j <- q.col.(q.k); q.u2 <- (cost q.i q.j) -. q.v.(q.j) -. q.h; if (q.u2 < q.d.(q.j)) then begin q.pred.(q.j) <- q.i; if (q.u2 = q.min) then begin if (q.y.(q.j) < 0) then raise (GotoAugment (q.j)) (* end path *) else begin q.col.(q.k) <- q.col.(q.up); q.col.(q.up) <- q.j; q.up <- q.up + 1; end; end; q.d.(q.j) <- q.u2; end; done; (* #9 *) done; (* #WHILE 2 *) with (GotoAugment(x)) -> q.j <- x; (* label Augment *) (* prerr_endline "Augmented"; *) (* prerr_endline "Augmented"; *) for k = 0 to q.last do (* #10 *) q.k <- k; q.j1 <- q.col.(q.k); q.v.(q.j1) <- q.v.(q.j1) +. q.d.(q.j1) -. q.min; done; (* #10 *) q.first <- true ; (* let the first pass work *) while (q.first || not(q.i = q.i1)) do (* start while *) q.first <- false; q.i <- q.pred.(q.j); q.y.(q.j) <- q.i; q.j1 <- q.j; q.j <- q.x.(q.i); q.x.(q.i) <- q.j1; done; (* end while *) done; (* augmentation done *) (* check with pascal *) q.res <- 0.; for i_ = 0 to (n-1) do (* #11 *) q.i <- i_; q.j <- q.x.(q.i); q.u.(q.i) <- (cost q.i q.j) -. q.v.(q.j); q.res <- q.res +. (cost q.i q.j); done; (* #11 *) q.res ;; let for_map f max = let rec foreach = function x when x >= max -> [] | x -> (f x) :: (foreach (x+1)) in foreach 0 ;; let cost_matrix_to_string n matrix = let strs = for_map (fun i -> let strs = for_map (fun j -> string_of_float matrix.(i).(j) ) n in (String.concat "\t" strs) ) n in (String.concat "\n" strs)^"\n" ;; let lapjv n costmatrix = let q = create_lapjv n in try let res = s_lapjv n costmatrix q in (res,q.x,q.y) with (Invalid_argument(x)) -> let cm = cost_matrix_to_string n costmatrix in let _ = print_endline cm in let fd = open_out "./cost_matrix.debug" in let _ = output_string fd cm in let _ = close_out fd in raise (Invalid_argument (" Bad matrix " ^ x )); ;; let verify_solution n x y = let si = string_of_int in for i = 0 to (n - 1) do if (x.(y.(i)) != i) then print_endline (" x o y " ^ (si i) ^ " "^(si (x.(y.(i))))); if (y.(x.(i)) != i) then print_endline (" y o x " ^ (si i) ^ " "^(si (y.(x.(i))))); done; ;; let lapjv_test () = Random.self_init (); let n = 30 in let c = Array.make_matrix n n 1. in for x = 0 to (n-1) do for y = 0 to (n-1) do c.(x).(y) <- 1. +. (Random.float 10.); done; done; print_endline "OK READY"; let (res, l,l2) = lapjv n c in print_float res; print_newline (); Array.iter (fun x -> print_int x; print_string " "; ) l; print_newline (); Array.iter (fun x -> print_int x; print_string " "; ) l2; print_newline (); verify_solution n l l2; ;; let lapjv_test_2 () = let c = [| [| 0.0 ; 1.0 ; 2.0 ; 2.0 |] ; [| 1.0 ; 0.0 ; 2.0 ; 2.0 |] ; [| 2.0 ; 2.0 ; 0.0 ; 1.0 |] ; [| 2.0 ; 2.0 ; 1.0 ; 0.0 |] |] in let (res,l,l') = lapjv 4 c in l = [| 0 ; 1 ; 2 ; 3 |] ;; let lapjv_test_2 () = (* 234_ __21 1___ _3__ ____ _4__ *) let c = [| [| 4.0 ; 3.0 ; 2.0 ; 3.0 |] ; [| 3.0 ; 2.0 ; 2.0 ; 3.0 |] ; [| 2.0 ; 1.0 ; 1.0 ; 2.0 |] ; [| 1.0 ; 0.0 ; 1.0 ; 2.0 |] |] in (* 3 0 1 1 ?? *) (* 3 2 1 0 *) (* 3 2 0 1 *) lapjv 4 c (* let (res,l,l') = lapjv 4 c in l = [| 0 ; 1 ; 2 ; 3 |] *) ;; let shuffle n perm = for i = 0 to (n-1) do let r = Random.int n in let i' = perm.(i) in perm.(i) <- perm.(r); perm.(r) <- i'; done; ;; let abez_permuter n costmatrix = let perm = Array.of_list (zero_to n) in let not_chosen = Array.create n true in let finalperm = Array.of_list (zero_to n) in shuffle n perm; let sum = ref 0. in for i = 0 to (n-1) do let j = perm.(i) in let d = ref infinity in let di = ref 0 in for k = 0 to (n - 1) do let d' = costmatrix.(i).(k) in if (d' < !d) then begin d := d'; di := k; end; done; not_chosen.(!di) <- false; finalperm.(j) <- !di; sum := !sum +. !d; done; (!sum,finalperm,finalperm) ;; let min_cost_assignment n costmatrix = let finalperm = Array.of_list (zero_to n) in let sum = ref 0. in for i = 0 to (n - 1) do let minarg = ref 0 in for k = 0 to (n - 1) do if (costmatrix.(i).(k) < costmatrix.(i).(!minarg)) then minarg := k done; finalperm.(i) <- !minarg; sum := !sum +. costmatrix.(i).(!minarg); done; (!sum, finalperm, finalperm) ;;