Comerciante de bolsa de viaje en el tiempo

21

Historia
Hace mucho tiempo, Bobby creó una billetera Bitcoin con 1 Satoshi (1e-8 BTC, la unidad monetaria más pequeña) y se olvidó de ella. Como muchos otros, más tarde pensó "Maldita sea, si solo invirtiera más en ese entonces ...".
Sin detenerse a soñar despierto, dedica todo su tiempo y dinero a construir una máquina del tiempo. Pasa la mayor parte de su tiempo en su garaje, sin darse cuenta de los asuntos mundanos y los rumores que circulan sobre él. Completa el prototipo un día antes de que su electricidad esté a punto de apagarse debido a pagos atrasados. Al levantar la vista de su banco de trabajo, ve una camioneta de la policía que se detiene en su casa, parece que los vecinos entrometidos pensaron que estaba ejecutando un laboratorio de metanfetamina en su garaje y llamó a la policía.
Sin tiempo para ejecutar pruebas, toma una memoria USB con los datos de la tasa de cambio de los últimos años, conecta el condensador de flujo al descombobulador cuántico y se encuentra transportado al día en que creó su billetera.

Tarea
Teniendo en cuenta los datos del tipo de cambio, descubra cuánto dinero puede ganar Bobby. Sigue una regla muy simple: "Compra bajo - vende alto" y dado que comienza con un capital infinitamente pequeño, asumimos que sus acciones no tendrán impacto en los tipos de cambio del futuro.

Entrada
Una lista de flotantes> 0, ya sea como una cadena separada por un solo carácter (nueva línea, tabulación, espacio, punto y coma, lo que prefiera) pasado como argumento de línea de comando al programa, leído desde un archivo de texto o STDIN o pasado como un parámetro a una función. Puede usar tipos de datos numéricos o matrices en lugar de una cadena porque es básicamente una cadena con corchetes.

Producto
El factor por el cual el capital de Bobbys se multiplica por el final de la negociación.

Ejemplo

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

Tipo de cambio: 0.48 $ / BTC, ya que está a punto de caer, vendemos todos los Bitcoins por 4.8 nanodólares. Factor = 1 Tipo de cambio: 0.4, no hacer nada
Tipo de cambio: 0.24 $ / BTC y en aumento: convierta todo $ a 2 Satoshis. Factor = 1 (el valor en dólares aún no ha cambiado)
Tipo de cambio: 0.39 - 2.1 $ / BTC: no hacer nada
Tipo de cambio: 2.24 $ / BTC: vender todo antes de la caída. 44.8 nanodólar, factor = 9.33
Tipo de cambio: 2.07 $ / BTC: comprar 2.164 Satoshis, factor = 9.33
Tipo de cambio: 2.41 $ / BTC: comprar 52.15 nanodólar, factor = 10.86

Output: 10.86

Detalles adicionales
Puede ignorar casos extremos extraños como entrada constante, valores cero o negativos, solo un número de entrada, etc.
Siéntase libre de generar sus propios números aleatorios para probar o usar gráficos de acciones reales. Aquí hay una entrada más larga para la prueba (Salida esperada aprox. 321903884.638)
Explique brevemente qué hace su código Los
gráficos son apreciados pero no necesarios

DenDenDo
fuente
Si tomamos los números a través del argumento de función, ¿todavía tiene que ser una cadena, o podríamos tomar directamente una matriz?
Martin Ender
@ MartinBüttner Lo consideré durante un tiempo, ya sea que la entrada sea una cadena, una matriz numérica o una opción libre, siempre hay algunos idiomas que obtienen una ventaja. No parece haber un consenso general al respecto, y escribir dos programas, uno para una entrada numérica y otro para el ingreso de cadenas y promediar ambos puntajes parece una exageración.
DenDenDo
¿Qué pasa con la unidad de improbabilidad infinita? :)
Pomo de la puerta
2
Volviendo al problema, ¿necesitamos redondear los valores BTC y / o $ con una precisión dada, en cada iteración? Por ejemplo, en el mundo real, la billetera BTC de uno debe redondearse al Satoshi. Esto hace la diferencia, porque en su ejemplo, a 2.07 solo puede comprar 2 (no 2.164); entonces a 2.41 tus 2s te compran 48.2 n $ (no 52.15) por lo que el factor es 10.04 (no 10.86). A menos que mantenga una billetera $ separada con el cambio y necesite volver a agregarla cada vez. ¿Y los dólares? ¿Alguien puede afirmar hoy tener un nanodólar? Creo que la cantidad más pequeña que uno puede tener es 1 ¢.
Tobia
1
@CortAmmon: ¿estás diciendo que el comercio de BTC no es caótico? ;-)
Steve Jessop

Respuestas:

10

APL, 16 caracteres

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

Esta versión utiliza @Frxstrem 's más simple algoritmo y @xnor ' s max(r,1)idea.

