Zeitreisender Aktienhändler

21

Geschichte
Vor langer Zeit hat Bobby eine Bitcoin-Brieftasche mit 1 Satoshi (1e-8 BTC, kleinste Währungseinheit) erstellt und vergessen. Wie viele andere später auch "Verdammt, wenn ich damals nur mehr investiert hätte ...".
Er hört nicht beim Tagträumen auf und widmet all seine Zeit und sein Geld dem Bau einer Zeitmaschine. Er verbringt die meiste Zeit in seiner Garage, ohne sich der weltlichen Angelegenheiten und Gerüchte bewusst zu sein, die über ihn kursieren. Er stellt den Prototypen einen Tag vor dem Stromausfall fertig. Als er von seiner Werkbank aufblickt, sieht er einen Polizeiwagen, der zu seinem Haus fährt. Er sieht aus, als hätten die neugierigen Nachbarn gedacht, er betreibe ein Meth-Labor in seiner Garage und rief die Polizei an.
Da er keine Zeit hat, Tests durchzuführen, greift er zu einem USB-Stick mit den Wechselkursdaten der letzten Jahre, verbindet den Flusskondensator mit dem Quantum Discombobulator und findet sich zurückversetzt zu dem Tag, an dem er sein Portemonnaie erstellt hat

Aufgabe
Finden Sie anhand der Wechselkursdaten heraus, wie viel Geld Bobby verdienen kann. Er folgt einer sehr einfachen Regel: "Kauftief - Verkauftief" und da er mit einem unendlich kleinen Kapital beginnt, gehen wir davon aus, dass sein Handeln keinen Einfluss auf die Wechselkurse der Zukunft haben wird.

Eingabe
Eine Liste von Gleitkommazahlen> 0, entweder als durch ein einzelnes Zeichen (Zeilenvorschub, Tabulator, Leerzeichen, Semikolon, was auch immer Sie bevorzugen) getrennte Zeichenfolge, die als Befehlszeilenargument an das Programm übergeben, aus einer Textdatei oder STDIN gelesen oder als Parameter übergeben wird zu einer Funktion. Sie können numerische Datentypen oder Arrays anstelle einer Zeichenfolge verwenden, da es sich im Grunde nur um eine Zeichenfolge mit eckigen Klammern handelt.

Output
Der Faktor, mit dem das Kapital von Bobbys bis zum Handelsende multipliziert wurde.

Beispiel

Input:  0.48 0.4 0.24 0.39 0.74 1.31 1.71 2.1 2.24 2.07 2.41

Wechselkurs: 0,48 $ / BTC, da es kurz vor dem Einbruch steht, verkaufen wir alle Bitcoins für 4,8 Nanodollar. Faktor = 1 Wechselkurs: 0,4, nichts tun
Wechselkurs: 0,24 $ / BTC und steigend: alle $ in 2 Satoshis umrechnen. Faktor = 1 (der Dollarwert bleibt unverändert)
Wechselkurs: 0,39 - 2,1 $ / BTC: Nichts tun
Wechselkurs: 2,24 $ / BTC: Alles verkaufen, bevor es fällt. 44,8 Nanodollar, Faktor = 9,33
Wechselkurs: 2,07 $ / BTC: kaufen 2,164 Satoshis, Faktor = 9,33
Wechselkurs: 2,41 $ / BTC: kaufen 52,15 Nanodollar, Faktor = 10,86

Output: 10.86

Zusätzliche Details
Sie können seltsame Randfälle wie konstante Eingabe, Null- oder negative Werte, nur eine Eingabezahl usw. ignorieren. Sie können auch
eigene Zufallszahlen zum Testen oder Verwenden von tatsächlichen Aktiencharts generieren. Hier ist eine längere Eingabe zum Testen (erwartete Ausgabe ca. 321903884.638). Erklären Sie
kurz, was Ihr Code tut . Grafiken
werden geschätzt, sind aber nicht erforderlich

DenDenDo
quelle
Wenn wir die Zahlen über ein Funktionsargument annehmen, muss es dann immer noch ein String sein, oder können wir direkt ein Array aufnehmen?
Martin Ender
@ MartinBüttner Ich habe eine Weile darüber nachgedacht, ob die Eingabe eine Zeichenfolge, ein numerisches Array oder eine freie Wahl ist, es gibt immer einige Sprachen, die einen Vorteil haben. Es scheint keinen allgemeinen Konsens darüber zu geben, und das Schreiben von zwei Programmen, eines für eine numerische und eines für eine Zeichenketteneingabe und das Mitteln beider Noten, scheint übertrieben.
DenDenDo
Was ist mit dem Infinite Improbability Drive? :)
Türklinke
2
Um auf das Problem zurückzukommen: Müssen wir die BTC- und / oder $ -Werte bei jeder Iteration mit einer bestimmten Genauigkeit runden? In der realen Welt muss beispielsweise die BTC-Geldbörse auf den Satoshi gerundet werden. Dies macht einen Unterschied, denn in Ihrem Beispiel können Sie bei 2.07 nur 2s kaufen (nicht 2.164). dann kaufen Sie bei 2,41 Ihre 2s 48,2 n $ (nicht 52,15), so dass der Faktor 10,04 (nicht 10,86) ist. Es sei denn, Sie haben eine separate Brieftasche mit der Änderung und müssen diese jedes Mal wieder hinzufügen. Was ist mit Dollar? Kann heute jemand behaupten, einen Nanodollar zu haben? Ich glaube, die kleinste Menge, die man aufnehmen kann, ist 1 ¢.
Tobia
1
@CortAmmon: Sie sagen, dass BTC-Handel nicht chaotisch ist? ;-)
Steve Jessop

