Cosa c'è nella mia salsa di pasta?


37

sfondo

In Francia, e probabilmente nel resto dell'Unione europea, tutti gli alimenti disponibili per la vendita devono elencare gli ingredienti che lo compongono sulla confezione, in ordine di percentuale decrescente in peso . Tuttavia, non è necessario indicare la percentuale esatta , a meno che l'ingrediente non sia evidenziato dal testo o da un'immagine sulla copertina.

Ad esempio, la mia salsa di pomodoro al basilico, che mostra solo alcuni grandi pomodori rossi e belle foglie di basilico sulla confezione, ha le seguenti indicazioni:

Ingredienti: pomodori 80%, cipolle a pezzi, basilico 1,4%, sale marino, aglio schiacciato, zucchero di canna crudo, olio extra vergine di oliva, pepe nero.

Sembra salato, ma ... quante cipolle mangerò , esattamente?

Sfida

Dato un elenco di percentuali di peso in ordine decrescente, eventualmente incompleto, viene visualizzato un elenco completo delle percentuali di peso minimo e massimo che è possibile trovare nella ricetta.

  • È possibile scrivere una funzione o un programma completo.
  • L' input può essere in qualsiasi forma ragionevole (matrice di numeri o elenco di stringhe, ad esempio). I valori frazionari devono essere supportati almeno con una cifra decimale. Una percentuale peso mancante può essere rappresentato in alcun modo coerente e inequivocabile ( 0, '?'o null, per esempio). Puoi presumere che l'input sarà sempre associato a una ricetta valida ( [70]e [∅, ∅, 50]non sono validi, per esempio).
  • L' output può essere in qualsiasi forma ragionevole (un array per entrambe le percentuali di peso minimo e massimo, o un singolo elenco di doppietti, per esempio). Le percentuali minime e massime possono essere in qualsiasi ordine ( [min, max]e [max, min]sono entrambe accettabili). Le percentuali di peso esatte non devono essere elaborate in modo diverso rispetto ad altre percentuali e possono essere rappresentate da uguali valori minimi e massimi.

Si applicano le regole standard per il : mentre digiti il ​​codice, il mio piatto di pasta si raffredda, quindi vince l'invio più breve.

Esempi

Poiché questo problema è più difficile di quanto possa sembrare a prima vista, ecco una risoluzione dettagliata di alcuni casi.

[40, ∅, ∅]

Chiamiamo rispettivamente xe yle due percentuali mancanti.

  • Perché viene dopo il primo ingrediente al 40%, xnon può essere superiore al 40% stesso.
    [40, [?, 40], [?, ?]]
  • La somma delle due percentuali mancanti è sempre del 60%. Di conseguenza:
    • Se xassume il suo valore massimo , quindi yprende il suo valore minimo , che è quindi del 60% - 40% = 20%.
      [40, [?, 40], [20, ?]]
    • Se xassume il suo valore minimo , quindi yprende il suo valore massimo . Ma xnon può essere inferiore a y, quindi in questo caso x= y= 60% / 2 = 30%.
      [40, [30, 40], [20, 30]]

[70, ∅, ∅, 5, ∅]

Chiamiamo, rispettivamente x, ye zle tre percentuali mancanti.

  • Le percentuali minima e massima per zsono necessariamente comprese tra 0% e 5%. Assumiamo z= 0% per un momento. La somma delle due percentuali mancanti è sempre del 25%. Di conseguenza:
    [70, [?, ?], [?, ?], 5, [0, 5]]
    • Se yassume il suo valore minimo , 5%, quindi xprende il suo valore massimo , che è quindi 25% - 5% = 20%.
      [70, [?, 20], [5, ?], 5, [0, 5]]
    • Se yprende il suo valore massimo , quindi xprende il suo valore minimo . Ma xnon può essere inferiore a y, quindi in questo caso x= y= 25% / 2 = 12,5%.
      [70, [12.5, 20], [5, 12.5], 5, [0, 5]]
  • Verifichiamo che tutto va bene se assumiamo ora che z= 5%. La somma delle due percentuali mancanti è sempre del 20%. Di conseguenza:
    • Se yassume il suo valore minimo , 5%, quindi xprende il suo valore massimo , che è quindi 20% - 5% = 15%. Questo caso è già incluso negli intervalli calcolati in precedenza.
    • Se yprende il suo valore massimo , quindi xprende il suo valore minimo . Ma xnon può essere inferiore a y, quindi in questo caso x= y= 20% / 2 = 10%. Questo caso è già incluso nell'intervallo calcolato in precedenza per y, ma non per x.
      [70, [10, 20], [5, 12.5], 5, [0, 5]]

Casi test

Input:  [∅]
Output: [100]

Input:  [70, 30]
Output: [70, 30]

Input:  [70, ∅, ∅]
Output: [70, [15, 30], [0, 15]]

Input:  [40, ∅, ∅]
Output: [40, [30, 40], [20, 30]]

Input:  [∅, ∅, 10]
Output: [[45, 80], [10, 45], 10]

Input:  [70, ∅, ∅, ∅]
Output: [70, [10, 30], [0, 15], [0, 10]]

Input:  [70, ∅, ∅, 5, ∅]
Output: [70, [10, 20], [5, 12.5], 5, [0, 5]]

Input:  [30, ∅, ∅, ∅, 10, ∅, ∅, 5, ∅, ∅]
Output: [30, [10, 25], [10, 17.5], [10, 15], 10, [5, 10], [5, 10], 5, [0, 5], [0, 5]]


