Round-robin algorithm in OCaml

喜夏-厌秋 提交于 2019-12-06 11:44:45
let round_robin ~nplayers ~round i =
  (* only works for an even number of players *)
  assert (nplayers mod 2 = 0);
  assert (0 <= round && round < nplayers - 1);
  (* i is the position of a match,
     at each round there are nplayers/2 matches *)
  assert (0 <= i && i < nplayers / 2);
  let last = nplayers - 1 in
  let player pos =
    if pos = last then last
    else (pos + round) mod last
  in
  (player i, player (last - i))

let all_matches nplayers =
  Array.init (nplayers - 1) (fun round ->
    Array.init (nplayers / 2) (fun i ->
      round_robin ~nplayers ~round i))

let _ = all_matches 6;;
(**
[|[|(0, 5); (1, 4); (2, 3)|];
  [|(1, 5); (2, 0); (3, 4)|];
  [|(2, 5); (3, 1); (4, 0)|];
  [|(3, 5); (4, 2); (0, 1)|];
  [|(4, 5); (0, 3); (1, 2)|]|]
*)

You don't need to compute the clockwise rotation by operating over actual data. You can represent it as picking indices in a fixed array (of things you rotate): after rotating the array t r times, the element at index i in the rotated array will be at index i+r in the original array, in fact (i+r) mod (Array.length t) to have wrap-around.

With this idea you could compute pairing without moving data around, simply incrementing a counter representing the number of rotations performed so far. In fact, you could probably even come up with a purely numerical solution that does not create any data structure (the array of things-to-rotate), and reasons on the various indices to apply this reasoning.

Although this question has been answered, but the correct answer is in an imperative way.

I finally found the following way to deal with round-robin algorithm simpler in functional way.

let round l1 l2 = let move = List.hd l2 in move::l1, (List.tl l2)@[move]

let combine m l1 l2 =
  let rec comb i acc = function
    |[], _ | _, [] -> acc
    |_ when i >= m -> acc
    |hd1::tl1, hd2::tl2 -> comb (i+1) ((hd1,hd2)::acc) (tl1,tl2)
  in
  comb 0 [] (l1,l2)

let round_robin l =
  let fix = List.hd l in
  let half = (List.length l)/2 in
  List.fold_left (
      fun (acc, (l1, l2)) _ -> (combine half (fix::l1) l2)::acc, round l1 l2
    ) ([], (List.tl l, List.rev l)) l |> fst |> List.tl
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!