Fractionner le projet de loi

12

Tâche

Supposons que le ppepole doive scinder un projet de loi; chacun d'eux est identifié par un triple (Name, n, k)composé de:

  • Name: le nom ;
  • n: le montant qu'elle doit payer ;
  • k: le montant effectivement payé .

Le défi ici est de savoir combien doit à qui.

Hypothèses

  • L'entrée et la sortie peuvent être dans n'importe quel format pratique.

  • p N,n N+, k N.

  • p >1.

  • Les noms sont des chaînes uniques de longueur arbitraire, composées de caractères alphabétiques en minuscules.

Solution

La solution est représentée par l'ensemble minimal de transactions entre les ppersonnes; en particulier, ce sont des triplets(from, to, amount)

  • from: namede la personne qui donne de l'argent;
  • to: namede la personne qui reçoit de l'argent;
  • amount: montant d'argent de la transaction.

REMARQUE : La somme de toutes les dettes ( n) peut différer de la somme de tous les montants déjà payés ( k). Dans ce cas, vous devez ajouter la sortie ('owner', Name, amount)ou (Name, 'owner', amount)le format que vous avez choisi. N'importe quel nom ne sera jamais owner. La chaîne «propriétaire» est flexible.

S'il existe plusieurs ensembles minimaux, sélectionnez celui avec la somme minimale de tous les montants de transaction (valeurs absolues); en cas d'égalité, choisissez-en un.

Cas de 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)]

C'est le code-golf: le code le plus court gagne .

Vedant Kandoi
la source
1
Je pense que vous devriez changer "Vous devez sortir le scénario le plus simplifié qui nécessite le moins de transactions." à "Vous devez générer le scénario qui nécessite le moins de transactions. Si plusieurs de ces scénarios existent, choisissez-en un avec le moins de notionnels de transactions." car je crois que c'est plus clair.
Jonathan Allan
1
Beau défi. Mais cela nécessitera probablement des cas de test plus complexes pour s'assurer que les solutions sont toujours toujours optimales.
Arnauld
3
Qu'est-ce que le «moins total notionnel»?
lirtosiast
1
@JonathanAllan encore confus par le mot «notionnel». D'où cela vient-il? sur la base du cas de test 3, il semble que l'on devrait préférer les réponses où la même personne ne donne pas et ne reçoit pas à la fois? Est-ce exact? aussi pourquoi est-ce décrit comme notionnel?
Jonah
1
@Jonah J'ai utilisé "notionnel total" car les directions des transactions ne devraient pas être prises en compte (juste leur taille absolue), bien que je réalise maintenant que c'est quelque peu redondant (car avoir deux transactions qui s'opposent ne serait pas considéré une fois le bris d'égalité) !). [Notionnel est juste le terme utilisé en finance.]
Jonathan Allan

Réponses:

2

JavaScript (ES6),  252 227 223 222  215 octets

Prend l'entrée comme [[n0, k0, name0], [n1, k1, name1], ...].

Les transactions dans la solution peuvent être positives ou négatives. Le propriétaire est appelé non défini .

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

Essayez-le en ligne!

Commenté

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
Arnauld
la source