Tháng 10, 1588 (n = 36)
Giải pháp này sử dụng cách tiếp cận mẫu bit thông thường để biểu diễn các vectơ -1 và 1. Sản phẩm vô hướng được tính như bình thường bằng cách lấy xor của hai vectơ bit và trừ đi n / 2. Các vectơ là trực giao nếu xor của chúng có chính xác n / 2 bit được đặt.
Các từ Lyndon không hữu ích như một đại diện được chuẩn hóa cho điều này, vì chúng loại trừ bất kỳ mẫu nào là một vòng quay của chính nó. Chúng cũng tương đối đắt tiền để tính toán. Do đó, mã này sử dụng một hình thức bình thường có phần đơn giản hơn, đòi hỏi chuỗi số 0 liên tiếp dài nhất sau khi quay (hoặc một trong số chúng, nếu có bội số) phải chiếm các bit quan trọng nhất. Theo sau đó, bit có ý nghĩa nhỏ nhất luôn là 1.
Cũng lưu ý rằng bất kỳ vectơ ứng cử viên nào cũng phải có ít nhất n / 4 cái (và nhiều nhất là 3n / 4). Do đó, chúng tôi chỉ xem xét các vectơ có n / 4 ... n / 2 bit được đặt, vì chúng tôi có thể lấy được các vectơ khác thông qua bổ sung và xoay (trong thực tế, tất cả các vectơ như vậy dường như có từ n / 2-2 đến n / 2 + 2 , nhưng điều đó dường như cũng khó chứng minh).
Chúng tôi xây dựng các hình thức bình thường này từ bit có ý nghĩa nhỏ nhất, quan sát ràng buộc rằng mọi lần chạy số 0 còn lại (được gọi là "khoảng trống" trong mã) phải tuân theo yêu cầu biểu mẫu thông thường của chúng tôi. Cụ thể, miễn là phải đặt ít nhất 1 bit nữa, thì phải có khoảng trống cho khoảng cách hiện tại và khoảng trống khác ít nhất là khoảng cách hiện tại hoặc bất kỳ khoảng cách nào khác được quan sát cho đến nay.
Chúng tôi cũng quan sát rằng danh sách kết quả là nhỏ. Do đó, chúng tôi không cố gắng tránh trùng lặp trong quá trình khám phá mà chỉ ghi lại kết quả trong các bộ công nhân và tính toán kết hợp của các bộ này ở cuối.
Điều đáng chú ý là chi phí thời gian chạy của thuật toán vẫn tăng theo cấp số nhân và với tốc độ tương đương với phiên bản brute-force; những gì điều này mua cho chúng ta về cơ bản là giảm theo một yếu tố không đổi, và phải trả giá bằng một thuật toán khó song song hơn so với phiên bản brute-force.
Đầu ra cho n lên tới 40:
4: 12
8: 40
12: 144
16: 128
20: 80
24: 192
28: 560
32: 0
36: 432
40: 640
Chương trình được viết bằng OCaml, được biên dịch với:
ocamlopt -inline 100 -nodynlink -o orthcirc unix.cmxa bigarray.cmxa orthcirc.ml
Chạy ./orthcirc -help
để xem những tùy chọn mà chương trình hỗ trợ.
Trên các kiến trúc hỗ trợ nó, -fno-PIC
có thể cung cấp một số tăng hiệu suất bổ sung nhỏ.
Điều này được viết cho OCaml 4.02.3, nhưng cũng có thể hoạt động với các phiên bản cũ hơn (miễn là chúng không quá cũ).
CẬP NHẬT: Phiên bản mới này cung cấp song song tốt hơn. Lưu ý rằng nó sử dụng các p * (n/4 + 1)
luồng công nhân cho mỗi trường hợp của vấn đề và một số trong số chúng vẫn sẽ chạy ngắn hơn đáng kể so với các luồng khác. Giá trị p
phải là một công suất bằng 2. Tốc độ tăng tốc trên 4-8 lõi là tối thiểu (có thể khoảng 10%), nhưng nó có tỷ lệ tốt hơn với số lượng lớn các lõi lớn 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
đó là bội số của bốn?