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


11

Un sistema che sto costruendo include un set di slider dell'interfaccia utente (il numero varia) ciascuno con una scala da 0 a 100. Per cursore 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 assicura che siano sempre 100. Quindi quando un cursore viene spostato verso l'alto, tutti gli altri si spostano verso il basso, eventualmente 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 totalizzano il 100%:

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

Se il primo dispositivo di scorrimento si sposta quindi SU da 40 a 70, gli altri devono spostarsi di valore GIÙ (mentre il dispositivo di scorrimento viene trascinato). Nota tre cursori cambiati 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 un cursore raggiunge 0 o 100, non può spostarsi oltre, ed è qui che la mia testa inizia davvero a farmi male. Quindi se un cursore si sposta in alto, gli altri si spostano in basso, ma quando uno di essi raggiunge lo zero solo quelli rimanenti che non hanno ancora raggiunto lo zero possono spostarsi in basso.

Sto chiedendo questo 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 il mio primo colpo è stato quello di calcolare la variazione percentuale del cursore che si muoveva. Ho quindi diviso quel cambiamento e l'ho applicato (nell'altra direzione) agli altri valori del cursore. Il problema però è che usando le percentuali e moltiplicando, se uno slider arriva a zero, non può mai essere aumentato di nuovo da zero - il risultato netto è che i singoli slider rimangono bloccati a zero. Ho usato cursori 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.


2
Domande simili sono state poste sul sito UX, anche se IMHO non hanno ricevuto risposta in modo particolarmente soddisfacente. Sono curioso di vedere se ci sono buone soluzioni. Anche se ad essere sincero, trovo che avere i cursori collegati tra loro causi più problemi di quanti ne valga la pena anche per l'utente - diventa difficile finire con la distribuzione che desideri, poiché continui a modificare i valori già inseriti mentre partire.
Daniel B,

1
Un suggerimento sarebbe quello di consentire all'utente di passare tra due modalità, essenzialmente una in cui i dispositivi di scorrimento sono collegati tra loro e una in cui non lo sono (e il passaggio alla "modalità relativa" avrebbe normalizzato nuovamente la distribuzione). Ciò consentirebbe di evitare alcuni dei problemi menzionati, ma aggiungerebbe tanta complessità all'interfaccia utente che potrebbe essere più semplice indurre l'utente a digitare i valori.
Daniel B

Hai sicuramente ragione, richiede un sacco di armeggi da parte dell'utente, ma in questo caso è una forma di app di sondaggio in cui i cursori rappresentano problemi, quindi la domanda è TUTTA sui pesi relativi dei valori.
Ollie C

1
Capisco ... il mio problema è proprio che esprimere qualcosa come una divisione 60/30/10 richiederà più di 3 azioni, perché quando si armeggia con la parte 30/10, il 60 continua a muoversi. Peggiora progressivamente con elenchi più grandi ed è davvero adatto solo per input estremamente vaghi, dal momento che non lo otterrai mai con il numero che hai in testa.
Daniel B,

2
Da un punto UX di te, suggerirei di presentare il punteggio totale come un grande numero a lato dei cursori. Quando si fa scorrere un cursore verso l'alto, il punteggio totale aumenta oltre "100" e qualunque sia il pulsante "successivo" viene disabilitato fino a quando l'utente non fa scorrere un altro cursore verso il basso per ridurre il punteggio totale. Non saprai mai quale degli altri 4 cursori l'utente vuole ridurre quando fa scorrere un cursore verso l'alto, quindi non provare nemmeno.
Graham,

Risposte:


10

Algoritmo goloso

Quando un cursore si sposta verso l'alto (verso il basso), tutti gli altri devono spostarsi verso il basso (verso l'alto). Ognuno ha un po 'di spazio che può spostare (per il basso: la sua posizione, per il massimo: 100 posizioni).

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

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

La complessità è lineare (poiché è possibile utilizzare più volte la stessa coda ordinata, una volta che è stata ordinata).

In questo scenario, nessun dispositivo di scorrimento si blocca, tutti provano a muoversi il più possibile, ma solo fino alla loro giusta quota.

Mossa ponderata

Un approccio diverso sarebbe quello di ponderare la mossa che devono fare. Penso che sia quello che hai cercato di fare, a giudicare dai tuoi "cursori restano bloccati a 0". IMHO questo è più naturale, devi solo fare più modifiche.

