OCaml, 1588 (n = 36)
Diese Lösung verwendet den üblichen Bitmusteransatz, um Vektoren von -1s und 1s darzustellen. Das Skalarprodukt wird wie üblich berechnet, indem das xor von zwei Bitvektoren genommen und n / 2 subtrahiert wird. Vektoren sind orthogonal, wenn für ihr xor genau n / 2 Bits gesetzt sind.
Lyndon-Wörter sind an sich nicht als normalisierte Darstellung nützlich, da sie jedes Muster ausschließen, das eine Rotation von sich selbst ist. Sie sind auch relativ teuer zu berechnen. Daher verwendet dieser Code eine etwas einfachere Normalform, die erfordert, dass die längste aufeinanderfolgende Folge von Nullen nach der Drehung (oder eine von ihnen, wenn es mehrere gibt) die höchstwertigen Bits belegen muss. Daraus folgt, dass das niedrigstwertige Bit immer 1 ist.
Beachten Sie auch, dass jeder Kandidatenvektor mindestens n / 4 (und höchstens 3n / 4) haben muss. Wir betrachten daher nur Vektoren mit n / 4 ... n / 2 gesetzten Bits, da wir andere über Komplement und Rotation ableiten können (in der Praxis scheinen alle diese Vektoren zwischen n / 2-2 und n / 2 + 2 zu liegen , aber das scheint auch schwer zu beweisen zu sein).
Wir erstellen diese Normalformen vom niedrigstwertigen Bit an und beachten dabei die Einschränkung, dass alle verbleibenden Nullenläufe (im Code als "Lücken" bezeichnet) unseren normalen Formularanforderungen entsprechen müssen. Insbesondere muss, solange mindestens ein weiteres 1-Bit platziert werden muss, Platz für die aktuelle Lücke vorhanden sein und eine andere, die mindestens so groß ist wie die aktuelle Lücke oder eine andere bisher beobachtete Lücke.
Wir stellen auch fest, dass die Liste der Ergebnisse klein ist. Daher versuchen wir nicht, Duplikate während des Erkennungsprozesses zu vermeiden, sondern zeichnen die Ergebnisse einfach in Worker-Sets auf und berechnen die Vereinigung dieser Sets am Ende.
Es ist erwähnenswert, dass die Laufzeitkosten des Algorithmus immer noch exponentiell und mit einer Geschwindigkeit steigen, die mit der der Brute-Force-Version vergleichbar ist. Dies bringt uns im Wesentlichen eine Reduzierung um einen konstanten Faktor und geht zu Lasten eines Algorithmus, der schwieriger zu parallelisieren ist als die Brute-Force-Version.
Ausgabe für n bis 40:
4: 12
8: 40
12: 144
16: 128
20: 80
24: 192
28: 560
32: 0
36: 432
40: 640
Das Programm ist in OCaml geschrieben und kann kompiliert werden mit:
ocamlopt -inline 100 -nodynlink -o orthcirc unix.cmxa bigarray.cmxa orthcirc.ml
Führen Sie aus, um ./orthcirc -help
zu sehen, welche Optionen das Programm unterstützt.
Bei Architekturen, die dies unterstützen, -fno-PIC
kann dies zu einem kleinen zusätzlichen Leistungsgewinn führen.
Dies ist für OCaml 4.02.3 geschrieben, funktioniert aber möglicherweise auch mit älteren Versionen (sofern diese nicht zu alt sind).
UPDATE: Diese neue Version bietet eine bessere Parallelisierung. Beachten Sie, dass p * (n/4 + 1)
pro Instanz des Problems Arbeitsthreads verwendet werden und einige von ihnen immer noch erheblich kürzer als andere ausgeführt werden. Der Wert von p
muss eine Potenz von 2 sein. Die Beschleunigung bei 4-8 Kernen ist minimal (möglicherweise um 10%), lässt sich jedoch bei großen Kernen besser auf eine große Anzahl von Kernen skalieren n
.
let max_n = ref 40
let min_n = ref 4
let seq_mode = ref false
let show_res = ref false
let fanout = ref 8
let bitcount16 n =
let b2 n = match n land 3 with 0 -> 0 | 1 | 2 -> 1 | _ -> 2 in
let b4 n = (b2 n) + (b2 (n lsr 2)) in
let b8 n = (b4 n) + (b4 (n lsr 4)) in
(b8 n) + (b8 (n lsr 8))
let bitcount_data =
let open Bigarray in
let tmp = Array1.create int8_signed c_layout 65536 in
for i = 0 to 65535 do
Array1.set tmp i (bitcount16 i)
done;
tmp
let bitcount n =
let open Bigarray in
let bc n = Array1.unsafe_get bitcount_data (n land 65535) in
(bc n) + (bc (n lsr 16)) + (bc (n lsr 32)) + (bc (n lsr 48))
module IntSet = Set.Make (struct
type t = int
let compare = Pervasives.compare
end)
let worker_results = ref IntSet.empty
let test_row vec row mask n =
bitcount ((vec lxor (vec lsr row) lxor (vec lsl (n-row))) land mask) * 2 = n
let record vec len n =
let m = (1 lsl n) - 1 in
let rec test_orth_circ ?(row=2) vec m n =
if 2 * row >= n then true
else if not (test_row vec row m n) then false
else test_orth_circ ~row:(row+1) vec m n
in if test_row vec 1 m n &&
test_orth_circ vec m n then
begin
for i = 0 to n - 1 do
let v = ((vec lsr i) lor (vec lsl (n - i))) land m in
worker_results := IntSet.add v !worker_results;
worker_results := IntSet.add (v lxor m) !worker_results
done
end
let show vec n =
for i = 0 to n / 2 - 1 do
let vec' = (vec lsr i) lor (vec lsl (n - i)) in
for j = 0 to n-1 do
match (vec' lsr (n-j)) land 1 with
| 0 -> Printf.printf " 1"
| _ -> Printf.printf " -1"
done; Printf.printf "\n"
done; Printf.printf "\n"; flush stdout
let rec build_normalized ~prefix ~plen ~gap ~maxgap ~maxlen ~bits ~fn =
if bits = 0 then
fn prefix plen maxlen
else begin
let room = maxlen - gap - plen - bits in
if room >= gap && room >= maxgap then begin
build_normalized
~prefix:(prefix lor (1 lsl (plen + gap)))
~plen:(plen + gap + 1)
~gap:0
~maxgap:(if gap > maxgap then gap else maxgap)
~maxlen
~bits:(bits - 1)
~fn;
if room > gap + 1 && room > maxgap then
build_normalized ~prefix ~plen ~gap:(gap + 1) ~maxgap ~maxlen ~bits ~fn
end
end
let rec log2 = function
| 0 -> -1
| n -> 1 + (log2 (n lsr 1))
let rec test_gap n pat =
if n land pat = 0 then true
else if pat land 1 = 0 then test_gap n (pat lsr 1)
else false
let rec test_gaps n maxlen len =
let fill k = (1 lsl k) -1 in
if len = 0 then []
else if test_gap n ((fill maxlen) lxor (fill (maxlen-len))) then
len :: (test_gaps n maxlen (len-1))
else test_gaps n maxlen (len-1)
let rec longest_gap n len =
List.fold_left max 0 (test_gaps n len len)
let start_search low lowbits maxlen bits fn =
let bits = bits - (bitcount low) in
let plen = log2 low + 1 in
let gap = lowbits - plen in
let maxgap = longest_gap low lowbits in
worker_results := IntSet.empty;
if bits >= 0 then
build_normalized ~prefix:low ~plen ~gap ~maxgap ~maxlen ~bits ~fn;
!worker_results
let spawn f x =
let open Unix in
let safe_fork () = try fork() with _ -> -1 in
let input, output = pipe () in
let pid = if !seq_mode then -1 else safe_fork() in
match pid with
| -1 -> (* seq_mode selected or fork() failed *)
close input; close output; (fun () -> f x)
| 0 -> (* child process *)
close input;
let to_parent = out_channel_of_descr output in
Marshal.to_channel to_parent (f x) [];
close_out to_parent; exit 0
| pid -> (* parent process *)
close output;
let from_child = in_channel_of_descr input in
(fun () ->
ignore (waitpid [] pid);
let result = Marshal.from_channel from_child in
close_in from_child; result)
let worker1 (n, k) =
start_search 1 1 n k record
let worker2 (n, k, p) =
start_search (p * 2 + 1) (log2 !fanout + 1) n k record
let spawn_workers n =
let queue = Queue.create () in
if n = 4 || n = 8 then begin
for i = n / 4 to n / 2 do
Queue.add (spawn worker1 (n, i)) queue
done
end else begin
for i = n / 2 downto n / 4 do
for p = 0 to !fanout - 1 do
Queue.add (spawn worker2 (n, i, p)) queue
done
done
end;
Queue.fold (fun acc w -> IntSet.union acc (w())) IntSet.empty queue
let main () =
if !max_n > 60 then begin
print_endline "error: cannot handle n > 60";
exit 1
end;
min_n := max !min_n 4;
if bitcount !fanout <> 1 then begin
print_endline "error: number of threads must be a power of 2";
exit 1;
end;
for n = !min_n to !max_n do
if n mod 4 = 0 then
let result = spawn_workers n in
Printf.printf "%2d: %d\n" n (IntSet.cardinal result);
if !show_res then
IntSet.iter (fun v -> show v n) result;
flush stdout
done
let () =
let args =[("-m", Arg.Set_int min_n, "min size of the n by n/2 matrix");
("-n", Arg.Set_int max_n, "max size of the n by n/2 matrix");
("-p", Arg.Set_int fanout, "parallel fanout");
("-seq", Arg.Set seq_mode, "run in single-threaded mode");
("-show", Arg.Set show_res, "display list of results") ] in
let usage = ("Usage: " ^
(Filename.basename Sys.argv.(0)) ^
" [-n size] [-seq] [-show]") in
let error _ = Arg.usage args usage; exit 1 in
Arg.parse args error usage;
main ()
n
, das ein Vielfaches von vier ist?