Algoritmo per un'interfaccia utente che mostra cursori di percentuale X i cui valori collegati sono sempre pari al 100%

10

Un sistema che sto costruendo include un set di cursori dell'interfaccia utente (il numero varia) ciascuno con una scala da 0 a 100. Per dispositivo di scorrimento intendo un'interfaccia utente in cui prendi un elemento e trascinalo su e giù, come un controllo del volume. Sono collegati da un algoritmo che garantisce sempre un totale di 100. Quindi, quando uno slider viene spostato verso l'alto, gli altri si spostano tutti verso il basso, fino a zero. Quando uno viene spostato verso il basso, gli altri si spostano verso l'alto. In ogni momento il totale deve essere 100. Quindi qui i cursori hanno vari valori, ma sono pari al 100%:

----O------ 40
O----------  0
--O-------- 20
--O-------- 20
--O-------- 20

Se il primo cursore si sposta su SU da 40 a 70, gli altri devono spostare GIÙ in valore (mentre il cursore viene trascinato). Nota che tre cursori sono passati da 20 a 10 e uno è rimasto a zero in quanto non può scendere.

-------O--- 70
O----------  0
-O--------- 10
-O--------- 10
-O--------- 10

Ovviamente quando uno slider raggiunge 0 o 100, non può spostarsi ulteriormente, che è dove la mia testa inizia davvero a ferire. Quindi se un cursore si muove più in alto, gli altri si spostano più in basso, ma quando uno di essi raggiunge lo zero, solo i restanti che non hanno ancora raggiunto lo zero possono spostarsi più in basso.

Lo chiedo qui in quanto questa domanda è specifica dell'algoritmo e non dell'implementazione. FWIW la piattaforma è Android Java, ma non è particolarmente rilevante.

L'approccio che ho seguito con la mia prima pugnalata è stato quello di calcolare la variazione percentuale del cursore che si muoveva. Ho quindi diviso quella modifica e l'ho applicata (nell'altra direzione) agli altri valori del cursore. Il problema però è che usando percentuali e moltiplicando, se un qualsiasi cursore arriva a zero, non può mai essere aumentato di nuovo da zero - il risultato netto di ciò è che i singoli cursori rimangono bloccati a zero. Ho usato gli slider con un intervallo compreso tra 0 e 1.000.000 per evitare problemi di arrotondamento e questo sembra essere utile, ma devo ancora creare un algoritmo che gestisca bene tutti gli scenari.

    
posta Ollie C 27.10.2014 - 12:29
fonte

6 risposte

9

Algoritmo goloso

Quando un cursore si sposta verso l'alto (in basso), tutti gli altri devono spostarsi verso il basso (su). Ognuno ha uno spazio che può spostare (per il basso - la loro posizione, per sopra: 100-posizione).

Quindi quando si muove un cursore, prendi gli altri cursori, ordinali per lo spazio che possono spostare e semplicemente itera attraverso di essi.

Ad ogni iterazione, sposta il dispositivo di scorrimento nella direzione desiderata per (totale per spostare a sinistra / i cursori lasciati in coda) o la distanza che può spostare, a seconda di quale sia più piccolo.

Questo è lineare nella complessità (dal momento che è possibile utilizzare la stessa coda ordinata più e più volte, una volta che è stata ordinata).

In questo scenario, nessun cursore si blocca, tutti cercano di muoversi il più possibile, ma solo fino alla loro giusta quota.

Spostamento ponderato

Un approccio diverso sarebbe quello di ponderare la mossa che devono fare. Io penso questo è quello che hai cercato di fare, a giudicare dalla tua frase "i cursori rimangono bloccati a 0". IMHO questo è più naturale, hai solo bisogno di fare più ritocchi.

Speculando di nuovo, direi che provi a pesare le mosse di diversi cursori in base alla loro posizione (questo si tradurrebbe direttamente nel tuo problema bloccato a 0). Tuttavia, tieni presente che puoi visualizzare la posizione del cursore da direzioni diverse, dall'inizio o dalla fine. Se pesi per posizione dall'inizio alla diminuzione e posizioni dalla fine quando aumenti, dovresti evitare il tuo problema.

Questo è abbastanza simile nella terminologia alla parte precedente - non ponderare la mossa da fare per la posizione dei cursori, pesarla lo spazio che hanno lasciato per muoversi in quella direzione.

    
risposta data 27.10.2014 - 12:46
fonte
1

L'approccio che prenderei è leggermente diverso e comporta l'uso di una diversa rappresentazione interna di ciascun cursore.

  1. ogni cursore può assumere qualsiasi valore da 0..100 (X) che viene usato come fattore di ponderazione per quel cursore (non a%)

  2. somma tutti i valori del cursore per ottenere la cifra totale (T)

  3. per determinare il valore visualizzato di ciascun cursore, usa ROUND (X * 100 / T)

  4. quando un cursore viene spostato verso l'alto o verso il basso, cambi solo il valore del cursore 1 ; la ponderazione per quel cursore aumenterà o diminuirà rispetto a tutti gli altri cursori, e il calcolo sopra assicurerà che la modifica di tutti gli altri cursori sia distribuita nel modo più uniforme possibile.

risposta data 27.10.2014 - 14:31
fonte
1

Penso che tu stia complicando eccessivamente le cose provando ad aggiustare il valore corrente di una percentuale della variazione del cursore "spostato", che ti dà percentuali di percentuali, che sta introducendo errori di arrotondamento.

Dal momento che sai che hai sempre a che fare con un valore totale di 100, manterrei le cose come numeri interi e lavorerò all'indietro rispetto al 100, evitando qualsiasi problema serio con gli arrotondamenti. (Nell'esempio seguente gestisco ogni arrotondamento come interi interi alla fine)

