Algorytm interfejsu użytkownika pokazujący suwaki procentowe X, których połączone wartości zawsze wynoszą 100%

11

System, który buduję, zawiera zestaw suwaków interfejsu użytkownika (liczba jest różna), każdy w skali 0-100. Przez suwak rozumiem interfejs użytkownika, w którym chwytasz element i przeciągasz go w górę iw dół, jak regulator głośności. Są one połączone algorytmem, który zapewnia, że ​​zawsze ich suma wynosi 100. Tak więc, gdy jeden suwak jest przesuwany w górę, pozostałe przesuwają się w dół, ostatecznie do zera. Kiedy jeden jest przesunięty w dół, pozostałe poruszają się w górę. Przez cały czas suma musi wynosić 100. Więc tutaj suwaki mają różne wartości, ale łącznie 100%:

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

Jeśli pierwszy suwak następnie przesunie się W GÓRĘ z 40 do 70, pozostałe muszą przesunąć się W DÓŁ o wartość (w miarę przeciągania suwaka). Zauważ, że trzy suwaki zmieniły się z 20 na 10, a jeden pozostał na zero, ponieważ nie może spaść niżej.

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

Oczywiście, gdy jakikolwiek suwak osiągnie 0 lub 100, nie może się ruszyć dalej, w tym momencie moja głowa naprawdę zaczyna boleć. Więc jeśli suwak przesuwa się wyżej, pozostałe poruszają się niżej, ale gdy którykolwiek z nich osiągnie zero, tylko pozostałe, które jeszcze nie osiągnęły zera, mogą przesunąć się niżej.

Pytam o to tutaj, ponieważ to pytanie jest specyficzne dla algorytmu, a nie implementacji. FWIW platformą jest Android Java, ale nie jest to szczególnie istotne.

Podejście, które podjąłem przy pierwszym dźgnięciu, polegało na obliczeniu procentowej zmiany przesuniętego suwaka. Następnie podzieliłem tę zmianę i zastosowałem ją (w innym kierunku) do innych wartości suwaka. Problem polega jednak na tym, że przy użyciu wartości procentowych i mnożenia, jeśli jakikolwiek suwak dojdzie do zera, nie można go nigdy zwiększyć od zera - w rezultacie pojedyncze suwaki utkną na zero. Użyłem suwaków z zakresu od 0 do 1 000 000, aby uniknąć problemów z zaokrąglaniem i wydaje się to pomocne, ale jeszcze nie stworzyłem algorytmu, który dobrze radzi sobie ze wszystkimi scenariuszami.

Ollie C.
źródło
2
Podobne pytania zadano na stronie UX, chociaż na IMHO nie udzielono odpowiedzi w szczególnie zadowalający sposób. Jestem ciekawy, czy są jakieś dobre rozwiązania. Chociaż szczerze mówiąc, uważam, że połączenie suwaków powoduje więcej problemów niż jest to warte nawet dla użytkownika - trudno jest skończyć z rozkładem, który chcesz, ponieważ ciągle zmieniasz wprowadzone wartości udać się.
Daniel B
1
Jedną z sugestii byłoby umożliwienie użytkownikowi przełączania się między dwoma trybami, zasadniczo jednym, w którym suwaki są ze sobą połączone, i drugim, w którym nie są (i przełączenie z powrotem do „trybu względnego” ponownie znormalizuje rozkład). Pozwoliłoby to na boczną interwencję w niektórych z wymienionych przez ciebie problemów, ale zwiększyło tak złożoność interfejsu użytkownika, że ​​łatwiej byłoby skłonić użytkownika do wpisania wartości.
Daniel B
Masz zdecydowanie rację, to wymaga dużo manipulacji ze strony użytkownika, ale w tym przypadku jest to forma aplikacji ankietowej, w której suwaki reprezentują problemy, więc pytanie dotyczy WSZYSTKICH względnych wag wartości.
Ollie C
1
Rozumiem ... moim problemem jest to, że wyrażenie czegoś w rodzaju podziału 60/30/10 będzie wymagało więcej niż 3 akcji, ponieważ podczas manipulowania częścią 30/10, 60 wciąż się porusza. Z większymi listami staje się coraz gorzej i naprawdę nadaje się tylko do bardzo niejasnych danych wejściowych, ponieważ nigdy nie osiągniesz tego, co masz w głowie.
Daniel B
2
Z punktu widzenia ciebie, sugeruję, aby całkowity wynik był wyświetlany jako duża liczba obok suwaków. Gdy przesuwasz jeden suwak w górę, łączny wynik wzrasta powyżej „100” i cokolwiek twój przycisk „następny” zostaje wyłączony, dopóki użytkownik nie przesunie innego suwaka w dół, aby zmniejszyć łączny wynik. Nigdy nie dowiesz się, który z pozostałych 4 suwaków użytkownik chce zmniejszyć, przesuwając jeden suwak w górę, więc nawet nie próbuj.
Graham,

