Algorithmus für eine Benutzeroberfläche mit X-Prozent-Schiebereglern, deren verknüpfte Werte immer 100% betragen


11

Ein System, das ich baue, enthält eine Reihe von UI-Schiebereglern (die Anzahl variiert) mit einer Skala von 0 bis 100. Mit Schieberegler meine ich eine Benutzeroberfläche, in der Sie ein Element greifen und es wie einen Lautstärkeregler nach oben und unten ziehen. Sie sind durch einen Algorithmus verbunden, der sicherstellt, dass sie immer 100 betragen. Wenn also ein Schieberegler nach oben bewegt wird, bewegen sich alle anderen nach unten und schließlich auf Null. Wenn einer nach unten bewegt wird, bewegen sich die anderen nach oben. Zu jeder Zeit muss die Summe 100 sein. Hier haben Schieberegler also verschiedene Werte, aber sie summieren sich zu 100%:

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

Wenn sich der erste Schieberegler dann von 40 auf 70 nach OBEN bewegt, müssen sich die anderen im Wert nach UNTEN bewegen (wenn der Schieberegler gezogen wird). Beachten Sie, dass drei Schieberegler von 20 auf 10 geändert wurden und einer bei Null blieb, da er nicht tiefer gehen kann.

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

Wenn ein Schieberegler 0 oder 100 erreicht, kann er sich natürlich nicht weiter bewegen. Hier beginnt mein Kopf wirklich zu schmerzen. Wenn sich ein Schieberegler nach oben bewegt, bewegen sich die anderen nach unten, aber wenn einer von ihnen Null erreicht, können sich nur die verbleibenden, die noch nicht Null erreicht haben, nach unten bewegen.

Ich stelle dies hier, da diese Frage spezifisch für den Algorithmus und nicht für die Implementierung ist. FWIW die Plattform ist Android Java, aber das ist nicht besonders relevant.

Der Ansatz, den ich bei meinem ersten Stich gewählt habe, bestand darin, die prozentuale Änderung des sich bewegenden Schiebereglers zu berechnen. Ich habe diese Änderung dann aufgeteilt und (in die andere Richtung) auf die anderen Schiebereglerwerte angewendet. Das Problem ist jedoch, dass durch Verwendung von Prozentsätzen und Multiplikation, wenn ein Schieberegler auf Null kommt, dieser nie wieder von Null erhöht werden kann - das Nettoergebnis davon ist, dass einzelne Schieberegler bei Null stecken bleiben. Ich habe Schieberegler mit einem Bereich von 0 bis 1.000.000 verwendet, um Rundungsprobleme zu vermeiden, und das scheint hilfreich zu sein, aber ich habe noch keinen Algorithmus erstellt, der alle Szenarien gut handhabt.


2
Ähnliche Fragen wurden auf der UX-Site gestellt, obwohl sie meiner Meinung nach nicht besonders zufriedenstellend beantwortet wurden. Ich bin gespannt, ob es gute Lösungen gibt. Um ehrlich zu sein, stelle ich fest, dass das Verknüpfen der Schieberegler mehr Probleme verursacht, als es selbst für den Benutzer wert ist. Es wird schwierig, die gewünschte Verteilung zu erhalten, da Sie Ihre bereits eingegebenen Werte ständig ändern gehen.
Daniel B

1
Ein Vorschlag wäre, dem Benutzer zu ermöglichen, zwischen zwei Modi zu wechseln, im Wesentlichen einem, in dem die Schieberegler miteinander verbunden sind, und einem, in dem sie nicht miteinander verbunden sind (und ein Zurückschalten in den "relativen Modus" würde die Verteilung neu normalisieren). Dies würde es ermöglichen, einige der von Ihnen erwähnten Probleme zu umgehen, aber die Benutzeroberfläche so komplex zu gestalten, dass es möglicherweise einfacher ist, den Benutzer dazu zu bringen, die Werte
Daniel B

Sie haben definitiv Recht, es erfordert viel Fummeln seitens des Benutzers, aber in diesem Fall handelt es sich um eine Form der Umfrage-App, bei der die Schieberegler Probleme darstellen. Die Frage bezieht sich also auf die relativen Gewichte der Werte.
Ollie C

1
Ich verstehe ... mein Problem ist nur, dass das Ausdrücken einer 60/30/10-Aufteilung mehr als 3 Aktionen erfordert , da sich die 60 beim Spielen mit dem 30/10-Teil weiter bewegt. Bei größeren Listen wird es immer schlimmer und eignet sich nur für äußerst vage Eingaben, da Sie es nie auf die Zahl bringen, die Sie in Ihrem Kopf haben.
Daniel B

