Biorąc pod uwagę dodatnią liczbę całkowitą N , Twoim zadaniem jest zwrócenie liczby kroków wymaganych przez następujący algorytm do osiągnięcia N :
Znajdź najmniejszą trójkątny numer T i taka, że T i ≥ N . Zbuduj odpowiednią listę L = [1, 2, ..., i] .
Chociaż suma wyrażeń L jest większa niż N , usuń pierwszy wyraz z listy.
Jeśli suma warunków L jest teraz mniejsza niż N , zwiększ i i dodaj ją do listy. Przejdź do kroku 2.
Zatrzymujemy się, gdy tylko N zostanie osiągnięte. Tylko pierwszy krok jest wykonywany systematycznie. Kroki 2 i 3 mogą w ogóle nie zostać przetworzone.
Przykłady
Poniżej znajduje się przykład dla N = 11 :
Oczekiwany wynik dla N = 11 wynosi 4 .
Inne przykłady:
- N = 5 - Zaczynamy od T 3 = 1 + 2 + 3 = 6 , a następnie 2 + 3 = 5 . Oczekiwany wynik: 2 .
- N = 10 - wymagany jest tylko pierwszy krok, ponieważ 10 jest liczbą trójkątną: T 4 = 1 + 2 + 3 + 4 = 10 . Oczekiwany wynik: 1 .
Pierwsze 100 wartości
Poniżej znajdują się wyniki dla 1 ≤ N ≤ 100 :
1, 2, 1, 4, 2, 1, 2, 10, 2, 1, 4, 2, 6, 2, 1, 22, 8, 2, 10, 2,
1, 2, 12, 6, 2, 4, 2, 1, 16, 2, 18, 50, 2, 6, 2, 1, 22, 6, 2, 4,
26, 2, 28, 2, 1, 8, 30, 16, 2, 6, 4, 2, 36, 2, 1, 2, 4, 12, 40, 2,
42, 14, 2,108, 2, 1, 46, 2, 6, 4, 50, 2, 52, 18, 2, 4, 2, 1, 56, 12,
2, 20, 60, 4, 2, 22, 10, 2, 66, 2, 1, 4, 10, 24, 2, 40, 72, 8, 2, 6
Zasady
- Możesz napisać pełny program lub funkcję, która albo wydrukuje, albo zwróci wynik.
- Jesteś zobowiązany do przetwarzania dowolnego N ≤ 65536 w mniej niż jedną minutę na sprzęcie średniej klasy.
- Biorąc pod uwagę wystarczającą ilość czasu, twój program / funkcja powinna teoretycznie działać na każdą wartość N, która jest natywnie obsługiwana przez twój język. Jeśli nie, proszę wyjaśnić dlaczego w swojej odpowiedzi.
- To jest golf golfowy, więc wygrywa najkrótsza odpowiedź w bajtach!
code-golf
math
integer-partitions
Arnauld
źródło
źródło
Odpowiedzi:
Galaretka ,
2931 bajtówŁącze monadyczne, które zwraca wynik (N = 65536 zajmuje mniej niż dwie sekundy).
Wypróbuj online!
W jaki sposób?
Dokładne wyjaśnienie algorytmu znajduje się w fantastycznym poście Martina Endera .
29-bajtowa implementacja pełnego programu, którą stworzyłem opisanego algorytmu, zajmuje 4 min 30 dla N = 65536 na moim laptopie, więc przypuszczam, że się nie liczy.
Użycie pętli while dla każdego kroku 3 i ponowne użycie go jako kroku 1 jest równe długości tego, co mogę zrobić przy inicjalizacji listy, ponieważ brak kontroli w kroku 3 oznacza zbudowanie listy, dopóki nic nie pozostanie, a następnie znalezienie pierwszego indeksu wartości:
źródło
Mathematica, 79 bajtów
Wyjaśnienie
Nie mogłem się martwić implementacją algorytmu w wyzwaniu, więc chciałem poszukać skrótu do rozwiązania. Chociaż znalazłem jedną, niestety nie bije ona odpowiedzi Mathematica, która implementuje algorytm. To powiedziawszy, jestem pewien, że to nie jest jeszcze optymalna gra w golfa, i mogą istnieć inne języki, które mogą skorzystać z tego podejścia lub niektórych spostrzeżeń uzyskanych w tym procesie.
Twierdzę więc, że sekwencja, którą powinniśmy obliczyć to:
f (n) = 2 * ( A212652 (n) - A002024 (n)) + 1 + A023532 (n-1)
Alternatywnie jest to f (n) = 1, jeśli n jest liczbą trójkątną, a f (n) = 2 * ( A212652 (n) - A002024 (n) + 1) w przeciwnym razie.
W pierwszym wyrażeniu A023532 po prostu koduje te dwa różne przypadki. Pozostałe dwie sekwencje (plus 1) to różnica między największą liczbą całkowitą k w najdłuższym rozkładzie n na kolejne liczby całkowite (k-i + 1) + (k-i + 2) + ... + k = n i największa liczba całkowita j, tak że 1 + 2 + ... + j <n .
W nieco prostszych słów, oto jak znaleźć odpowiedź na liczbach niż trójkątne: po pierwsze, znaleźć największą liczbę trójkątną T j , która jest mniejsza niż n . Zatem j jest przedostatnią liczbą całkowitą, która jest dodawana podczas kroku 1 (ponieważ po dodaniu j + 1 przekroczymy n ). Następnie rozłóż n na tak wiele (lub tak małe) kolejnych liczb całkowitych, jak to możliwe i wywołaj maksimum spośród tych liczb k . Wynik to po prostu 2 * (kj) . Intuicyjnie wynika to z tego, że maksimum w rozkładzie rośnie o 1 co drugi krok i zatrzymujemy się, gdy osiągamyk .
Musimy pokazać cztery rzeczy, aby udowodnić, że to działa:
Pokazaliśmy już, dlaczego (1) jest prawdą. Następnie dowodzimy, że nie możemy zakończyć kroku wstawiania oprócz początkowego (co nie dzieje się w przypadku liczb innych niż trójkątne).
Załóżmy, że zakończyliśmy krok wstawiania, osiągając n po dodaniu wartości p do sumy. Oznacza to, że przed tym krokiem wstawiania wartość wynosiła np ( lub mniej, jeśli dodaliśmy wiele wartości na raz). Ale ten krok wstawiania był poprzedzony krokiem usuwania (ponieważ nie mogliśmy trafić n podczas kroku 1). Ostatnia wartość q, którą usunęliśmy podczas tego etapu usuwania, była z konieczności mniejsza niż p ze względu na sposób działania algorytmu. Ale to oznacza, że zanim usunęliśmy q , mieliśmy n-p + q ( lub mniej ), co jest mniejsze niż n. Ale to sprzeczność, ponieważ musielibyśmy przestać usuwać liczby całkowite, gdy naciśniemy n-p + q zamiast usuwać kolejne q . To potwierdza punkt (2) powyżej. Teraz wiemy, że zawsze kończymy krok usuwania i dlatego wszystkie liczby inne niż trójkątne mają nawet dane wyjściowe.
Następnie udowodnimy (3), że każdy krok wstawiania może wstawić tylko jedną wartość. Jest to zasadniczo następstwo (2). Wykazaliśmy, że po dodaniu jednej wartości nie możemy trafić n dokładnie, a ponieważ w dowodzie zastosowano nierówność, nie możemy również skończyć poniżej n (ponieważ od tego czasu n-p + q nadal będzie mniejsze niż n i nie powinniśmy byli usuwać tak wiele wartości w pierwszej kolejności). Tak więc za każdym razem, gdy dodamy jedną wartość, gwarantujemy, że przekroczysz n, ponieważ spadliśmy poniżej n , usuwając mniejszą wartość. Wiemy zatem, że górna granica sumy rośnie o 1 co drugi krok. Znamy wartość początkową tego górnego końca (jest to najmniejszy m taki, żeT m > n ). Teraz musimy tylko ustalić ten górny koniec, gdy osiągniemy ostateczną sumę. Wtedy liczba kroków jest po prostu dwukrotnością różnicy (plus 1).
Aby to zrobić, dowodzimy (4), że ostateczna suma jest zawsze rozkładem n na jak najwięcej liczb całkowitych, jak to możliwe, lub rozkładem, w którym maksimum w tym rozkładzie jest minimalne (tj. Jest to najwcześniejszy możliwy rozkład). Znowu zrobimy to przez sprzeczność (sformułowania w tej części mogą być nieco bardziej rygorystyczne, ale spędziłem już na tym zbyt dużo czasu ...).
Powiedzmy, że najwcześniejszy / najdłuższy możliwy rozkład n to jakieś a + (a + 1) + ... (b-1) + b , a ≤ b , i powiedzmy, że algorytm go pomija. Oznacza to, że w momencie dodania b , a nie może już być częścią sumy. Gdyby a było częścią sumy s , wtedy mielibyśmy n ≤ s w tym momencie. Tak więc albo suma zawiera tylko wartości od a do b , która jest równa n, i zatrzymujemy się (stąd nie pominęliśmy tego rozkładu), lub jest co najmniej jedna wartość mniejsza niż a w sumie, wygraj który przypadek n <si ta wartość będzie usuwana, dopóki nie osiągniemy dokładnej sumy (ponownie, rozkład nie został pominięty). Tak musielibyśmy pozbyć przed dodaniem b . Ale to oznacza, że musielibyśmy dojść do sytuacji, w której a jest najmniejszym składnikiem sumy, a największy nie jest jeszcze b . Jednak w tym momencie nie możemy usunąć a , ponieważ suma jest wyraźnie mniejsza niż n (ponieważ brakuje b ), więc musimy najpierw dodać wartości, aż dodamy b i naciśniemy dokładnie n . Dowodzi to (4).
Łącząc te rzeczy razem: wiemy, że pierwsza para kroków daje nam maksymalną wartość A002024 (n) . Wiemy, że maksymalna wartość końcowego rozkładu wynosi A212652 (n) . I wiemy, że to maksimum zwiększa się raz na każdą parę kroków. Zatem końcowe wyrażenie to 2 * ( A212652 (n) - A002024 (n) + 1) . Ta formuła prawie działa na liczbach trójkątnych, z tym wyjątkiem, że dla tych potrzebujemy tylko 1 krok zamiast 2, dlatego korygujemy wynik za pomocą funkcji wskaźnika liczb trójkątnych (lub odwrotnie, w zależności od tego, co jest wygodniejsze).
Wreszcie, jeśli chodzi o wdrożenie. W poprzedniej sekwencji używam wzoru MIN (nieparzysty d | n; n / d + (d-1) / 2) z OEIS. Okazuje się, że zapisujemy kilka bajtów, jeśli weźmiemy współczynnik 2 do tego wyrażenia, aby uzyskać MIN (nieparzyste d | n; 2n / d + d-1) , ponieważ to -1 następnie anuluje z +1 w mojej pierwszej wersji z f (n), który bezpośrednio koduje dwa przypadki dla liczb trójkątnych i nie trójkątnych. W kodzie jest to:
W przypadku drugiej sekwencji (
1, 2, 2, 3, 3, 3, ...
) możemy użyć prostej formy zamkniętej:I na koniec funkcja odwrotnego wskaźnika liczb trójkątnych wynosi 0, gdy 8n + 1 jest idealnym kwadratem. Można to wyrazić w Mathematica jako
Istnieje wiele sposobów wyrażenia tych dwóch ostatnich sekwencji i przesunięcia między nimi stałego przesunięcia, więc jestem pewien, że nie jest to jeszcze optymalna implementacja, ale mam nadzieję, że może to dać innym punkt wyjścia do spojrzenia na nowe podejścia w ich własne języki.
Ponieważ zadałem sobie tyle trudu, oto wykres sekwencji do n = 1000 (mogłem również obliczyć 100k w kilka sekund, ale tak naprawdę nie pokazuje żadnych dodatkowych spostrzeżeń):
Interesujące może być przeanalizowanie wariantów tych bardzo prostych linii, ale zostawię to komuś innemu ...
źródło
Mathematica, 72 bajty
Czysta funkcja przyjmująca argument liczby całkowitej.
Jak to działa
For
Pętli.Inicjalizacja; ustaw
l
(dolny),u
(górny),c
(licznik) ik
(suma) na 0.Stan; powtórzenie podczas gdy
k
nie jest równe wejściowi.Przyrost; zwiększyć licznik
c
.Ciało
Jeśli dane wejściowe są większe niż
k
:Podczas gdy wartość wejściowa jest większa niż
k
, przyrostu
i przyrostk
ou
.Jeśli dane wejściowe nie są większe niż
k
:Choć wejście jest mniejsza niż
k
, ubytekk
przezl
i przyrostul
.Wróć
c
po pętli.źródło
For[,...]
uderzeniaWhile[...]
.Python 2 , 104 bajty
Wypróbuj online!
Na przemian między dodawaniem terminów na końcu listy a usuwaniem terminów od początku.
źródło
Haskell ,
70 63 6864 bajtówEDYTOWAĆ:
a
. Naprawiono błędy w wyjaśnieniach jeden po drugim.a
ib
liniowo, aby uzyskać warunki w formule sumowania w celu anulowania.1#1
to anonimowa funkcja przyjmująca i zwracająca liczbę całkowitą.Użyj jako
(1#1) 100
.Wypróbuj online!
Jak to działa
(a#b)n
reprezentuje bieżący krok obliczeń.a, b
są liczbami w1, 3, 5, ..
, an
mogą być dodatnie lub ujemne w zależności od kroku.[(a+1)/2,(a+3)/2..(b-1)/2]
i numer celu-n
.[(b+1)/2,(b+3)/2..(a-1)/2]
i numer celun
.a, b
listami i między nimi ma na celu dokonanie podsumowania za pomocą krótkiego wyrażenias=a*a-b*b
.s= -8*sum[(a+1)/2..(b-1)/2]
.s=8*sum[(b+1)/2..(a-1)/2]
.s>8*n
, tob
jest zwiększane o 2 przed powtórzeniem.s<8*n
, to rekurencja zmienia krok, zamieniająca
ib
, i negującn
, a do wyniku dodaje się 1.s==8*n
, to żadne z dwóch zestawień listy nie zawiera żadnych elementów, więc suma jest0
.(1#1) n
reprezentuje obojętny „etap 2” przed rozpoczęciem, który natychmiast zmienia się na krok 1, od którego budowana jest lista[1..0]=[]
.źródło
PHP> = 7.0, 74 bajtów
użyj operatora statku kosmicznego
Wypróbuj online!
Rozszerzony
źródło
$argn
?-R
wiele mniejargv
lubargi
. Oczywiście wiedziałem o argc i argv. Bardzo interesujące, dzięki.C,
9491 bajtówWypróbuj online
źródło
return
), ale dla tych, którzy to robią, włącz ich do swojej odpowiedzi.JavaScript (ES6), 82 bajty
Test Snippet
Pokaż fragment kodu
źródło
dc , 61 bajtów
Wypróbuj online!
Wyjaśnienie
Główne makro rekurencyjne:
To makro:
S
dokładnie reprezentuje bieżącą liczbę. Wychodzi, jeśli tak jest.S+N
(przybliżeniem) lubS-N
(przybliżeniem), wybór zmienia się między iteracjami.Po wyjściu ślad pozostawiony na stosie informuje program główny, ile iteracji potrzebował.
źródło
Python 3,
150138 bajtówDziennik zmian:
źródło
else
jest to konieczne? Wierzę, żeelse
przebiega za każdym razem, ponieważ pętla zawsze kończy się normalnie (bezbreak
) i wydaje się, że bez niej działa dobrze .A=l.append
część i użyćl+=[x]
zamiast tego.Partia, 126 bajtów
Objaśnienie:
l
wynosi zero, jeśli krok 2 nigdy nie został wykonany. Pozwala ton
na śledzenie liczby iteracji kroku 3. Ponieważ algorytm nigdy nie zatrzymuje się na kroku 3, musi zatem wykonać krok 1 raz, a krok 2n+1
razy, łącznie dla wszystkichn+n+2
kroków. Jeśli jednak parametr jest liczbą trójkątną, krok 2 nigdy się nie wykonuje, więc musimy odjąć jeden krok.źródło
Python 2,
8681 bajtówWypróbuj online!
Oblicza przypadek testowy 65536 w
0.183s
TIO.Ta rekurencyjna wersja o wielkości 84 bajtów nie jest w stanie obliczyć wszystkich wartości do 65536:
Wypróbuj online!
źródło
Mathematica, 92 bajty
Czysta funkcja przyjmująca argument liczby całkowitej i zwracająca liczbę całkowitą.
Zmienne
a
ib
oznaczają (stale zmieniające się) liczby początkowe i końcowe w rozpatrywanej sumie, natomiastq
oznaczają bieżącą sumę (liczb oda+1
dob
);t
śledzi wszystkieq
napotkane dotąd wartości. Po zainicjowaniu tych zmiennychFor
pętla wykonuje wykonywanieIf[q<#,q+=++b,q-=++a]
, które albo dodaje nową liczbę na końcu, albo odejmuje liczbę z przodu, jak dyktuje specyfikacja, ażq
zrówna się z wejściem.Teraz musimy tylko wyodrębnić liczbę kroków
t
, listęq
wartości napotkanych po drodze. Na przykład, gdy wejście jest11
,For
pętla kończy się zt
wyrównaniem{0,1,3,6,10,15,14,12,9,15,11}
. Najlepszym sposobem, jaki obliczyłem na podstawie liczby kroków, jest policzenie, ile razy różnice przechodzą od przejścia w górę w dół; tak robi pełne polecenieLength@Split@Sign@Differences@t
, ale podejrzewam, że można to poprawić.źródło
C (tcc), 71 bajtów (61 + 10)
Argumenty wiersza polecenia (w tym spacja):
Źródło:
Jak to działa:
c
liczy liczbę kroków.m
iM
przechowuj minimum i maksimum zakresu,s
sumę. Początkowo wszystkie mają zero.Ciągle
c
jest zwiększany is
porównywanyn
. O ile są one nierówne:Jeśli
c
jest nieparzyste, to tak długo, jaks<n
dodać liczbę całkowitą na końcu zakresu: zwiększyćM
o jeden is
przezM
.Jeśli
c
jest parzyste, tos>n
usuń liczbę całkowitą z początku zakresu: pomniejszs
om
i zwiększm
o jeden.Kiedy pętla wychodzi,
c
została zwiększona zbyt wiele razy. Zmniejszenie powoduje uzyskanie poprawnego wyniku i zdarza się, że jest obliczany w odpowiednim rejestrze, aby działał jako wartość zwracana.Zabawnie się zdarza, że używa dokładnie tych samych nazw zmiennych, co odpowiedź C. w języku Khaled.K . Nie są kopiowane.
źródło
Perl 6 , 114 bajtów
(zainspirowany wcześniejszą implementacją Haskell )
Wypróbuj
Działa z wejściem 65536 w mniej niż 45 sekund na moim komputerze, ale nie udało mi się uruchomić go w czasie krótszym niż 60 sekund z TIO.run.
Mam Rakudo v2017.04 +, gdzie ma v2017.01 .
Rakudo / NQP / MoarVM dostaje optymalizacje prawie codziennie, więc od czasu do czasu może być ich dowolna liczba, które są potrzebne, aby uzyskać to na czas.
Rozszerzony
Zauważ, że Rakudo ma optymalizację,
Range.sum
aby nie musiała iterować wszystkich wartości.źródło