Antworten:

10

APL, 16 Zeichen

{×/1⌈÷/⊃⍵,¨¯1⌽⍵}

Diese Version verwendet @Frxstrem ‚s einfacher Algorithmus und @xnor ‘ s max(r,1)Idee.

Es wird auch davon ausgegangen, dass die Serie insgesamt ansteigt, dh, der erste Bitcoin-Wert ist kleiner als der letzte. Dies stimmt mit der Problembeschreibung überein. Um eine allgemeinere Formel zu erhalten, müssen die ersten beiden Sätze gestrichen werden, wobei 2 Zeichen hinzugefügt werden:{×/1⌈÷/⊃1↓⍵,¨¯1⌽⍵}

Beispiel:

    {×/1⌈÷/⊃⍵,¨¯1⌽⍵}  0.48 0.4 0.24 0.39 0.74 1.31 1.71 2.1 2.24 2.07 2.41
10.86634461
    {×/1⌈÷/⊃⍵,¨¯1⌽⍵}  (the 1000 array from pastebin)
321903884.6

Erläuterung:

Beginnen Sie mit den Wechselkursdaten:

    A←0.48 0.4 0.24 0.39 0.74 1.31 1.71 2.1 2.24 2.07 2.41

Paaren Sie jede Zahl mit der vorhergehenden (die erste wird mit der letzten gepaart) und fügen Sie sie in eine Matrix ein:

    ⎕←M←⊃A,¨¯1⌽A
0.48 2.41
0.4  0.48
0.24 0.4
0.39 0.24
0.74 0.39
1.31 0.74
1.71 1.31
2.1  1.71
2.24 2.1
2.07 2.24
2.41 2.07

Reduzieren Sie jede Zeile durch Division, behalten Sie die mit dem Verhältnis> 1 bei und kombinieren Sie die Verhältnisse durch Multiplikation. Dadurch werden alle sich wiederholenden Faktoren in einer Reihe aufeinanderfolgender steigender Wechselkurse sowie das falsche Verhältnis zwischen dem ersten und dem letzten Wechselkurs beseitigt:

    ×/1⌈÷/M
10.86634461
Tobia
quelle
Ihre Annahme, dass Sie immer auf der ersten Position verkaufen sollten, führt dazu, dass die längere Eingabe fehlschlägt und eine Zahl unter 1 zurückgibt (was offensichtlich unmöglich ist).
Frxstrem
@Frxstrem danke, behoben. Jetzt gibt es das gleiche Ergebnis wie Ihr Skript. Es wäre hilfreicher gewesen, wenn das OP uns einige Testfälle mit Ergebnissen gegeben hätte!
Tobia
1
Ich liebe gute APL-Lösungen, weil sie, wenn ich sie mir ansehe, meinen "Dies ist Binärdatei-Kauderwelsch" -Filter auslösen und ich beginne, nach einer Dateierweiterung zu suchen, um herauszufinden, wie man sie öffnet.
Cort Ammon - Reinstate Monica
@CortAmmon ist keineswegs unbegründet: APL beschäftigt viele Dutzend Grafikoperatoren. An der Oberfläche können sie Sie an die Symbole aus 8-Bit-DOS-Zeichensätzen erinnern. Es ist auch eine sehr knappe Sprache, was bedeutet, dass eine APL-Zeile eine sehr hohe Informationsentropie aufweist. Diese beiden Funktionen lösen zusammen das Gefühl einer Binärdatei aus, die in einem DOS-Fenster abgelegt wird. Es dauert jedoch nur so lange, bis Sie die Schönheit der APL-Symbole und -Syntax kennen.
Tobia
6

Python, 47

f=lambda t:2>len(t)or max(t[1]/t[0],1)*f(t[1:])

Beispiellauf am Testfall .

Nehmen Sie eine Liste der Schwimmer. Multipliziert rekursiv den Gewinnfaktor aus den ersten beiden Elementen, bis weniger als zwei Elemente übrig bleiben. Für den Basisfall gibt Truewas gleich 1.

Die Verwendung popgibt die gleiche Anzahl von Zeichen.