Speculando di nuovo, direi che provi a ponderare le mosse di diversi cursori in base alla loro posizione (Questo si tradurrebbe direttamente nel tuo problema bloccato a 0). Tuttavia, è possibile visualizzare la posizione del dispositivo di scorrimento da diverse direzioni, dall'inizio o dalla fine. Se pesi in base alla posizione dall'inizio quando diminuisci e alla fine quando aumenti, dovresti evitare il tuo problema.

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


1
Ho guardato più in profondità e risulta che quelli "bloccati" non sono bloccati, ma semplicemente hanno valori così bassi che la variazione percentuale non ha quasi alcun effetto - la quantità che un cursore si sposta è relativa al suo valore. Ho il sospetto che il tuo suggerimento di utilizzare il valore opposto possa funzionare piuttosto meglio, ho solo bisogno di mantenere il valore totale a 100. Grazie per averci iniettato un po 'di chiarezza.
Ollie C

1

L'approccio che prenderei è leggermente diverso e prevede l'utilizzo di una diversa rappresentazione interna di ciascun dispositivo di scorrimento.

  1. ciascun dispositivo di scorrimento può assumere qualsiasi valore compreso tra 0 e 100 (X) utilizzato come fattore di ponderazione per tale dispositivo di scorrimento (non una%)

  2. sommare tutti i valori del dispositivo di scorrimento per ottenere la cifra totale (T)

  3. per determinare il valore visualizzato di ciascun dispositivo di scorrimento, utilizzare ROUND (X * 100 / T)

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


Questa sarebbe una buona interfaccia se l'utente è principalmente preoccupato di fare frazioni più o meno precise. Tuttavia, non riesco a vedere come questo sia funzionalmente diverso dal far muovere le altre diapositive in modo proporzionale alla loro posizione.
Ordous,

1

Penso che tu stia complicando troppo le cose cercando di regolare il valore corrente di una percentuale della variazione del cursore "spostato", che ti dà percentuali di percentuali, introducendo errori di arrotondamento.

Dato che sai che hai sempre a che fare con un valore totale di 100, terrei le cose come numeri interi e lavorerei all'indietro rispetto ai 100, evitando qualsiasi seria confusione con gli arrotondamenti. (Nell'esempio seguente gestisco gli arrotondamenti come numeri interi interi alla fine)

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

Questo, per quanto ne so, non è un 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 gestire intrinsecamente qualsiasi valore 0 o 100


0

Che dire dell'approccio Round Robin? Crea una transazione che assicuri che l'aggiunta di valore a un dispositivo di scorrimento si riduca dal suo peer. e viceversa.

Quindi ogni volta che una modifica del dispositivo di scorrimento esegue la transazione con un dispositivo di scorrimento peer diverso (creerei un iteratore che restituisca il dispositivo di scorrimento peer a turno). Se il dispositivo di scorrimento peer è zero, passare a quello successivo.


0

Giusto per approfondire la risposta dell'algoritmo goloso di @Ordous. Ecco una ripartizione dei passaggi.

  1. Sposta cursore
  2. Salva la differenza : montala come spostata come newValue - oldValue (il valore positivo indica che si è spostato verso l'alto e che gli altri cursori devono spostarsi verso il basso e viceversa)
  3. Ordinare gli altri nell'elenco dalla minima alla maggior parte della distanza che POSSONO spostare nella direzione richiesta dalla differenza, essendo positivo o negativo.
  4. Imposta quantitàToMove = differenzaAmount / rimanenti OthersCount
  5. Loop attraverso e spostare ogni slider sia la quantità che può spostare o amountToMove . Qualunque sia il più piccolo
  6. Sottrarre la quantità spostata dal dispositivo di scorrimento da amountToMove e dividere per il nuovo conto rimanente rimanente
  7. Una volta terminato, hai distribuito correttamente la differenza Montare tra gli altri cursori.

Risposta fantastica qui. stackoverflow.com/a/44369773/4222929
Sean

-3

Una tecnica semplice consiste nel calcolare le percentuali dei valori del dispositivo di scorrimento in relazione alla somma dei valori del dispositivo di scorrimento e quindi riassegnare i valori del dispositivo di scorrimento alle rispettive percentuali calcolate. in questo modo i valori del dispositivo di scorrimento verranno regolati, ad es

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

Anche se introduce un errore di arrotondamento ma può essere gestito nel caso in cui abbiamo bisogno che i valori dei cursori siano esattamente e sempre sommati a 100.

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


2
... che è la risposta di Jeffrey. Leggere prima le altre risposte per evitare duplicati.
Jan Doggen,
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.