2
Aus UX-Sicht würde ich vorschlagen, dass die Gesamtpunktzahl als große Zahl neben den Schiebereglern angezeigt wird. Wenn Sie einen Schieberegler nach oben schieben, erhöht sich die Gesamtpunktzahl auf "100", und unabhängig davon, welche Schaltfläche "Weiter" aktiviert ist, wird der Benutzer deaktiviert, bis der Benutzer einen anderen Schieberegler nach unten schiebt, um die Gesamtpunktzahl zu verringern. Sie werden nie wissen, welchen der anderen 4 Schieberegler der Benutzer reduzieren möchte, wenn er einen Schieberegler nach oben schiebt. Versuchen Sie es also nicht einmal.
Graham

Antworten:


10

Gieriger Algorithmus

Wenn sich ein Schieberegler nach oben (unten) bewegt, müssen sich alle anderen nach unten (oben) bewegen. Jeder hat etwas Platz, den er bewegen kann (für unten - ihre Position, für oben: 100-Position).

Wenn sich also ein Schieberegler bewegt, nehmen Sie die anderen Schieberegler, sortieren Sie sie nach dem Raum, den sie bewegen können, und durchlaufen Sie sie einfach.

Bewegen Sie den Schieberegler bei jeder Iteration in die gewünschte Richtung um (insgesamt nach links / Schieberegler nach links in der Warteschlange) oder um die Entfernung, um die er sich bewegen kann, je nachdem, welcher Wert kleiner ist.

Dies ist in seiner Komplexität linear (da Sie dieselbe geordnete Warteschlange nach dem Sortieren immer wieder verwenden können).

In diesem Szenario bleiben keine Schieberegler hängen, alle versuchen, sich so weit wie möglich zu bewegen, aber nur bis zu ihrem gerechten Anteil.

Gewichteter Zug

Ein anderer Ansatz wäre, die Bewegung zu gewichten, die sie tun müssen. Ich denke, das haben Sie versucht, gemessen an Ihrer Aussage "Schieberegler bleiben bei 0 hängen". IMHO ist dies natürlicher, Sie müssen nur mehr Optimierungen vornehmen.

Wenn Sie noch einmal spekulieren, würde ich sagen, dass Sie versuchen, die Bewegungen verschiedener Schieberegler nach ihrer Position zu gewichten (dies würde sich direkt auf Ihr Problem mit 0 auswirken). Beachten Sie jedoch, dass Sie die Position des Schiebereglers aus verschiedenen Richtungen anzeigen können - vom Anfang oder vom Ende. Wenn Sie beim Abnehmen von Anfang an nach Position und beim Erhöhen von Ende nach Gewicht wiegen, sollten Sie Ihr Problem vermeiden.

Dies ist in der Terminologie dem vorherigen Teil ziemlich ähnlich - gewichten Sie die Bewegung nicht nach der Position der Schieberegler, sondern nach dem Raum, den sie noch haben , um sich in diese Richtung zu bewegen.


1
Ich habe tiefer geschaut und es stellte sich heraus, dass die "stecken gebliebenen" nicht stecken bleiben, sondern einfach so niedrige Werte haben, dass die prozentuale Änderung fast keine Auswirkung hat - der Betrag, um den sich ein Schieberegler bewegt, ist relativ zu seinem Wert. Ich vermute, dass Ihr Vorschlag, den entgegengesetzten Wert zu verwenden, besser funktioniert. Ich muss nur den Gesamtwert bei 100 halten. Vielen Dank, dass Sie etwas Klarheit geschaffen haben.
Ollie C

1

Der Ansatz, den ich wählen würde, ist etwas anders und beinhaltet die Verwendung einer anderen internen Darstellung jedes Schiebereglers.

  1. Jeder Schieberegler kann einen beliebigen Wert von 0..100 (X) annehmen, der als Gewichtungsfaktor für diesen Schieberegler verwendet wird (kein%).

  2. Addiere alle Schiebereglerwerte, um die Gesamtzahl (T) zu erhalten.

  3. Verwenden Sie RUND (X * 100 / T), um den angezeigten Wert jedes Schiebereglers zu bestimmen.

  4. Wenn ein Schieberegler nach oben oder unten bewegt wird, ändern Sie nur den Wert des einen Schiebereglers . Die Gewichtung für diesen Schieberegler wird im Vergleich zu allen anderen Schiebereglern erhöht oder verringert, und die obige Berechnung stellt sicher, dass die Änderung auf alle anderen Schieberegler so gleichmäßig wie möglich verteilt wird.


