Négociant dans le temps

21

Récemment
, Bobby a créé un portefeuille Bitcoin avec 1 Satoshi (1e-8 BTC, la plus petite unité monétaire) et l'a oublié. Comme beaucoup d'autres, il a dit plus tard "Merde, si seulement j'investissais plus à l'époque ...".
Ne s'arrêtant pas à la rêverie, il consacre tout son temps et son argent à la construction d'une machine à voyager dans le temps. Il passe la plupart de son temps dans son garage, ignorant les affaires du monde et les rumeurs qui circulent à son sujet. Il termine le prototype un jour avant que son électricité soit sur le point d'être coupée en raison de paiements manqués. Levant les yeux de son établi, il voit une camionnette de police s'arrêter chez lui, on dirait que les voisins curieux pensaient qu'il dirigeait un laboratoire de méthamphétamine dans son garage et ont appelé les flics.
Sans temps pour effectuer les tests, il saisit une clé USB avec les données de taux de change des dernières années, connecte le condensateur de flux au Discombobulator Quantum et se retrouve transporté au jour où il a créé son portefeuille

Tâche
Compte tenu des données sur le taux de change, découvrez combien d'argent Bobby peut gagner. Il suit une règle très simple: «Acheter bas - vendre haut» et comme il commence avec un capital infiniment petit, nous supposons que ses actions n'auront pas d'impact sur les taux de change du futur.

Entrée
Une liste de flottants> 0, soit sous forme de chaîne séparée par un seul caractère (nouvelle ligne, tabulation, espace, point-virgule, tout ce que vous préférez) passée en argument de ligne de commande au programme, lue dans un fichier texte ou STDIN ou passée en paramètre à une fonction. Vous pouvez utiliser des types de données numériques ou des tableaux au lieu d'une chaîne, car il s'agit essentiellement d'une chaîne avec des crochets.

Production
Le facteur par lequel le capital de Bobby s'est multiplié à la fin de la négociation.

Exemple

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

Taux de change: 0,48 $ / BTC, car il est sur le point de baisser, nous vendons tous les Bitcoins pour 4,8 nanodollars. Facteur = 1 Taux de change: 0,4, ne rien faire
Taux de change: 0,24 $ / BTC et en hausse: convertir tous les $ en 2 Satoshis. Facteur = 1 (la valeur en dollars reste inchangée)
Taux de change: 0,39 - 2,1 $ / BTC: ne rien faire
Taux de change: 2,24 $ / BTC: tout vendre avant la baisse. 44,8 nanodollar, facteur = 9,33
Taux de change: 2,07 $ / BTC: acheter 2,164 Satoshis, facteur = 9,33
Taux de change: 2,41 $ / BTC: acheter 52,15 nanodollar, facteur = 10,86

Output: 10.86

Détails supplémentaires
Vous pouvez ignorer des cas de bord étranges tels qu'une entrée constante, des valeurs nulles ou négatives, un seul numéro d'entrée, etc.
N'hésitez pas à générer vos propres nombres aléatoires pour tester ou utiliser des graphiques boursiers réels. Voici une entrée plus longue pour les tests (Sortie attendue environ 321903884.638)
Expliquez brièvement ce que fait votre code Les
graphiques sont appréciés mais pas nécessaires

DenDenDo
la source
Si nous prenons les nombres via l'argument de fonction, doit-il toujours s'agir d'une chaîne, ou pourrions-nous prendre directement un tableau?
Martin Ender
@ MartinBüttner J'ai réfléchi à ce sujet pendant un certain temps, que l'entrée soit une chaîne, un tableau numérique ou un choix libre, il y a toujours des langues qui ont un avantage. Il ne semble pas y avoir de consensus général à ce sujet, et l'écriture de deux programmes, un pour une entrée numérique et une pour l'entrée de chaîne et la moyenne des deux scores semble exagérée.
DenDenDo
Qu'en est-il de l'Infinite Improbability Drive? :)
Poignée de porte
2
Pour en revenir au problème, devons-nous arrondir les valeurs BTC et / ou $ avec une précision donnée, à chaque itération? Par exemple, dans le monde réel, son portefeuille BTC doit être arrondi au Satoshi. Cela fait une différence, car dans votre exemple, à 2.07, vous ne pouvez acheter que des 2 (pas 2.164); puis à 2,41 vos 2 vous achètent 48,2 n $ (pas 52,15) donc le facteur est 10,04 (pas 10,86). Sauf si vous conservez un portefeuille $ distinct avec la modification et que vous devez l'ajouter à chaque fois. Et l'argent? Quelqu'un peut-il prétendre aujourd'hui avoir un nanodollar? Je crois que le plus petit montant que l'on puisse détenir est de 1 ¢.
Tobia
1
@CortAmmon: vous dites que le trading BTC n'est pas chaotique? ;-)
Steve Jessop

Réponses:

10

APL, 16 caractères

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

Cette version utilise @Frxstrem « s algorithme plus simple et @xnor de » max(r,1)l'idée.

Cela suppose également que la série augmente globalement, c'est-à-dire que la première valeur de bitcoin est plus petite que la dernière. Ceci est cohérent avec la description du problème. Pour obtenir une formule plus générale, les deux premiers taux doivent être supprimés, en ajoutant 2 caractères:{×/1⌈÷/⊃1↓⍵,¨¯1⌽⍵}