Odpowiedzi:

10

Chciwy Algorytm

Gdy suwak przesuwa się w górę (w dół), wszystkie pozostałe muszą przejść w dół (w górę). Każdy ma trochę miejsca, które może przesunąć (dla pozycji w dół - ich pozycja, dla pozycji w górę: 100 pozycji).

Kiedy jeden suwak się porusza, weź pozostałe, posortuj je według miejsca, w którym mogą się poruszać, i po prostu iteruj przez nie.

Na każdej iteracji przesuń suwak w żądanym kierunku o (łącznie, aby przesunąć w lewo / suwaki w lewo w kolejce) lub o odległość, którą może przemieścić, w zależności od tego, która wartość jest mniejsza.

Ma to złożoność liniową (ponieważ można posortować tę samą uporządkowaną kolejkę raz po raz, po jej posortowaniu).

W tym scenariuszu żadne suwaki nie utkną, wszystkie starają się poruszać tak dużo, jak to możliwe, ale tylko do ich sprawiedliwego udziału.

Ważony ruch

Innym podejściem byłoby wyważenie potrzebnego ruchu. Myślę, że to właśnie próbowałeś zrobić, sądząc po swoim oświadczeniu „suwaki utknęły na 0”. IMHO to jest bardziej naturalne, po prostu trzeba zrobić więcej ulepszeń.

Spekulując ponownie, powiedziałbym, że próbujesz wyważyć ruchy różnych suwaków według ich pozycji (bezpośrednio przekłada się to na twój problem z zablokowaniem na 0). Pamiętaj jednak, że pozycję suwaka można wyświetlić z różnych kierunków - od początku lub od końca. Jeśli ważysz według pozycji od początku podczas zmniejszania i pozycji od końca podczas zwiększania, powinieneś unikać swojego problemu.

Pod względem terminologicznym jest to bardzo podobne do poprzedniej części - nie obciążaj ruchu, jaki należy wykonać, pozycją suwaków, obciążaj go przestrzenią, którą pozostawili, aby poruszać się w tym kierunku.

Ordous
źródło
1
Spojrzałem głębiej i okazuje się, że „zablokowane” nie utknęły, ale po prostu mają tak niskie wartości, że zmiana procentowa prawie nie ma wpływu - wielkość ruchu suwaka zależy od jego wartości. Podejrzewam, że twoja sugestia, by użyć przeciwnej wartości, może działać lepiej, po prostu muszę utrzymać wartość całkowitą na 100. Dziękujemy za wstrzyknięcie trochę jasności.
Ollie C
1

Podejście, które podjąłem jest nieco inne i wymaga użycia innej wewnętrznej reprezentacji każdego suwaka.

  1. każdy suwak może przyjmować dowolną wartość od 0..100 (X), która jest używana jako współczynnik ważenia dla tego suwaka (nie%)

  2. dodaj wszystkie wartości suwaka, aby uzyskać całkowitą liczbę (T)

  3. aby określić wyświetlaną wartość każdego suwaka, użyj ZAOKR (X * 100 / T)

  4. kiedy suwak jest przesuwany w górę lub w dół, zmieniasz tylko wartość jednego suwaka ; waga tego suwaka zwiększy się lub zmniejszy w stosunku do wszystkich innych suwaków, a powyższe obliczenia zapewnią, że zmiana na wszystkich innych suwakach zostanie rozłożona możliwie równomiernie.

