1. (* 
  2.    Steve The Gambler: Caught by the IRS
  3.    A subset-sum problem with a constraint on the size of the subset to find
  4.    cameron {at} theaboutbox {dot} com
  5.    If you want to use this code, you can, without warranty.
  6. *)
  7. (* Steve's daily winnings *)
  8. let daily_winnings = [-50; -21; 13; 171; 14; -42; -58; 109; 4; 7; -23; 
  9.         -44; -98; -121; 101;  33; 87; -121; -40; -65; 43; 
  10.         54; -45; -12; -12; 38; 25; 3; 7; 8];;
  11. (* Splits a list into positive and negative sublists. Sorts the lists so the
  12.    numbers closest to zero are first. This way, if there is no ideal list, we
  13.    create the smallest non-ideal subset we can. *)
  14. let rec split_list l pos neg = match l with
  15.     [] -> ((List.sort compare pos),(List.rev (List.sort compare neg))) 
  16.   | h::t when h < 0 -> split_list t pos (h::neg)
  17.   | h::t -> split_list t (h::pos) neg;;
  18. (* Sums a list *)      
  19. let sum = List.fold_left (+) 0;;
  20. (* Prints an int list. Thanks, Andrew! *)
  21. let rec print_int_list = function
  22.   | [] -> ()
  23.   | x::xs -> print_int x; print_string " "; print_int_list xs;;
  24. (*
  25.   Subset sum solver that looks for a sublist of the given size that sums to zero.
  26.   Splits the list into positive and negative sublists to limit the number of
  27.   decisions at any point in searching the problem space. When the running sum is
  28.   positive, choose negative numbers and when the running sum is negative, choose 
  29.   positive numbers.
  30.   We employ some tricks to limit the effort spent searching because we know the size
  31.   of the subset we need to find. If we are deep enough in the search space that there 
  32.   is only one outcome, we return it immediately. If we run out of positive numbers
  33.   and the running sum is negative, we fill out the list with the smallest positive
  34.   numbers we can and vice versa.
  35. *)
  36. let subset_sum list size =
  37.   let (pos,neg) = split_list list [] [] in
  38.   (* Returns true if this list is the right size and sums to zero *)
  39.   let suitable l = (List.length l) = size && (sum l) = 0 in
  40.   (* Returns the best list: the one whose sum is closest to zero *)
  41.   let best_list l1 l2 = match (l1, l2) with
  42.       ([], l2) -> l2
  43.     | (l1, []) -> l1
  44.     | (l1, l2) -> let s1 = abs (sum l1) and s2 = abs (sum l2) in
  45.       if s1 < s2 then l1 else l2 in
  46.   let rec subset_aux l s p n = match (l,s,p,n) with
  47.       (list, sum, _, _) when (List.length list) = size -> list
  48.     (* Reached a dead end in the search space - only one possible outcome *)
  49.     | (list, _, p, n) when (List.length list) + (List.length p) + (List.length n) = size ->
  50.  list @ p @ n
  51.     (* Have a positive sum and only negative numbers: pile on the smallest neg #s possible *)
  52.     | (list, sum, [], n::t) when sum < 0 -> subset_aux (n::list) (sum+n) [] t 
  53.     (* Have a negative sum and only positive numbers: pile on the smallest pos #s possible *)
  54.     | (list, sum, p::t, []) when sum > 0 -> subset_aux (p::list) (sum+p) t []
  55.     (* Negative sum: pick a positive number and recurse *) 
  56.     | (list, sum, p::t, n) when sum <= 0 -> 
  57.  let with_list = subset_aux (p::list) (sum+p) t n in
  58.    if (suitable with_list) then with_list
  59.    else let without_list = subset_aux list sum t n in
  60.    best_list with_list without_list
  61.     (* Positive sum: pick a negative number and recurse *)
  62.     | (list, sum, p, n::t) ->
  63.  let with_list = subset_aux (n::list) (sum+n) p t in
  64.    if (suitable with_list) then with_list
  65.    else let without_list = subset_aux list sum p t in
  66.    best_list with_list without_list 
  67.   in
  68.     subset_aux [] 0 pos neg;;
  69. let read_days =
  70.   if (Array.length Sys.argv) = 1 then 9
  71.   else int_of_string Sys.argv.(1);;
  72. let _ = 
  73.   let best_list = subset_sum daily_winnings read_days in
  74.     print_string "Best List: ";
  75.     print_int_list best_list;
  76.     print_string "\n";;
  77. View this file Here (Right Click and save As)