La mia tecnica sarebbe quella di impostare i cursori come semplici 0-100. Sottrarre il valore "nuovo" da 100 per calcolare quanto ridistribuire, distribuirlo tra gli altri cursori in base al loro peso, quindi pulire)

Questo non è, per quanto ne so, codice Android valido: p

// see how much is "left" to redistribute
amountToRedistribute = 100 - movedSlider.value

//What proportion of the original 100% was contained in the other sliders (for weighting)
allOtherSlidersTotalWeight = sum(other slider existing values)

//Approximately redistribute the amount we have left between the other sliders, adjusting for their existing weight
for each (otherSlider)
    otherSlider.value = floor(otherSlider.value/allOtherSlidersWeight * amountToRedistribute)
    amountRedistributed += otherSlider.value

//then clean up because the floor() above might leave one or two leftover... How much still hasn't been redistributed?
amountLeftToRedistribute -= amountRedistributed

//add it to a slider (you may want to be smarter and add in a check if the slider chosen is zero, or add it to the largest remaining slider, spread it over several sliders etc, I'll leave it fairly simple here)
otherSlider1.value += amountLeftToRedistribute 

Questo dovrebbe intrinsicamente gestire qualsiasi valore 0 o 100

    
risposta data 27.10.2014 - 14:35
fonte
0

Che ne pensi di Round Robin? Crea una transazione che assicuri che l'aggiunta di valore a un cursore si riduca dal suo peer. e viceversa.

Quindi ogni volta che una modifica del cursore esegue la transazione con un cursore peer diverso (creerei un iteratore che restituisce il cursore peer a turno). Se il cursore peer è zero, passa a quello successivo.

    
risposta data 27.10.2014 - 13:29
fonte
0

Solo per approfondire la grande risposta di Greedy Algorithm di @Ordous. Ecco una ripartizione dei passaggi.

  1. Muovi il cursore
  2. Salva differenceAmount spostato come newValue - oldValue (valore positivo significa che è stato spostato verso l'alto e altri cursori devono spostarsi verso il basso e viceversa )
  3. Ordina gli altri nell'elenco da almeno alla maggior parte di distanza che possono spostare nella direzione richiesta da differenceAmount positivo o negativo.
  4. Imposta amountToMove = differenceAmount / rimanente OthersCount
  5. Passa in rassegna e sposta ciascun cursore di o la quantità che può spostare o amountToMove . Qualunque sia il più piccolo
  6. Sottrai l'importo che il cursore ha spostato da amountToMove e divide per nuovo rimanente OthersCount
  7. Una volta terminato, hai distribuito con successo differenceAmount tra gli altri slider.
risposta data 29.05.2017 - 13:16
fonte
-3

Una tecnica semplice consiste nel calcolare le percentuali dei valori del cursore relativamente alla somma dei valori del cursore e quindi riassegnare i valori del cursore alle rispettive percentuali calcolate. in questo modo i valori dei cursori saranno riadattati e.g

slider1.value = (slider1.value / sumOfSliderValues) * 100 ;
slider2.value = (slider2.value / sumOfSliderValues) * 100 ;
slider3.value = (slider3.value / sumOfSliderValues) * 100 ;

Sebbene introduca un errore di arrotondamento, ma può essere gestito nel caso in cui avessimo bisogno dei valori dei cursori esattamente e sempre di sommare fino a 100.

Ho impostato un violino per dimostrarlo usando angularjs. Si prega di visitare demo

    
risposta data 17.03.2015 - 12:35
fonte

Leggi altre domande sui tag