3
Vorrei aggiungere una spiegazione dettagliata dell'input-to-output per [40, ∅, ∅]e [70, ∅, ∅, 5, ∅]per rendere le cose un po 'più chiare. Una sfida dovrebbe essere chiara senza guardare i casi di test, che non è il caso in questo momento. Se lo capisco correttamente per [40, ∅, ∅]: ne servono altri 60 per il 100%, divisi su questi due . Il primo deve essere 30 o superiore (altrimenti il ​​secondo sarà sopra di esso, il che non dovrebbe essere possibile quando sono in ordine). Inoltre, non può essere sopra 40, quindi il primo diventa [30,40]e il secondo diventa [(100-40-40=)20, (100-40-30=)30].
Kevin Cruijssen,

Consententemente [min,max]/ [max,min]o misto permesso?
l4m2

@ l4m2 di miscelazione [min,max]ed [max,min]è al limite accettabile, ma dal momento che non può portare a risultati ambigui, direi che va bene.
Blackhole,

Forse mi manca qualcosa, ma perché [70, 12, 11, 5, 2]non funziona per il tuo secondo esempio? Se funziona, il minimo per xsarebbe inferiore a 12.5.
DLosc

Risposte:


11

JavaScript (ES6), 252 byte

Si aspetta 0percentuali mancanti. Restituisce una coppia di valori minimo e massimo per tutte le voci.

a=>(g=a=>(h=(M,I,J=I^1)=>a.some((x,i)=>a.map((y,j)=>s-=j-i?M(j,i)-i?y[I]:M(w=y[I],z=x[J])-z||w==z?w:++k&&z:y[J],s=100,k=1,X=x)&&(I?-s:s)<0)?X[J]=M(X[I],X[J]+s/k):0)(Math.max,0)+h(Math.min,1)?g(a):a)(a.map((n,i)=>[n?p=n:a.find(n=>i--<0&&n)||0,p],p=100))

Provalo online!

Come?

Inizializzazione

Sostituiamo innanzitutto ogni valore nella matrice di input a [] con l'intervallo più ampio possibile.

a.map((n, i) =>       // for each value n at position i in a[]:
  [                   //   generate a [min, max] array:
    n ?               //     if n is not 0:
      p = n           //       use n as the minimum and save it in p
    :                 //     else:
      a.find(n =>     //       find the first value n
        i-- < 0 &&    //         which is beyond the current value
        n             //         and is not equal to 0
      ) || 0,         //       or use 0 as a default value
    p                 //     use p as the maximum
  ],                  //   end of array declaration
  p = 100             //   start with p = 100
)                     // end of map()

Esempi:

[ 0 ] --> [ [ 0, 100 ] ]
[ 30, 0, 5, 0 ] --> [ [ 30, 30 ], [ 5, 30 ], [ 5, 5 ], [ 0, 5 ] ]

Funzione principale

La funzione principale è h () . Cerca la prima voce che sembra essere incoerente quando proviamo a minimizzarla o massimizzarla. Se ne trova uno, lo aggiorna ad un valore che è almeno temporaneamente accettabile, dati gli altri intervalli.

Prende come input M = Math.max / I = 0 o M = Math.min / I = 1 e definisce J come I XOR 1 .

Poiché h () è stato scritto per supportare sia i passaggi di minimizzazione che di ingrandimento, il codice è un po 'complicato da commentare. Ecco perché ci concentreremo solo sul passaggio di massimizzazione, per il quale abbiamo M = Math.max , I = 0 e J = 1 . Con questi parametri, il codice legge come segue:

a.some((x, i) =>              // for each range x at position i in a[] (tested range):
  a.map((y, j) =>             //   for each range y at position j in a[] (reference range):
    s -=                      //     update s:
      j - i ?                 //       if i is not equal to j:
        Math.max(j, i) - i ?  //         if j > i:
          y[0]                //           the reference range is beyond the tested range
                              //           so we just use the minimum value of the y range
        :                     //         else:
          Math.max(           //           take the maximum of:
            w = y[0],         //             w = minimum value of the y range
            z = x[1]          //             z = maximum value of the x range
          ) - z ||            //           if it's not equal to z
          w == z ?            //           or they are equal (i.e. if w <= z):
            w                 //             use w
          :                   //           else:
            ++k && z          //             increment the counter k and use z
      :                       //       else:
        y[1],                 //         use the maximum value of the y range
    s = 100,                  //     start with s = 100
    k = 1,                    //     start with k = 1
    X = x                     //     save the range x in X
  ) &&                        //   end of map()
  (0 ? -s : s) < 0            //   abort if s < 0 (i.e. if we've subtracted more than 100)
) ?                           // end of some(); if truthy:
  X[1] = Math.max(            //   update the maximum value of the faulty range to:
    X[0],                     //     either the minimum value
    X[1] + s / k              //     or the maximum value, less the correction
  )                           //   whichever is greater
:                             // else:
  0                           //   do nothing

ricorsione

La funzione ricorsiva g () continua a chiamare h () fino a quando né il passaggio di minimizzazione o di ingrandimento porta a una nuova correzione, e alla fine restituisce il risultato finale.

g = a => h(Math.max, 0) + h(Math.min, 1) ? g(a) : a

Ben fatto :-) !
Blackhole,

4
@Blackhole Grazie! E a proposito: la mia salsa di pasta legge [38,0,10,0,0,0,0,0,0,0].
Arnauld
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.