Jeffrey Kemp
źródło
Byłby to dobry interfejs, jeśli użytkownik jest zainteresowany głównie tworzeniem mniej lub bardziej precyzyjnych ułamków. Jednak nie widzę, jak to funkcjonalnie różni się od tego, że inne slajdy przesuwają się proporcjonalnie do ich położenia.
Ordous
1

Myślę, że nadmiernie komplikujesz rzeczy, próbując dostosować bieżącą wartość o procent zmiany suwaka „przeniesionego”, który daje procentowe wartości procentowe, co wprowadza błędy zaokrąglania.

Ponieważ wiesz, że masz do czynienia tylko z całkowitą wartością 100, zachowałbym liczby całkowite i cofałem się od 100, unikając poważnych problemów z zaokrąglaniem. (W poniższym przykładzie obsłużę zaokrąglanie jako całe liczby całkowite na końcu)

Moją techniką byłoby ustawienie suwaków jako prostych 0-100. Odejmij „nową” wartość od 100, aby obliczyć, ile należy redystrybuować, rozłóż ją między innymi suwakami zgodnie z ich wagą, a następnie posprzątaj)

O ile mi wiadomo, nie jest to prawidłowy kod systemu Android: str

// 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 

To powinno wewnętrznie obsłużyć dowolną wartość 0 lub 100

Jon Story
źródło
0

Co z podejściem Round Robin? Utwórz transakcję, która zapewni, że dodanie wartości jednemu suwakowi zmniejszy się w porównaniu do jego elementu równorzędnego. i wzajemnie.

Następnie za każdym razem, gdy zmiana suwaka uruchamia transakcję za pomocą innego suwaka równorzędnego (utworzę iterator, który zwróci suwak równorzędny po kolei). Jeśli suwak równorzędny ma wartość zero, przejdź do następnego.

michaelbn
źródło
0

Aby rozwinąć świetną odpowiedź na Chciwy Algorytm od @Ordous. Oto podział poszczególnych kroków.

  1. Przesuń suwak
  2. Zapisz różnicę Kwota została przeniesiona jako newValue - oldValue (Wartość dodatnia oznacza, że ​​przesunęła się w górę, a inne suwaki muszą przesunąć się w dół i odwrotnie)
  3. Sortuj gdy inni na liście od najmniej do najbardziej od odległości mogą poruszać się w kierunku wymaganym przez differenceAmount być dodatnia lub ujemna.
  4. Ustaw amountToMove = differenceAmount / pozostały OthersCount
  5. Pętli i przesunąć suwak przez każdą obu kwoty to może przenieść lub amountToMove . Cokolwiek jest mniejsze
  6. Odejmij kwotę, którą suwak przesunął z kwotyToMove, i podziel przez nowe pozostałe Pozostałe Konto
  7. Po zakończeniu pomyślnie rozdzieliłeś różnicę między pozostałymi suwakami.
Sean
źródło
Fantastyczna odpowiedź tutaj. stackoverflow.com/a/44369773/4222929
Sean
-3

Jedną z prostych technik jest obliczenie wartości procentowych wartości suwaka względem sumy wartości suwaka, a następnie ponowne przypisanie wartości suwaka do odpowiednich obliczonych wartości procentowych. w ten sposób wartości suwaka zostaną ponownie dostosowane np

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

Wprawdzie wprowadza błąd zaokrąglenia, ale można sobie z tym poradzić na wypadek, gdyby wartości suwaków były dokładnie i zawsze sumowane do 100.

Ustawiłem skrzypce, aby to zademonstrować za pomocą angularjs. Odwiedź demo

Ahmad Noor
źródło
2
... co jest odpowiedzią Jeffreya. Przeczytaj najpierw pozostałe odpowiedzi, aby uniknąć duplikatów.
Jan Doggen,