Dividere il conto


12

Compito

Supponiamo che il ppepole debba dividere un conto; ognuno di essi è identificato da una tripla (Name, n, k)composta da:

  • Name: il nome ;
  • n: l'importo che deve pagare ;
  • k: l'importo effettivamente pagato .

La sfida qui è scoprire quanto deve a chi.

ipotesi

  • L'input e l'output possono essere in qualsiasi formato conveniente.

  • p N,n N+, k N.

  • p >1.

  • I nomi sono stringhe uniche di lunghezza arbitraria, composte da caratteri alfabetici minuscoli.

Soluzione

La soluzione è rappresentata dall'insieme minimo di transazioni tra le ppersone; in particolare sono triple(from, to, amount)

  • from: namedella persona che dà soldi;
  • to: namedella persona che riceve denaro;
  • amount: importo della transazione.

NOTA : la somma di tutti i debiti ( n) può differire dalla somma di tutti gli importi già pagati ( k). In questo caso, devi aggiungere l'output ('owner', Name, amount)o (Name, 'owner', amount)il formato che hai scelto. Qualsiasi nome non sarà mai owner. La stringa "proprietario" è flessibile.

Se esistono più set mimimum, selezionare quello con la somma minima di tutti gli importi delle transazioni (valori assoluti); in caso di pareggio, scegline uno.

Casi test:

inputs(Name,n,k):
[('a',30,40),('b',40,50),('c',30,15)]
[('a',30,30),('b',20,20)]
[('a',30,100),('b',30,2),('c',40,0)]
[('a',344,333),('b',344,200),('c',2,2)]
[('a',450,400),('b',300,300),('c',35,55)]

outputs(from, to, amount):
[('c','a',10),('c','b',5),('owner','b',5)] or [('c','b',10),('c','a',5),('owner','a',5)]
[]
[('owner','a',2),('b','a',28),('c','a',40)] PS: [('owner','a',2),('b','a',68),('c','b',40)] has the same number of transactions, but it is not a valid answer, because the total amount of its transaction is greater than that of the proposed solution.
[('a','owner',11),('b','owner',144)]
[('a','owner',30),('a','c',20)]

Questo è code-golf: vince il codice più corto .


1
Penso che dovresti cambiare "Devi produrre lo scenario più semplificato che richiede il minor numero di transazioni". a "Devi generare lo scenario che richiede il minor numero di transazioni. Se esistono più scenari di questo tipo, scegline uno con il minimo nozionale totale delle transazioni." poiché credo che sia più chiaro.
Jonathan Allan,

1
Bella sfida. Ma probabilmente richiederà casi di test più complessi per assicurarsi che le soluzioni siano sempre ottimali.
Arnauld,

3
Che cos'è il "nozionale meno totale"?
lirtosiast

1
@JonathanAllan ancora confuso dalla parola "nozionale". Da dove viene? in base al caso 3 sembra che si debbano preferire le risposte in cui la stessa persona non dà e non riceve? è corretto? anche perché è descritto come nozionale?
Giona

1
@Jonah Ho usato il "nozionale totale" poiché le direzioni delle transazioni non dovrebbero essere prese in considerazione (ma solo la loro dimensione assoluta), anche se ora mi rendo conto che è in qualche modo ridondante (poiché avere due transazioni che si contrastano a vicenda non sarebbe considerato una volta in pareggio !). [Nozionale è solo il termine usato in finanza.]
Jonathan Allan,

Risposte:


2

JavaScript (ES6),  252 227 223 222  215 byte

Accetta input come [[n0, k0, name0], [n1, k1, name1], ...].

Le transazioni nella soluzione possono essere positive o negative. Il proprietario si chiama indefinito .

a=>(m=g=(B,t,s)=>B.some(x=>x)?B.map((b,x)=>B.map((c,y)=>b*c<0&b*b<=c*c&&g(C=[...B],[...t,[a[x][2],b,a[y][2]]],s+a.length-1/b/b,C[C[y]+=b,x]=0))):m<s||(r=t,m=s))([...a.map(([x,y])=>t-(t+=y-x),t=0),t],[],a.push(g))&&r

Provalo online!

Commentate

a => (                              // a[] = input array
  m =                               // initialize m to a non-numeric value
  g = (B, t, s) =>                  // g = function taking: B = balances, t = transactions,
                                    //     s = score of the current solution
    B.some(x => x) ?                // if at least one balance is not equal to 0:
      B.map((b, x) =>               //   for each balance b at position x:
        B.map((c, y) =>             //     for each balance c at position y:
          b * c < 0 &               //       if b and c are of opposite sign
          b * b <= c * c &&         //       and |b| <= |c|,
          g(                        //       do a recursive call to g:
            C = [...B],             //         - with a copy C of B
            [ ...t,                 //         - append the new transaction to t[]
              [a[x][2], b, a[y][2]] //           in [from_name, amount, to_name] format
            ],                      //
            s + a.length - 1/b/b,   //         - add (a.length - 1/b²) to s
            C[C[y] += b, x] = 0     //         - update C[y] and clear C[x]
          )                         //       end of recursive call
        )                           //     end of inner map()
      )                             //   end of outer map()
    :                               // else:
      m < s ||                      //   if the score of this solution is lower than m,
      (r = t, m = s)                //   update r to t and m to s
)(                                  // initial call to g:
  [                                 //   build the list of balances:
    ...a.map(([x, y]) =>            //     each balance is equal to:
      t - (t += y - x),             //     due_value - paid_value
      t = 0                         //     keep track of the total t ...
    ),                              //
    t                               //   ... which is appended at the end of this array
  ],                                //   (this is the balance of the owner)
  [],                               //   start with t = []
  a.push(g)                         //   append a dummy owner to a[]; start with s = 1
) && r                              // return r
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.