Bei einer positiven Ganzzahl N müssen Sie die Anzahl der Schritte zurückgeben, die der folgende Algorithmus benötigt, um N zu erreichen :
Finden Sie die kleinste Dreieckszahl T i, so dass T i ≥ N ist . Erstellen Sie die entsprechende Liste L = [1, 2, ..., i] .
Entfernen Sie den ersten Begriff aus der Liste, während die Summe der Begriffe von L größer als N ist.
Wenn die Summe der Terme von L jetzt kleiner als N ist , erhöhe i und hänge sie an die Liste an. Fahren Sie mit Schritt 2 fort.
Wir halten an, sobald N erreicht ist. Nur der erste Schritt wird systematisch ausgeführt. Die Schritte 2 und 3 werden möglicherweise überhaupt nicht verarbeitet.
Beispiele
Unten ist ein Beispiel für N = 11 :
Die erwartete Ausgabe für N = 11 ist also 4 .
Andere Beispiele:
- N = 5 - Wir beginnen mit T 3 = 1 + 2 + 3 = 6 , gefolgt von 2 + 3 = 5 . Erwartete Leistung: 2 .
- N = 10 - Nur der erste Schritt ist erforderlich, da 10 eine Dreieckszahl ist: T 4 = 1 + 2 + 3 + 4 = 10 . Erwartete Leistung: 1 .
Erste 100 Werte
Unten sind die Ergebnisse für 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
Regeln
- Sie können entweder ein vollständiges Programm oder eine Funktion schreiben, die das Ergebnis entweder ausgibt oder zurückgibt.
- Sie sind erforderlich , alle verarbeiten N ≤ 65536 auf Mid-Range - Hardware in weniger als eine Minute.
- Mit genügend Zeit, Ihr Programm / Funktion sollte theoretisch für jeden Wert von Arbeit N , die nativ von Ihrer Sprache unterstützt wird. Wenn nicht, erläutern Sie bitte in Ihrer Antwort, warum.
- Das ist Codegolf, also gewinnt die kürzeste Antwort in Bytes!
quelle
Antworten:
Jelly ,
2931 BytesEine monadische Verbindung, die das Ergebnis zurückgibt (N = 65536 dauert weniger als zwei Sekunden).
Probieren Sie es online!
Wie?
Für eine gründliche Erklärung des Algorithmus siehe den fantastischen Beitrag von Martin Ender .
Die 29-Byte-Vollprogrammimplementierung, die ich mit dem beschriebenen Algorithmus erstellt habe, dauert 4 Minuten 30 für N = 65536 auf meinem Laptop, daher denke ich, dass sie nicht zählt.
Die Verwendung einer while-Schleife für jeden Schritt 3 und deren Wiederverwendung als Schritt 1 entspricht der Länge, die ich mit der Initialisierung der Liste erreichen kann, da keine Überprüfung in Schritt 3 bedeutet, eine Liste zu erstellen, bis nichts mehr übrig ist, und dann den ersten Index des Werts zu finden:
quelle
Mathematica, 79 Bytes
Erläuterung
Ich konnte nicht die Mühe machen, den Algorithmus in der Herausforderung zu implementieren, deshalb wollte ich nach einer Verknüpfung zur Lösung suchen. Obwohl ich eine gefunden habe, übertrifft sie leider nicht die Mathematica-Antwort, die den Algorithmus implementiert. Trotzdem bin ich mir sicher, dass dies noch nicht optimal ist, und es könnte andere Sprachen geben, die von diesem Ansatz oder einigen der dabei gewonnenen Erkenntnisse profitieren können.
Also behaupte ich, dass die Sequenz, die wir berechnen sollen, ist:
f (n) = 2 * ( A212652 (n) - A002024 (n)) + 1 + A023532 (n-1)
Alternativ ist f (n) = 1, wenn n eine Dreieckszahl ist, und f (n) = 2 * ( A212652 (n) - A002024 (n) + 1), andernfalls.
Im ersten Ausdruck codiert A023532 einfach diese beiden unterschiedlichen Fälle. Die beiden anderen Folgen (plus 1) sind die Differenz zwischen der größten ganzen Zahl k bei der längsten Zerlegung von n in aufeinanderfolgende ganze Zahlen (k-i + 1) + (k-i + 2) + ... + k = n und die größte ganze Zahl j, so dass 1 + 2 + ... + j <n ist .
In etwas einfacheren Worten finden wir hier die Antwort für nicht dreieckige Zahlen: Erstens finden wir die größte dreieckige Zahl T j, die kleiner als n ist . Dann ist j die vorletzte Ganzzahl, die in Schritt 1 hinzugefügt wird (da wir nach der Addition von j + 1 n überschritten haben ). Zerlegen Sie dann n in so viele (oder so kleine) aufeinanderfolgende ganze Zahlen wie möglich und nennen Sie das Maximum unter diesen Zahlen k . Das Ergebnis ist einfach 2 * (kj) . Der intuitive Grund dafür ist, dass das Maximum in der Zerlegung mit jedem zweiten Schritt um 1 wächst und wir aufhören, wenn wir erreichenk .
Wir müssen vier Dinge zeigen, um zu beweisen, dass dies funktioniert:
Wir haben bereits gezeigt, warum (1) wahr ist. Als nächstes beweisen wir, dass wir nicht mit einem Einfügungsschritt enden können, außer dem ersten (was bei nicht dreieckigen Zahlen nicht der Fall ist).
Angenommen, wir haben mit einem Einfügungsschritt geendet und n erreicht, nachdem der Summe ein Wert p hinzugefügt wurde . Das bedeutet, dass der Wert vor diesem Einfügungsschritt np war ( oder weniger, wenn wir mehrere Werte gleichzeitig hinzugefügt haben). Diesem Einfügeschritt ging jedoch ein Löschschritt voraus (da wir in Schritt 1 n nicht gedrückt haben konnten ). Der letzte Wert q, den wir während dieses Löschschritts entfernt haben, war aufgrund der Funktionsweise des Algorithmus notwendigerweise kleiner als p . Aber das heißt, bevor wir q entfernt haben, hatten wir n-p + q ( oder weniger ), das ist weniger als n. Aber das ist ein Widerspruch, denn wir hätten aufhören müssen, Ganzzahlen zu entfernen, wenn wir n-p + q gedrückt hätten, anstatt ein anderes q zu entfernen . Dies beweist Punkt (2) oben. Jetzt wissen wir also, dass wir immer mit einem Löschschritt enden und dass daher alle nicht dreieckigen Zahlen gerade Ausgaben haben.
Als nächstes beweisen wir (3), dass jeder Einfügeschritt nur einen Wert einfügen kann. Dies ist im Wesentlichen eine Folgerung aus (2). Wir haben gezeigt, dass wir nach dem Addieren eines Wertes nicht genau n treffen können und da der Beweis eine Ungleichung verwendet, können wir auch nicht unter n enden (da dann n-p + q immer noch kleiner als n wäre und wir nicht hätten entfernen dürfen so viele Werte überhaupt). Wenn wir also einen einzelnen Wert hinzufügen, werden wir garantiert n überschreiten, da wir durch Entfernen eines kleineren Werts unter n gesunken sind. Daher wissen wir, dass das obere Ende der Summe mit jedem zweiten Schritt um 1 wächst . Wir kennen den Anfangswert dieses oberen Endes (es ist das kleinste m, so dassT m > n ). Jetzt müssen wir nur noch dieses obere Ende herausfinden, sobald wir die Endsumme erreicht haben. Dann ist die Anzahl der Schritte einfach doppelt so groß wie die Differenz (plus 1).
Dazu beweisen wir (4), dass die Endsumme immer die Zerlegung von n in so viele ganze Zahlen wie möglich ist, oder die Zerlegung, bei der das Maximum in dieser Zerlegung minimal ist (dh es ist die frühestmögliche Zerlegung). Wir werden das im Widerspruch wieder tun (die Formulierung in diesem Teil könnte etwas strenger sein, aber ich habe bereits viel zu viel Zeit damit verbracht ...).
Angenommen, die frühestmögliche / längste mögliche Zerlegung von n ist etwas a + (a + 1) + ... (b-1) + b , a ≤ b , und der Algorithmus überspringt sie. Das bedeutet , dass zu dem Zeitpunkt, b hinzugefügt wird, ein längeres muss keinen Teil der Summe sein. Wenn a Teil der Summe s wäre , hätten wir in diesem Moment n ≤ s . Also entweder die Summe enthält nur die Werte von a bis b , die gleich n und wir stoppen (daher haben wir diese Zersetzung nicht überspringen), oder es ist zumindest ein Wert kleiner als ein in der Summe, gewinnen den Fall n <sund dieser Wert würde entfernt werden, bis wir die genaue Summe erreicht haben (die Zerlegung wurde wiederum nicht übersprungen). So würden wir davon haben , um loszuwerden , ein vor der Zugabe von b . Das heißt aber, wir müssten eine Situation erreichen, in der a die kleinste Komponente der Summe ist und die größte noch nicht b ist. Doch an diesem Punkt können wir nicht entfernen ein , weil die Summe weniger eindeutig als n (da b fehlt), so dass wir die erforderlichen Werte hinzufügen zuerst , bis wir hinzufügen b und drücken Sie n genau. Dies beweist (4).
Nehmen wir also diese Dinge zusammen: Wir wissen, dass das erste Paar von Schritten uns einen Maximalwert von A002024 (n) gibt . Wir wissen, dass der Maximalwert der endgültigen Zersetzung ist A212652 (n) ist . Und wir wissen, dass dieses Maximum in jedem Schrittpaar einmal erhöht wird. Daher ist der endgültige Ausdruck 2 * ( A212652 (n) - A002024 (n) + 1) . Diese Formel funktioniert fast für Dreieckszahlen, außer dass wir für diese nur 1 Schritt anstelle von 2 benötigen, weshalb wir das Ergebnis mit der Indikatorfunktion der Dreieckszahlen korrigieren (oder umgekehrt, je nachdem, was praktischer ist).
Zum Schluss noch was die Umsetzung betrifft. Für die erste Sequenz verwende ich die Formel MIN (ungerade d | n; n / d + (d-1) / 2) von OEIS. Es stellt sich heraus, dass ein paar Bytes gespart werden, wenn wir den Faktor 2 in diesen Ausdruck aufnehmen, um zu erhalten MIN (ungerades d | n; 2n / d + d-1) , weil dieses -1 dann mit dem +1 in meiner ersten Version aufhört von f (n), die die beiden Fälle für dreieckige und nicht dreieckige Zahlen direkt codiert. Im Code ist dies:
Für die letztere Sequenz (
1, 2, 2, 3, 3, 3, ...
) können wir eine einfache geschlossene Form verwenden:Und schließlich ist die inverse Indikatorfunktion der Dreieckszahlen immer dann 0, wenn 8n + 1 ein perfektes Quadrat ist. Dies kann in Mathematica ausgedrückt werden als
Es gibt viele Möglichkeiten, diese beiden letzten Sequenzen auszudrücken und einen konstanten Versatz zwischen ihnen zu verschieben. Daher bin ich mir sicher, dass dies noch keine optimale Implementierung ist, aber ich hoffe, dass dies anderen einen Ausgangspunkt bietet, um neue Ansätze in zu untersuchen ihre eigenen Sprachen.
Da ich mich um all diese Mühe gekümmert habe, ist hier eine Handlung der Sequenz bis zu n = 1000 (ich könnte auch 100 k in ein paar Sekunden berechnen, aber es zeigt keine zusätzlichen Erkenntnisse):
Es mag interessant sein, die Variationen über diese sehr geraden Linien zu untersuchen, aber ich überlasse das jemand anderem ...
quelle
Mathematica, 72 Bytes
Reine Funktion, die ein ganzzahliges Argument verwendet.
Wie es funktioniert
Eine
For
Schleife.Initialisierung; setze
l
(untere),u
(obere),c
(Zähler) undk
(Summe) auf 0.Bedingung; wiederholen, solange
k
nicht gleich der Eingabe ist.Zuwachs; Inkrementiere den Zähler
c
.Karosserie
Wenn die Eingabe größer ist als
k
:Während die Eingabe größer als ist
k
, erhöhenu
und erhöhen Siek
umu
.Wenn die Eingabe nicht größer ist als
k
:Während die Eingabe kleiner als ist
k
, wirdk
uml
und inkrementiertl
.Rückkehr
c
nach der Schleife.quelle
For[,...]
schlägtWhile[...]
.Python 2 , 104 Bytes
Probieren Sie es online!
Wechselt zwischen dem Hinzufügen von Begriffen am Ende der Liste und dem Entfernen von Begriffen am Anfang.
quelle
Haskell ,
70 63 6864 BytesBEARBEITEN:
a
. Off-by-One-Fehler in der Erklärung behoben.a
undb
linear, um Terme in der Summenformel zu stornieren.1#1
ist eine anonyme Funktion, die eine Ganzzahl annimmt und zurückgibt.Verwenden Sie als
(1#1) 100
.Probieren Sie es online!
Wie es funktioniert
(a#b)n
repräsentiert den aktuellen Berechnungsschritt.a, b
sind Zahlen in1, 3, 5, ..
, währendn
sie je nach Schritt entweder positiv oder negativ sein können.[(a+1)/2,(a+3)/2..(b-1)/2]
und die Zielnummer angezeigt-n
.[(b+1)/2,(b+3)/2..(a-1)/2]
und die Zielnummer angezeigtn
.a, b
und den Listen besteht darin, mit dem kurzen Ausdruck summieren zu könnens=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
, dannb
wird vor der Rekursion um 2 erhöht.s<8*n
ja, ändert die Rekursion den Schritt durch Vertauschen vona
undb
und Negierenn
Hinzufügen von 1 zum Ergebnis.s==8*n
dann keines der beiden Listenverständnisse ein Element ergibt, so ist die Summe0
.(1#1) n
Stellt einen Dummy "Stufe 2" vor dem Start dar, der sofort zu Schritt 1 geändert wird und aus dem die Liste erstellt wird[1..0]=[]
.quelle
PHP> = 7.0, 74 Bytes
Verwenden Sie den Raumschiff-Operator
Probieren Sie es online!
Erweitert
quelle
$argn
?-R
viel weniger gehörtargv
oderargi
. Ich wusste natürlich von argc und argv. Sehr interessant, danke.C,
9491 BytesVersuchen Sie es online
quelle
return
), aber für diejenigen, die dies tun, können Sie sie gerne in Ihre Antwort aufnehmen.JavaScript (ES6), 82 Byte
Testschnipsel
Code-Snippet anzeigen
quelle
Gleichstrom , 61 Bytes
Probieren Sie es online!
Erläuterung
Hauptrekursives Makro:
Dieses Makro:
S
genau die aktuelle Zahl darstellt. Wird beendet, wenn dies der Fall ist.S+N
(Über-) oderS-N
(Unter-Näherung). Die Auswahl wechselt zwischen den Iterationen.Wenn es beendet wird, teilt der Pfad, der auf dem Stapel verbleibt, dem Hauptprogramm mit, wie viele Iterationen es benötigte.
quelle
Python 3
150138 BytesÄnderungsprotokoll:
quelle
else
notwendig ist? Ich glaube, daselse
läuft jedes Mal, weil die Schleife immer normal (ohnebreak
) endet , und es scheint gut zu funktionieren, ohne es .A=l.append
Teil überspringen undl+=[x]
stattdessen verwenden.Batch, 126 Bytes
Erläuterung:
l
Ist Null, wenn Schritt 2 noch nie ausgeführt wurde. Dies ermöglicht esn
, die Anzahl der Iterationen von Schritt 3 zu verfolgen. Da der Algorithmus niemals bei Schritt 3 anhält, muss er daher Schritt 1 einmal und Schritt 2n+1
mal für insgesamtn+n+2
Schritte ausgeführt haben. Wenn der Parameter jedoch eine Dreieckszahl ist, wird Schritt 2 niemals ausgeführt, sodass ein Schritt subtrahiert werden muss.quelle
Python 2,
8681 BytesProbieren Sie es online!
Berechnet den 65536- Testfall
0.183s
für TIO.Diese rekursive Version mit 84 Bytes kann nicht alle Werte bis 65536 berechnen:
Probieren Sie es online!
quelle
Mathematica, 92 Bytes
Reine Funktion, die ein ganzzahliges Argument verwendet und eine ganze Zahl zurückgibt.
Die Variablen
a
undb
stehen für die (sich ständig ändernden) Anfangs- und Endnummern in der betrachteten Summe, während sieq
für die laufende Summe (der Nummern vona+1
bisb
) stehen;t
Verfolgt alle Werte von bisherq
angetroffen. Nach der Initialisierung dieser Variablen wird dieFor
Schleife weiter ausgeführtIf[q<#,q+=++b,q-=++a]
, wobei entweder eine neue Nummer am Ende hinzugefügt oder die durch die Spezifikation vorgegebene Nummer vorne abgezogen wird, bisq
der Eingabe entspricht.Jetzt müssen wir nur noch die Anzahl der Schritte aus
t
der Liste derq
Werte extrahieren , die auf dem Weg gefunden wurden. Wenn der Eingang beispielsweise ist11
, wird dieFor
Schleife mitt
Gleichheit beendet{0,1,3,6,10,15,14,12,9,15,11}
. Der beste Weg, die Anzahl der Schritte zu berechnen, ist zu zählen, wie oft die Unterschiede von hoch nach unten wechseln. Das ist, was der ausführliche BefehlLength@Split@Sign@Differences@t
tut, aber ich vermute, dass das verbessert werden kann.quelle
C (tcc), 71 Bytes (61 + 10)
Befehlszeilenargumente (einschließlich Leerzeichen):
Quelle:
Wie es funktioniert:
c
zählt die Anzahl der Schritte.m
undM
speichern Sie das Minimum und Maximum des Bereichs,s
die Summe. Anfangs sind sie alle Null.Kontinuierlich
c
wird erhöht unds
mit verglichenn
. Solange sie ungleich sind:Wenn
c
ungerade ists<n
, fügen Sie so lange eine ganze Zahl an das Ende des Bereichs an: Erhöhen SieM
um eins unds
umM
.Wenn gerade
c
ists>n
, entfernen Sie eine ganze Zahl vom Anfang des Bereichs: Verringerns
umm
und Erhöhenm
um eins.Wenn die Schleife beendet wird,
c
wurde sie zu oft erhöht. Wenn Sie es dekrementieren, erhalten Sie das richtige Ergebnis, und es wird zufällig im richtigen Register berechnet, um als Rückgabewert zu fungieren.Amüsanterweise werden genau die gleichen Variablennamen wie in Khaled.Ks C-Antwort verwendet . Sie werden nicht kopiert.
quelle
Perl 6 , 114 Bytes
(inspiriert von einer früheren Haskell- Implementierung)
Probieren
Sie es aus Es läuft mit einer Eingabe von 65536 in weniger als 45 Sekunden auf meinem Computer, aber ich konnte es mit TIO.run nicht in weniger als 60 Sekunden zum Laufen bringen.
Ich habe Rakudo v2017.04 +, wo es v2017.01 hat .
Rakudo / NQP / MoarVM werden fast täglich optimiert, sodass es in der Zwischenzeit eine beliebige Anzahl von Optimierungen geben kann, die erforderlich sind, um das Problem rechtzeitig zu beheben.
Erweitert
Beachten Sie, dass Rakudo über eine Optimierung verfügt,
Range.sum
sodass nicht alle Werte durchlaufen werden müssen.quelle