Napisz program, który narysuje 2-wymiarowy schemat węzła na podstawie jego struktury. Węzeł jest dokładnie tak, jak to brzmi: pętla liny, która jest związana. W matematyce schemat węzła pokazuje, gdzie kawałek liny przecina się nad lub pod sobą, tworząc węzeł. Niektóre przykładowe diagramy węzłów pokazano poniżej:
W linii przecina się lina.
Dane wejściowe: dane wejściowe opisujące węzeł to tablica liczb całkowitych. Węzeł, w którym lina przecina się n razy, może być reprezentowany jako tablica n liczb całkowitych, każda o wartości z zakresu [0, n-1]. Nazwijmy tę tablicę K. .
Aby uzyskać tablicę opisującą węzeł, numeruj każdy z segmentów od 0 do n-1. Odcinek 0 powinien prowadzić do odcinka 1, który powinien prowadzić do odcinka 2, który powinien prowadzić do odcinka 3 i tak dalej, dopóki odcinek n-1 nie zapętli się i nie dojdzie do odcinka 0. Segment kończy się, gdy przechodzi przez niego kolejny odcinek liny ( reprezentowane przez przerwanie linii na schemacie). Weźmy najprostszy węzeł - węzeł koniczyny. Po ponumerowaniu segmentów segment 0 kończy się, gdy segment 2 przechodzi nad nim; segment 1 kończy się, gdy segment 0 przechodzi nad nim; a segment 2 kończy się, gdy przecina go segment 1. Zatem tablica opisująca węzeł to [2, 0, 1]. Ogólnie segment x zaczyna się tam, gdzie segment , gdzie zakończył x-1 mod n , i kończy się, gdy przecina go segment K [x] .
Poniższy obrazek pokazuje schematy węzłów z oznaczonymi segmentami i odpowiednimi tablicami opisującymi węzeł.
Trzy górne schematy to prawdziwe węzły, a dolne trzy to pętle liny, które przecinają się, ale nie są tak naprawdę wiązane (ale nadal mają odpowiednie kody).
Twoim zadaniem jest napisanie funkcji, która pobiera tablicę liczb całkowitych K (można to nazwać czymś innym), która opisuje węzeł (lub pętlę liny, która nie jest faktycznie wiązana) i która generuje odpowiedni schemat węzłów, jak opisano powyżej przykłady Jeśli możesz, podaj niestosowną wersję kodu lub wyjaśnienie, a także podaj przykładowe wyniki swojego kodu. Ten sam węzeł może być często reprezentowany na wiele różnych sposobów, ale jeśli schemat węzła, który spełnia dane wyjściowe funkcji, zawiera dane wejściowe jako jedną z możliwych reprezentacji, rozwiązanie jest prawidłowe.
To jest golf golfowy, więc wygrywa najkrótszy kod w bajtach. Linia reprezentująca linę może mieć grubość 1 piksela, jednak pod i nad skrzyżowaniami należy wyraźnie odróżnić (rozmiar zerwania liny powinien być większy niż grubość liny o co najmniej piksel z każdej strony) .
Będę głosować za odpowiedziami, które opierają się na wbudowanych możliwościach teorii węzłów, ale ta, która ostatecznie została wybrana, nie może polegać na wbudowanych możliwościach teorii węzłów.
Wszystko, co wiem o mojej notacji: wierzę, że istnieją sekwencje wartości, które wydają się nie odpowiadać żadnemu węzłu lub rozłączeniu. Na przykład ciąg [2, 3, 4, 0, 1] wydaje się niemożliwy do narysowania.
Poza tym załóżmy, że bierzesz skrzyżowanie i zaczynając od tego skrzyżowania, podążaj ścieżką liny w jednym kierunku i oznaczaj każde nieznakowane przejście, które napotkasz, kolejnymi większymi wartościami całkowitymi. W przypadku naprzemiennych węzłów istnieje prosty algorytm do konwersji mojej notacji na takie etykietowanie, a w przypadku naprzemiennych węzłów banalne jest przekształcenie tego oznakowania w kod Gaussa:
template<size_t n> array<int, 2*n> LabelAlternatingKnot(array<int, n> end_at)
{
array<int, n> end_of;
for(int i=0;i<n;++i) end_of[end_at[i]] = i;
array<int, 2*n> p;
for(int& i : p) i = -1;
int unique = 0;
for(int i=0;i<n;i++)
{
if(p[2*i] < 0)
{
p[2*i] = unique;
p[2*end_of[i] + 1] = unique;
++unique;
}
if(p[2*i+1] < 0)
{
p[2*i+1] = unique;
p[2*end_at[i]] = unique;
++unique;
}
}
return p;
}
template<size_t n> auto GetGaussCode(array<int, n> end_at)
{
auto crossings = LabelAlternatingKnot(end_at);
for(int& i : crossings) ++i;
for(int i=1;i<2*n;i+=2) crossings[i] = -crossings[i];
return crossings;
}
źródło
KnotData
w Mathematica ...: '(Knot
wbudowane! Przykładowe użycie:<< Units`; Convert[Knot, Mile/Hour]
daje1.1507794480235425 Mile/Hour
. (Myślę, że jest to śmieszne niezależnie od tego, czy jest to prawda, czy fałsz; ale tak naprawdę to prawda.)Odpowiedzi:
GNU Prolog,
622634668 bajtów na stronie kodowej 850Aktualizacja : poprzednia wersja programu czasami powodowała, że przejścia były tak ciasne, że nie renderowały się poprawnie, co narusza specyfikację. Dodałem dodatkowy kod, aby temu zapobiec.
Aktualizacja : Najwyraźniej reguły PPCG wymagają dodatkowego kodu, aby program mógł wyjść i przywrócić stan dokładnie tak, jak na początku. To sprawia, że program jest nieco dłuższy i nie dodaje do niego żadnych zainteresowań algorytmicznych, ale w celu zapewnienia zgodności z regułami zmieniłem go.
Program w golfa
Korzystanie z GNU Prolog, ponieważ ma składnię solvera ograniczającego, która jest nieco krótsza niż składnia arytmetyczna przenośnego Prologa, co pozwala zaoszczędzić kilka bajtów.
Algorytm
Jest to jeden z tych problemów, w którym trudno jest wiedzieć, jak zacząć. Nie jest oczywiste, jak obliczyć kształt węzła na podstawie podanej notacji, ponieważ nie informuje on, czy masz zginać linię w lewo czy w prawo w danym miejscu (i jako taki notacja może być niejednoznaczna). Moje rozwiązanie polegało na wykorzystaniu starej gotowości do gry w golfa: napisz niesamowicie nieefektywny program, który generuje wszystkie możliwe dane wyjściowe, a następnie sprawdź, czy pasują do danych wejściowych. (Zastosowany tutaj algorytm jest nieco bardziej wydajny, ponieważ Prolog może od czasu do czasu wyrzucić ślepy zaułek, ale nie ma wystarczających informacji, aby poprawić złożoność obliczeniową).
Wyjście odbywa się za pomocą art. GNU Prolog wydaje się chcieć zestawu znaków jednobajtowych, który jest zgodny z ASCII, ale nie dba o to, który z nich zostanie użyty (ponieważ traktuje znaki z zestawem wysokich bitów jako nieprzezroczyste). W rezultacie użyłem IBM850, który jest szeroko obsługiwany i ma potrzebne nam znaki do rysowania linii.
Wydajność
Program przeszukuje wszystkie możliwe obrazy węzłów, w kolejności odpowiadającej wielkości ich obwiedni, a następnie kończy pracę, gdy znajdzie pierwszy. Oto, jak wygląda wynik
m([0]).
:Uruchomienie na moim komputerze zajęło 290,528 sekund; program nie jest zbyt wydajny. Zostawiłem go na dwie godziny
m([0,1])
i skończyłem z tym:Wersja bez golfa z komentarzami
Podświetlacz składni Stack Exchange wydaje się mieć niewłaściwy symbol komentarza dla Prologa, więc zamiast
%
komentarzy (których faktycznie używa Prolog), to wyjaśnienie używa% #
komentarzy (które są oczywiście równoważne ze względu na początek%
, ale poprawnie podświetlone).źródło