f=lambda t:2>len(t)or max(t[1]/t.pop(0),1)*f(t)

So geht es ab dem Ende der Liste.

f=lambda t:2>len(t)or max(t.pop()/t[-1],1)*f(t)

Zum Vergleich, mein iterativer Code in Python 2 ist 49 Zeichen, 2 Zeichen länger

p=c=-1
for x in input():p*=max(x/c,1);c=x
print-p

Beginnen mit c=-1ist ein Hack, um die imaginäre erste "Bewegung" nie einen Gewinn zu zeigen. Starten des Produkt -1anstatt 1uns zusammen beide Elemente zuweisen können, und wir negative es kostenlos zurück vor dem Druck.

xnor
quelle
Der längere Testfall überschreitet das Standard-Rekursionslimit um 1. f (x [: 999]) liefert jedoch immer noch das richtige Ergebnis. Für längere Eingaben können Sie es in ([n:(n+1)*500 + 1] for n in range(N_elem/500) )
Blöcke aufteilen
Das Rekursionslimit ist implementierungsabhängig. Sie können Stackless Python verwenden , um dies zu vermeiden.
xnor
Oder verwenden Sie einfach sys.setrecursionlimit(in CPython)
user253751
3

Python, 79 81 76 77 Bytes

f=lambda x:reduce(float.__mul__,(a/b for a,b in zip(x[1:],x[:-1]) if a>b),1.)

xist die als Liste codierte Eingabe. Die Funktion gibt den Faktor zurück.

Frxstrem
quelle
Vielleicht ist es nur meine Python-Version, aber ich musste 1.statt 1am Ende der Funktion TypeError verwenden, sonst erhalte ich TypeError: Deskriptor ' mul ' benötigt ein ' float' -Objekt, erhält aber ein' int '
Tobia
Übrigens, intelligenter Algorithmus!
Tobia
Diesen f=Teil brauchst du nicht .
wizzwizz4
2

CJam, 33 Bytes

q~{X1$3$-:X*0>{\;}*}*](g\2/{~//}/

Dies kann weiter golfen werden.

Nimmt Eingaben von STDIN wie

[0.48 0.4 0.24 0.39 0.74 1.31 1.71 2.1 2.24 2.07 2.41]

und gibt den Faktor wie folgt an STDOUT aus

10.866344605475046

Probieren Sie es hier online aus

Optimierer
quelle
1

Pyth , 18

u*GeS,1ceHhHC,QtQ1

Erläuterung:

u                 reduce, G is accumulator, H iterates over sequence
 *G               multiply G by
   eS             max(               
     ,1               1,
       ceHhH            H[1]/H[0])
 C                H iterates over zip(
  ,QtQ                                Q,Q[1:])
 1                G is initialized to 1

max(H[1]/H[0],1) idee danke an @xnor

isaacg
quelle
1

C #, 333 , 313

Mein erster Versuch. Könnte es wahrscheinlich mehr optimieren, aber wie ich schon sagte, der erste Versuch wird es schaffen !.

double a(double [] b){var c=0.0;var d=1;for(int i=0;i<b.Count();i++){c=(d==1)?(((i+1)<b.Count()&&b[i+1]<=b[i]&&d==1)?((c==0)?b[i]:b[i]*c):((i+1)>=b.Count()?(c*b[i])/b[0]:c)):((i+1)<b.Count()&&b[i+1]>b[i]&&d==0)?c/b[i]:c;d=((i+1)<b.Count()&&b[i+1]<b[i]&&d==1)?0:((i+1)<b.Count()&&b[i+1]>b[i]&&d==0)?1:d;}return c;}

Eingang

0.48, 0.4, 0.24, 0.39, 0.74, 1.31, 1.71, 2.1, 2.24, 2.07, 2.41

Ausgabe

10.86

Bearbeiten: Dank an DenDenDo für den Vorschlag, nicht math.floor zum Runden zu verwenden und int anstelle von bool zum Schneiden von Zeichen zu verwenden. Werde mich für zukünftige Rätsel daran erinnern!

Darren Breen
quelle
Heya, danke für die Tipps. Ich habe wie vorgeschlagen aktualisiert.
Darren Breen
Sieht so aus, als würden Sie auf zwei Ziffern runden, Math.Floor(...)was nicht erforderlich ist. Ich weiß auch nicht, ob es in C # möglich ist, aber normalerweise können Sie 1 und 0 für trueund verwenden false.
DenDenDo
Tut mir leid, ich dachte, es wäre eine Rundung auf 2 erforderlich, da jeder 10,86 druckte und ich 10,866 bekam und aufrundete. Sie können für andere c-Sprachen aber nicht für C #. Obwohl das mir eine Idee gab, verwende ich jetzt 1 und 0 für meine Booleschen Prüfungen. Reduziert es ein bisschen mehr. Vielen Dank!
Darren Breen