Rozważ ciąg binarny S
o długości n
. Indeksując od 1
, możemy obliczyć odległości Hamminga pomiędzy S[1..i+1]
i S[n-i..n]
dla wszystkich i
w kolejności od 0
do n-1
. Odległość Hamminga między dwoma strunami o równej długości jest liczbą pozycji, w których odpowiednie symbole są różne. Na przykład,
S = 01010
daje
[0, 2, 0, 4, 0].
Wynika to z tego, że 0
mecze 0
, 01
odległość Hamminga dwa do 10
, 010
mecze 010
, 0101
odległość Hamminga cztery do, 1010
a ostatecznie 01010
dopasowuje się.
Interesują nas jednak tylko wyjścia, w których odległość Hamminga wynosi najwyżej 1. W tym zadaniu Y
zgłosimy, czy odległość Hamminga wynosi co najwyżej jeden, a w N
przeciwnym razie. W powyższym przykładzie otrzymalibyśmy
[Y, N, Y, N, Y]
Zdefiniuj f(n)
liczbę odrębnych tablic Y
s i N
s, które otrzymujesz podczas iteracji wszystkich 2^n
różnych możliwych ciągów bitów S
długości n
.
Zadanie
Aby zwiększyć, n
zaczynając od 1
, twój kod powinien wypisywać f(n)
.
Przykładowe odpowiedzi
Dla n = 1..24
, poprawne odpowiedzi są:
1, 1, 2, 4, 6, 8, 14, 18, 27, 36, 52, 65, 93, 113, 150, 188, 241, 279, 377, 427, 540, 632, 768, 870
Punktacja
Twój kod powinien powtarzać się od n = 1
udzielenia odpowiedzi po n
kolei. Poświęcę cały bieg, zabijając go po dwóch minutach.
Twój wynik jest najwyższy n
w tym czasie.
W przypadku remisu pierwsza odpowiedź wygrywa.
Gdzie będzie testowany mój kod?
Uruchomię twój kod na moim (nieco starym) laptopie z systemem Windows 7 pod cygwin. W związku z tym proszę udzielić wszelkiej możliwej pomocy, aby ułatwić to.
Mój laptop ma 8 GB pamięci RAM i procesor Intel i7 [email protected] GHz (Broadwell) z 2 rdzeniami i 4 wątkami. Zestaw instrukcji obejmuje SSE4.2, AVX, AVX2, FMA3 i TSX.
Wiodące wpisy według języka
- n = 40 w Rust za pomocą CryptoMiniSat, autor: Anders Kaseorg. (W maszynie wirtualnej gościa Lubuntu pod Vbox.)
- n = 35 w C ++ przy użyciu biblioteki BuDDy, Christian Seviers. (W maszynie wirtualnej gościa Lubuntu pod Vbox.)
- n = 34 w Clingo autorstwa Andersa Kaseorga. (W maszynie wirtualnej gościa Lubuntu pod Vbox.)
- n = 31 w Rust przez Andersa Kaseorga.
- n = 29 w Clojure autorstwa NikoNyrh.
- n = 29 w C według bartavelle.
- n = 27 w Haskell według bartavelle
- n = 24 w Pari / gp przez alephalpha.
- n = 22 w Python 2 + pypy przeze mnie.
- n = 21 w Mathematica przez alephalpha. (Własne zgłoszenie)
Przyszłe nagrody
Dam teraz nagrodę w wysokości 200 punktów za każdą odpowiedź, która na mojej maszynie osiągnie n = 80 w ciągu dwóch minut.
Odpowiedzi:
Rdza + CryptoMiniSat , n ≈ 41
src/main.rs
Cargo.toml
Jak to działa
Dokonuje to rekurencyjnego przeszukiwania drzewa wszystkich częściowych przypisań do przedrostków tablicy Y / N, używając solwera SAT do sprawdzania na każdym kroku, czy bieżące częściowe przypisanie jest spójne i przycina wyszukiwanie, jeśli nie. CryptoMiniSat jest właściwym narzędziem SAT do tego zadania dzięki specjalnej optymalizacji klauzul XOR.
Trzy rodziny ograniczeń to
S i ⊕ S k + i ⊕ D ki , dla 1 ≤ k ≤ n - 2, 0 ≤ i ≤ n - k ;
D ki 1 ∨ D ki 2 ∨ ¬ A k , dla 1 ≤ k ≤ n - 2, 0 ≤ i 1 < i 2 ≤ n - k ;
¬ D ki 1 ∨ ⋯ ∨ ¬ D ki n - k - 1∨ A k , dla 1 ≤ k ≤ n - 2, 0 ≤ i 1 <⋯ < i n - k - 1 ≤ n - k ;
z wyjątkiem tego, że jako optymalizacja S 0 jest zmuszone do fałszu, tak że D k 0 jest po prostu równe S k .
źródło
cargo build
, otrzymuję--- stderr CMake Error: Could not create named generator Visual Studio 14 2015 Win64
Rdza, n ≈
30 lub31 lub 32Na moim laptopie (dwa rdzenie, i5-6200U), to przechodzi przez n = 1,…, 31 w 53 sekundach, używając około 2,5 GiB pamięci lub przez n = 1,…, 32 w 105 sekund, używając około 5 GiB pamięciowy. Kompiluj
cargo build --release
i uruchamiajtarget/release/correlations
.src/main.rs
Cargo.toml
Wypróbuj online!
Mam też nieco wolniejszy wariant, który wykorzystuje znacznie mniej pamięci.
źródło
C ++ przy użyciu biblioteki BuDDy
Inne podejście: zastosuj wzór binarny (jako binarny schemat decyzyjny ), który przyjmuje bity
S
jako dane wejściowe i jest prawdziwy iff, który daje pewne ustalone wartościY
lubN
w niektórych wybranych pozycjach. Jeśli ta formuła nie jest stała fałszywe, wybierz wolną pozycję i rekursja, próbując zarównoY
iN
. Jeśli nie ma wolnej pozycji, znaleźliśmy możliwą wartość wyjściową. Jeśli formuła ma stałą wartość false, cofnij się.Działa to względnie rozsądnie, ponieważ istnieje tak mało możliwych wartości, że często możemy wcześnie wycofać się. Wypróbowałem podobny pomysł z solwerem SAT, ale to się nie powiodło.
Aby skompilować z debianem 8 (jessie), zainstaluj
libbdd-dev
i wykonajg++ -std=c++11 -O3 -o hb hb.cpp -lbdd
. Przydatne może być zwiększenie pierwszego argumentu dobdd_init
jeszcze większego.źródło
Clingo, n ≈
30 lub 3134Byłem trochę zaskoczony, gdy pięć linii kodu Clingo wyprzedziło moje brutalne rozwiązanie Rust i zbliżyłem się bardzo do rozwiązania BuDDy Christiana - wygląda na to, że to też by go pobiło z wyższym limitem czasowym.
corr.lp
corr.sh
źródło
bdd_init
może pomóc lub pozwolić na zwiększenie tabeli węzłów, wywołującbdd_setmaxincrease
wartość znacznie przekraczającą domyślną wartość 50000. - Czy używasz zmienionej wersji mojego programu?--configuration=crafty
(jumpy
itrendy
daje podobne wyniki).Pari / GP , 23
Domyślnie Pari / GP ogranicza swój stos do 8 MB. Pierwszy wiersz kodu
default(parisize, "4g")
określa ten limit na 4 GB. Jeśli nadal daje przepływ stosu, możesz ustawić go na 8 GB.źródło
Clojure, 29 w
7538 sekund, 30 w 80 i 31 w 165Runtimes z Intel i7 6700K , zużycie pamięci jest mniejsze niż 200 MB.
project.clj (używa com.climate / claypoole do wielowątkowości):
Kod źródłowy:
Rozwiązanie brute-force, każdy wątek przekracza podzbiór zakresu (2 ^ 12 elementów) i buduje zestaw wartości całkowitych wskazujących wykryte wzorce. Są one następnie „łączone” razem, a zatem obliczana jest odrębna liczba. Mam nadzieję, że kod nie jest zbyt trudny do śledzenia, mimo że używa często makr wątków . Mój
main
test przeprowadza kilka razy, aby rozgrzać JVM.Aktualizacja: Iteracja tylko połowy liczb całkowitych daje ten sam wynik dzięki symetrii. Pomijanie liczb o większej liczbie bitów również w dolnej połowie liczby, ponieważ produkują również duplikaty.
Wbudowany uberjar ( v1 ) (3,7 MB):
Wyniki dla różnych programów sprzętowych, oczekiwany czas działania to
O(n * 2^n)
?Możesz łatwo uczynić to jedno-wątkowym i uniknąć zależności zewnętrznych, używając standardu dla:
Cóż, wbudowane pmap również istnieje, ale claypoole ma więcej funkcji i możliwości dostrajania.
źródło
Mathematica, n = 19
naciśnij alt +. przerwać, a wynik zostanie wydrukowany
źródło
Mathematica, 21 lat
Dla porównania odpowiedź Jenny_mathy podaje się
n = 19
na moim komputerze.Najwolniejsza część to
Tr@IntegerDigits[#, 2] &
. Szkoda, że Mathematica nie ma wbudowanej wagi Hamminga.Jeśli chcesz przetestować mój kod, możesz pobrać bezpłatną wersję próbną Mathematica .
źródło
Wersja AC z wbudowanym popcount
Działa lepiej
clang -O3
, ale działa również, jeśli maszgcc
.źródło
Haskell, (nieoficjalne n = 20)
To tylko naiwne podejście - jak dotąd bez żadnych optymalizacji. Zastanawiałem się, jak dobrze poradzi sobie z innymi językami.
Jak z niego korzystać (zakładając, że masz zainstalowaną platformę Haskell ):
approx_corr.hs
(lub innej nazwy, odpowiednio zmodyfikuj następujące kroki)ghc approx_corr.hs
approx_corr.exe
n
Kod:
źródło
main = putStrLn "Hello World!"
?Data.Bits
Moduł może być przydatna. W swojej głównej pętli możesz użyć czegoś takiego jakmain = do maxn <- getmax; t0 <- gettime; loop 1
gdzieloop n|n==maxn = return ()
iloop n = do printresult n (f n); t <- gettime; printtime (t-t0); loop (n+1)
.getmax
może na przykład użyć,getArgs
aby użyć argumentów programu.Rozwiązanie Haskell, wykorzystujące popCount i ręcznie zarządzaną równoległość
Skompilować:
ghc -rtsopts -threaded -O2 -fllvm -Wall foo.hs
(upuść,
-llvm
jeśli to nie działa)Biegać :
./foo +RTS -N
źródło
-O3
i może być szybsze z-O3 -fllvm
...Python 2 + pypy, n = 22
Oto naprawdę proste rozwiązanie Python jako rodzaj podstawowego testu porównawczego.
źródło