Golf Un déjeuner gratuit

26

Trouvez une séquence d'échanges extrêmement rentable avec un tableau des taux de change.


À titre d'exemple, considérons les devises A riary (votre devise nationale), B aht, C edi et D enar où le taux de l'un à l'autre (après qu'un taux de transaction a été prélevé) est donné par l'entrée (ligne, colonne) dans le tableau des taux de change ci-dessous:

                       TO
       A          B          C          D

   A   0.9999     1.719828   4.509549   0.709929

F  B   0.579942   0.9999     2.619738   0.409959
R
O  C   0.219978   0.379962   0.9999     0.149985
M
   D   1.39986    2.429757   6.409359   0.9999

De toute évidence, l'échange de A contre A n'est pas une bonne idée car ce bureau se fera un plaisir de vous facturer pour ne rien faire.

Moins évidemment, mais vrai avec ce tableau, échanger A contre n'importe quelle autre devise, puis échanger à nouveau est un facteur de perte:

via B: 1.719828 × 0.579942 = 0.997400489976
via C: 4.509549 × 0.219978 = 0.992001569922
via D: 0.709929 × 1.39986  = 0.99380120994

Cependant, l'échange de A à D puis de D à B puis de B à A est rentable (avec suffisamment de capital pour ne pas succomber à l'arrondissement):

0.709929 × 2.429757 × 0.579942 = 1.0003738278192194

On pourrait à plusieurs reprises prendre ce "déjeuner gratuit" tant que l'occasion se présente.

Mais une chaîne encore plus séduisante existe ici, à savoir A à D puis D à C puis C à B et enfin B à A :

0.709929 × 6.409359 × 0.379962 × 0.579942 = 1.0026612752037345

Détails du défi

Étant donné un tableau des taux de change dans un format raisonnable qui fixe la signification de la devise nationale (par exemple, la 1ère ligne et la 1ère colonne sont toujours la devise nationale)
(ou étant donné un tel tableau et un indice de la devise nationale),
trouvez un * séquence maximale d'arbitrage des échanges commençant et se terminant avec la devise locale comme indices dans la liste des devises sans répéter l'utilisation d'aucun échange (c'est-à-dire qu'un échange Y-> X peut suivre un échange X-> Y, mais un X-> Y ne peut pas suivre un X-> Y).

S'il n'existe aucune opportunité rentable, indiquez une liste vide ou un autre résultat qui ne peut pas être confondu avec une opportunité identifiée.
- par exemple pour l'exemple ci-dessus ( A-> D, D-> C, C-> B, B-> A ):

  • en utilisant l'indexation 0, on pourrait retourner [[0,3],[3,2],[2,1],[1,0]]ou[0,3,2,1,0]
  • en utilisant 1-indexation on pourrait retourner [[1,4],[4,3],[3,2],[2,1]]ou[1,4,3,2,1]

Les autres formats sont corrects tant qu'il n'y a pas d'ambiguïté.
- Une chose à surveiller est qu'il est possible que la meilleure opportunité soit une seule transaction de la maison-> maison (un bureau stupide). Si vous décidez d'exclure l'index de la devise locale des deux extrémités de l'option plate ci-dessus (c'est-à [3,2,1]- dire ou [4,3,2]) et une liste vide pour "aucune opportunité", assurez-vous que le home-> home n'est pas également une liste vide.

* S'il existe plusieurs opportunités valables tout aussi rentables, renvoyez-en une, certaines ou la totalité.

L'algorithme Bellman-Ford est une façon d'aborder cela, mais ce n'est probablement pas le mieux adapté au golf.

Cas de test

Les entrées indiquées sont dans l'arrangement utilisé dans l'exemple, et les résultats affichés utilisent l'indexation 0 pour répertorier les indices de devise (lorsqu'une opportunité existe, la devise nationale est uniquement à l'extrémité arrière; aucune opportunité n'est une liste vide).

[[0.999900, 1.719828, 4.509549, 0.709929],
 [0.579942, 0.999900, 2.619738, 0.409959],
 [0.219978, 0.379962, 0.999900, 0.149985],
 [1.399860, 2.429757, 6.409359, 0.999900]]  ->  [3, 2, 1, 0]

[[0.9999, 1.5645, 0.9048, 1.0929],
 [0.6382, 0.9999, 0.5790, 0.6998],
 [1.1051, 1.7269, 0.9999, 1.2087],
 [0.9131, 1.4288, 0.8262, 0.9999]]  ->  [1, 2, 0]

[[0.9999, 1.4288, 0.8262, 0.9131],
 [0.6998, 0.9999, 0.5790, 0.6382],
 [1.2087, 1.7269, 0.9999, 1.1051],
 [1.0929, 1.5645, 0.9048, 0.9999]]  ->  [1, 2, 3, 1, 0]