También supone que la serie está aumentando en general, es decir, el primer valor de bitcoin es más pequeño que el último. Esto es consistente con la descripción del problema. Para obtener una fórmula más general, se deben descartar las dos primeras tasas, agregando 2 caracteres:{×/1⌈÷/⊃1↓⍵,¨¯1⌽⍵}

Ejemplo:

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

Explicación:

Comience con los datos del tipo de cambio:

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

Empareje cada número con el anterior (el primero se emparejará con el último) y póngalos en una matriz:

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

Reduzca cada fila por división, mantenga las que tengan una relación> 1 y combine las razones por multiplicación. Esto eliminará cualquier factor de repetición en una fila de tasas crecientes sucesivas, así como la relación espuria entre la primera y la última tasa de cambio:

    ×/1⌈÷/M
10.86634461
Tobia
fuente
Su suposición de que siempre debe vender en la primera posición hace que la entrada más larga falle y devuelve un número menor que 1 (lo que obviamente es imposible).
Frxstrem
@Frxstrem gracias, arreglado. Ahora da el mismo resultado que tu script. ¡Hubiera sido más útil si el OP nos hubiera dado algunos casos de prueba con resultados!
Tobia
1
Me encantan las buenas soluciones APL porque, cada vez que las miro, activan mi filtro "esto es un truco de archivos binarios" y empiezo a buscar una extensión de archivo para descubrir cómo abrirlo.
Cort Ammon - Restablece a Mónica el
@CortAmmon no tiene ningún fundamento: APL emplea muchas docenas de operadores gráficos; en la superficie pueden recordarle los símbolos de los juegos de caracteres DOS de 8 bits. También es un lenguaje muy breve, lo que significa que una línea de APL tiene una entropía de información muy alta. Estas dos características se combinan para provocar la sensación de un archivo binario volcado en una ventana de DOS. Pero solo dura hasta que aprende a apreciar la belleza de los símbolos y la sintaxis de APL.
Tobia
6

Python, 47

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

Ejemplo ejecutado en el caso de prueba .

Toma una lista de carrozas. Multiplica recursivamente el factor de ganancia de los dos primeros elementos hasta que queden menos de dos elementos. Para el caso base, da Truecuál es igual 1.

El uso popda el mismo número de caracteres.

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

Lo mismo ocurre al final de la lista.

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

A modo de comparación, mi código iterativo en Python 2 es 49 caracteres, 2 caracteres más largos

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

Comenzar con c=-1un truco para hacer que el primer "movimiento" imaginario nunca muestre ganancias. Al iniciar el producto en -1lugar de 1permitirnos asignar ambos elementos juntos, lo rechazamos de forma gratuita antes de imprimirlo.

xnor
fuente
Sin embargo, el caso de prueba más largo excede el límite de recursión predeterminado en 1. f (x [: 999]) todavía da el resultado correcto. Para una entrada más larga, puede dividirla en trozos ([n:(n+1)*500 + 1] for n in range(N_elem/500) )y multiplicar los factores parciales
DenDenDo
El límite de recursión depende de la implementación; puedes usar Stackless Python para evitarlo.
xnor
O simplemente use sys.setrecursionlimit(en 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.)

xes la entrada codificada como una lista. La función devuelve el factor.

Frxstrem
fuente
Tal vez sea solo mi versión de Python, pero tuve que usar en 1.lugar de 1al final de la función, de lo contrario obtengo TypeError: el descriptor ' mul ' requiere un objeto 'flotante' pero recibió un 'int'
Tobia
Por cierto, algoritmo inteligente!
Tobia
No necesitas esa f=parte.
wizzwizz4
2

CJam, 33 bytes

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

Esto se puede jugar más al golf.

Toma información de STDIN, como

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

y genera el factor en STDOUT, como

10.866344605475046

Pruébalo en línea aquí

Optimizador
fuente
1

Pyth , 18

u*GeS,1ceHhHC,QtQ1

Explicación:

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) idea gracias a @xnor

isaacg
fuente
1

C #, 333 , 313

Mi primer intento Probablemente podría optimizarlo más, pero como dije, ¡primer intento, así que me acostumbraré!

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

Entrada

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

Salida

10.86

Editar: Gracias a DenDenDo por sugerir no usar math.floor para redondear y usar int en lugar de bool para cortar caracteres. ¡Recordará eso para futuros rompecabezas!

Darren Breen
fuente
Hola, gracias por los consejos. Actualicé según lo sugerido.
Darren Breen
Parece que está redondeando a dos dígitos con los Math.Floor(...)que no se requiere. Además, no sé si es posible en C #, pero generalmente puede usar 1 y 0 para truey false.
DenDenDo
Lo siento, pensé que era necesario redondear a 2 porque todos imprimían 10.86 y yo obtenía 10.866 y redondeo. Puede para otros lenguajes c pero no para C #. Aunque eso me dio una idea, ahora estoy usando 1 y 0 para mis cheques booleanos. Lo reduje un poco más. ¡Gracias!
Darren Breen