Exemple:

    {×/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

Explication:

Commencez avec les données de taux de change:

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

Jumelez chaque numéro avec le précédent (le premier sera jumelé avec le dernier) et mettez-les dans une matrice:

    ⎕←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

Réduisez chaque ligne par division, conservez celles dont le ratio est> 1 et combinez les ratios par multiplication. Cela éliminera tous les facteurs qui se répètent dans une rangée de taux de hausse successifs, ainsi que le faux rapport entre le premier et le dernier taux de change:

    ×/1⌈÷/M
10.86634461
Tobia
la source
Votre hypothèse selon laquelle vous devez toujours vendre sur la première position fait échouer l' entrée la plus longue et renvoie un nombre inférieur à 1 (ce qui est évidemment impossible).
Frxstrem
@Frxstrem thanks, fixed. Maintenant, il donne le même résultat que votre script. Cela aurait été plus utile si le PO nous avait donné quelques cas de test avec des résultats!
Tobia,
1
J'adore les bonnes solutions APL car, chaque fois que je les regarde, elles déclenchent mon filtre "c'est un charabia de fichier binaire" et je commence à chercher une extension de fichier pour comprendre comment l'ouvrir.
Cort Ammon - Rétablir Monica
@CortAmmon n'est pas du tout infondé: APL emploie plusieurs dizaines d'opérateurs graphiques; en surface, ils peuvent vous rappeler les symboles des jeux de caractères DOS 8 bits. C'est également un langage très laconique, ce qui signifie qu'une ligne d'APL a une entropie d'information très élevée. Ces deux fonctionnalités se combinent pour déclencher l'impression d'un fichier binaire déversé dans une fenêtre DOS. Mais cela ne dure que jusqu'à ce que vous appreniez à apprécier la beauté des symboles et de la syntaxe d'APL.
Tobia
6

Python, 47

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

Exemple exécuté sur le cas de test .

Prenez une liste de flotteurs. Multiplie récursivement le facteur de profit des deux premiers éléments jusqu'à ce qu'il reste moins de deux éléments. Pour le cas de base, donne Truece qui est égal 1.

L'utilisation popdonne le même nombre de caractères.

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

Il en va de même de la fin de la liste.

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

A titre de comparaison, mon code itératif en Python 2 est de 49 caractères, 2 caractères de plus

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

Commencer par c=-1est un hack pour que le premier "mouvement" imaginaire ne montre jamais de profit. Le démarrage du produit au -1lieu de 1nous permet d'affecter les deux éléments ensemble, et nous le remboursons gratuitement avant l'impression.

xnor
la source
Le cas de test plus long dépasse la limite de récursivité par défaut de 1. f (x [: 999]) donne quand même le résultat correct. Pour une entrée plus longue, vous pouvez le diviser en morceaux ([n:(n+1)*500 + 1] for n in range(N_elem/500) )et multiplier les facteurs partiels
DenDenDo
La limite de récursivité dépend de l'implémentation; vous pouvez utiliser Stackless Python pour l'éviter.
xnor
Ou utilisez simplement sys.setrecursionlimit(dans CPython)
user253751
3

Python, 79 81 76 77 octets

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

xest l'entrée codée sous forme de liste. La fonction renvoie le facteur.

Frxstrem
la source
Peut-être que c'est juste ma version Python, mais j'ai dû utiliser à la 1.place de 1à la fin de la fonction, sinon j'obtiens TypeError: le descripteur ' mul ' nécessite un objet 'float' mais a reçu un 'int'
Tobia
BTW, algorithme intelligent!
Tobia
Vous n'avez pas besoin de cette f=partie.
wizzwizz4
2

CJam, 33 octets

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

Cela peut être joué plus loin.

Prend l'entrée de STDIN, comme

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

et envoie le facteur à STDOUT, comme

10.866344605475046

Essayez-le en ligne ici

Optimiseur
la source
1

Pyth , 18

u*GeS,1ceHhHC,QtQ1

Explication:

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) idée grâce à @xnor

isaacg
la source
1

C #, 333 , 313

Ma première tentative. Pourrait probablement l'optimiser davantage, mais comme je l'ai dit lors de la première tentative, vous y arriverez!

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;}

Contribution

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

Production

10.86

Edit: Merci à DenDenDo d'avoir suggéré de ne pas utiliser math.floor pour arrondir et d'utiliser int au lieu de bool pour couper les caractères. Je m'en souviendrai pour les futurs puzzles!

Darren Breen
la source
Heya, merci pour les conseils. J'ai mis à jour comme suggéré.
Darren Breen du
Il semble que vous arrondissiez à deux chiffres, Math.Floor(...)ce qui n'est pas nécessaire. De plus, je ne sais pas si c'est possible en C #, mais généralement vous pouvez utiliser 1 et 0 pour trueet false.
DenDenDo
Désolé, j'ai pensé que l'arrondi à 2 était nécessaire car tout le monde imprimait 10,86 et j'obtenais 10,866 et arrondis. Vous pouvez le faire pour d'autres langages c mais pas pour C #. Bien que cela m'ait donné une idée, j'utilise maintenant 1 et 0 pour mes vérifications booléennes. Réduit un peu plus. Merci!
Darren Breen