[[1.002662, 1.719828, 4.509549, 0.709929],
 [0.579942, 0.999900, 2.619738, 0.409959],
 [0.219978, 0.379962, 0.999900, 0.149985],
 [1.399860, 2.429757, 6.409359, 0.999900]]  ->  [3, 2, 1, 0, 0]

[[1.002662, 1.719828, 4.509549, 0.709929],
 [0.579942, 1.002604, 2.619738, 0.409959],
 [0.219978, 0.379962, 1.003000, 0.149985],
 [1.399860, 2.429757, 6.409359, 1.002244]]  ->  [3, 3, 2, 2, 1, 1, 0, 0]

[[0.9999, 1.4288, 0.8262, 0.9131],
 [0.6998, 0.9999, 0.5790, 0.6382],
 [1.2087, 1.7269, 1.0001, 1.1051],
 [1.0929, 1.4974, 0.9048, 0.9999]]  ->  [1, 2, 2, 0]

[[0.9999, 1.3262, 0.7262, 0.9131],
 [0.6998, 0.9999, 0.5490, 0.6382],
 [1.2087, 1.7269, 0.9999, 1.2051],
 [1.0929, 1.5645, 0.9048, 0.9999]]  ->  [3, 2, 3, 1, 0]

[[0.9999, 1.5645, 0.9048, 0.5790],
 [0.6382, 0.9999, 0.5790, 0.3585],
 [1.1051, 1.7269, 0.9999, 0.6391],
 [1.7271, 2.6992, 1.5645, 0.9999]]  ->  [1, 2, 0]  and/or  [3, 2, 0]

[[0.9999, 1.2645, 0.7048, 0.3790],
 [0.4382, 0.9999, 0.3790, 0.1585],
 [1.0001, 1.5269, 1.0001, 0.4391],
 [1.5271, 2.4992, 1.3645, 0.9999]]  ->  []

[[0.9999, 1.2645, 0.7048, 0.3790],
 [0.4382, 0.9999, 0.3790, 0.1585],
 [0.9999, 1.5269, 1.4190, 0.4391],
 [1.5271, 2.4992, 1.3645, 0.9999]]  ->  [2, 2, 0]

Il s'agit de donc la solution la plus courte en octets l'emporte, mais la compétition devrait également se faire en intra-langue, alors ne laissez pas les langues de golf par code vous empêcher de soumettre dans votre langue préférée!

Jonathan Allan
la source

Réponses:

8

JavaScript (ES6), 122 113 103 octets

Prend l'entrée comme une matrice transposée par rapport au format décrit dans le défi. Renvoie une chaîne décrivant les échanges au (from,to)format.

a=>(g=(s,x=b=0,h='')=>a.map((r,y)=>~h.search(k=`(${x},${y})`)||g(s*r[x],y,h+k),x|s<b||(b=s,p=h)))(1)&&p

Premier cas de test: essayez-le en ligne!

Plus de cas de test: essayez-le en ligne!

Commenté

a => (                  // given the exchange rate matrix a[][]
  g = (                 // g = recursive function taking:
    s,                  //   s = current amount of money
    x = b = 0,          //   x = ID of current currency, b = best result so far
    h = ''              //   h = exchange history, as a string
  ) =>                  //  
  a.map((r, y) =>       // for each row at position y in a[]:
    ~h.search(          //   if we can't find in h ...
      k = `(${x},${y})` //     ... the exchange key k from currency x to currency y
    ) ||                //   then:
    g(                  //   do a recursive call to g() with:
      s * r[x],         //     s = new amount obtained by applying the exchange rate
      y,                //     x = y
      h + k             //     h = h + k
    ),                  //   end of recursive call
    x | s < b ||        //   if x is our home currency and s is greater than or equal to b
    (b = s, p = h)      //   then set b to s and set p to h
  )                     // end of map()
)(1)                    // initial call to g() with s = 1
&& p                    // return p
Arnauld
la source
4

Python 2 , 143 125 124 octets

lambda M:g(M)[1]
g=lambda M,s=[],p=1,x=0:max([(p,s)]*-~-x+[g(M,s+[(x,y)],p*M[x][y],y)for y in range(len(M))if(x,y)not in s])

Essayez-le en ligne!

Utilise une indexation basée sur 0 (0 est la devise nationale); renvoie une liste de tuples des échanges donnant le paiement maximum.

L'approche est la force brute: via la récursivité, nous finissons par visiter chaque chemin non répétitif à partir de 0(pour nêtre le nombre de devises, cela donne une profondeur maximale de n^2). Pour le sous-ensemble de ces chemins se terminant également par «0», nous maximisons le gain.

Chas Brown
la source
1

Haskell, 175 octets

e?l|e`elem`l=0|2>1=1
w[]=[]
w l=[maximum l];0!q=[q]
n!c@(v,i,(h,l))=do{j<-[0..3];c:((n-1)!(v*l!!i!!j*(i,j)?h,j,((i,j):h,l)))}
z l=w$filter(\(v,e,_)->v>1&&e==0)$12!(1,0,([],l))

Essayez-le ici

Radek
la source