OCaml、1588(n = 36)
このソリューションでは、通常のビットパターンアプローチを使用して、-1と1のベクトルを表します。通常、スカラー積は、2つのビットベクトルのxorを取り、n / 2を引くことによって計算されます。ベクトルは、それらのxorが正確にn / 2ビットセットを持っている場合に限り直交します。
リンドンの単語は、それ自体の回転であるパターンを除外するため、これ自体は正規化された表現としては役に立ちません。また、計算に比較的コストがかかります。したがって、このコードはやや単純な正規形を使用します。これは、ローテーション後の最長の連続するゼロのシーケンス(または複数ある場合はそのうちの1つ)が最上位ビットを占有する必要があることを要求します。したがって、最下位ビットは常に1になります。
また、すべての候補ベクトルには少なくともn / 4個(最大で3n / 4個)のベクトルが必要であることに注意してください。したがって、n / 4 ... n / 2ビットが設定されたベクトルのみを考慮します。これは、補数と回転を介して他を導出できるためです(実際には、そのようなベクトルはすべてn / 2-2とn / 2 + 2の間にあるようです。 、しかしそれも証明するのは難しいようです)。
これらの正規形を最下位ビットから構築し、残りのゼロの実行(コードでは「ギャップ」と呼ばれます)は、正規形の要件に従う必要があるという制約を守ります。特に、1つ以上の1ビットを配置する必要がある限り、現在のギャップのための余地がなければならず、それ以外のスペースは、現在のギャップまたはこれまでに観測された他のギャップと同じ大きさでなければなりません。
また、結果のリストが少ないこともわかります。したがって、ディスカバリプロセス中に重複を回避しようとはせず、単にワーカーごとのセットに結果を記録し、最後にこれらのセットの結合を計算します。
アルゴリズムの実行時のコストが依然として指数関数的に増加し、ブルートフォースバージョンに匹敵する速度で増加することは注目に値します。これが私たちにもたらすものは、本質的に一定の要因による削減であり、ブルートフォースバージョンよりも並列化が難しいアルゴリズムを犠牲にしています。
最大40のnの出力:
4: 12
8: 40
12: 144
16: 128
20: 80
24: 192
28: 560
32: 0
36: 432
40: 640
プログラムはOCamlで記述されており、以下でコンパイルされます。
ocamlopt -inline 100 -nodynlink -o orthcirc unix.cmxa bigarray.cmxa orthcirc.ml
実行./orthcirc -help
して、プログラムがサポートするオプションを確認します。
それをサポートするアーキテクチャで-fno-PIC
は、いくつかの小さな追加のパフォーマンス向上を提供する可能性があります。
これはOCaml 4.02.3用に書かれていますが、古いバージョンでも動作する可能性があります(古すぎない限り)。
更新:この新しいバージョンは、より良い並列化を提供します。p * (n/4 + 1)
問題のインスタンスごとにワーカースレッドを使用することに注意してください。一部のスレッドは、他のスレッドよりもかなり短時間で実行されます。の値はp
2の累乗である必要があります。4〜8コアでのスピードアップは最小限(おそらく10%程度)ですが、大規模なコアの場合、コア数が多いほどスケールアップし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
に4の倍数であるすべてを1つ見つけることができますか?