Dies wäre eine gute Schnittstelle, wenn der Benutzer hauptsächlich daran interessiert ist, mehr oder weniger genaue Brüche zu erstellen. Ich kann jedoch nicht erkennen, wie sich dies funktional davon unterscheidet, dass sich die anderen Folien proportional zu ihrer Position bewegen.
Ordous

1

Ich denke, Sie machen die Dinge zu kompliziert, indem Sie versuchen, den aktuellen Wert um einen Prozentsatz der Änderung des Schiebereglers "Verschoben" anzupassen, wodurch Sie Prozentsätze der Prozentsätze erhalten, was zu Rundungsfehlern führt.

Da Sie wissen, dass Sie immer nur mit einem Gesamtwert von 100 zu tun haben, würde ich die Dinge als Ganzzahlen beibehalten und von den 100 rückwärts arbeiten, um ernsthafte Probleme beim Runden zu vermeiden. (Im folgenden Beispiel behandle ich jede Rundung als ganze Ganzzahlen am Ende.)

Meine Technik wäre, die Schieberegler auf einfach 0-100 einzustellen. Subtrahieren Sie den 'neuen' Wert von 100, um herauszufinden, wie viel neu verteilt werden soll, verteilen Sie diesen Wert entsprechend ihrem Gewicht auf die anderen Schieberegler und räumen Sie ihn auf.

Dies ist meines Wissens kein gültiger Android-Code: 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 

Dies sollte grundsätzlich jeden 0- oder 100-Wert behandeln


0

Was ist mit Round Robin Ansatz? Erstellen Sie eine Transaktion, die sicherstellt, dass der Mehrwert eines Schiebereglers gegenüber dem Peer verringert wird. und umgekehrt.

Führen Sie dann jedes Mal, wenn ein Schieberegler wechselt, die Transaktion mit einem anderen Peer-Schieberegler aus (ich würde einen Iterator erstellen, der den Peer-Schieberegler abwechselnd zurückgibt). Wenn der Peer-Schieberegler Null ist, fahren Sie mit dem nächsten fort.


0

Nur um auf die großartige Antwort des Greedy-Algorithmus von @Ordous einzugehen. Hier ist eine Aufschlüsselung der Schritte.

  1. Schieberegler bewegen
  2. Speichern Sie den UnterschiedBetrag, den es als newValue verschoben hat - oldValue (Positiver Wert bedeutet, dass es nach oben verschoben wurde und andere Schieberegler nach unten und umgekehrt verschoben werden müssen)
  3. Sortieren Sie die anderen in der Liste von dest die meisten der Entfernung kann sie sich bewegen in Richtung durch die erforderliche differenceAmount positiv oder negativ ist .
  4. Setzen Sie den BetragToMove = DifferenzAmount / verbleibender OthersCount
  5. Durchlaufen und verschieben Sie jeden Schieberegler entweder um den Betrag, um den er sich bewegen kann , oder um den Betrag, den Sie verschieben möchten . Was auch immer das kleinere ist
  6. Subtrahieren Sie den Betrag, den der Schieberegler bewegt hat, von amountToMove und dividieren Sie ihn durch den neuen verbleibenden OthersCount
  7. Sobald Sie fertig sind, haben Sie den differentAmount erfolgreich auf die anderen Schieberegler verteilt.

Fantastische Antwort hier. stackoverflow.com/a/44369773/4222929
Sean

-3

Eine einfache Technik besteht darin, Prozentsätze der Schiebereglerwerte relativ zur Summe der Schiebereglerwerte zu berechnen und die Schiebereglerwerte dann den jeweils berechneten Prozentsätzen neu zuzuweisen. Auf diese Weise werden die Schiebereglerwerte neu eingestellt, z

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

Es führt zwar zu Rundungsfehlern, kann aber behandelt werden, wenn die Schiebereglerwerte genau und immer auf 100 summiert werden müssen.

Ich habe eine Geige eingerichtet, um dies mit anglejs zu demonstrieren. Bitte besuchen Sie die Demo


2
... das ist Jeffreys Antwort. Bitte lesen Sie zuerst die anderen Antworten, um Duplikate zu vermeiden.
Jan Doggen
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.