リンクされた値が常に合計100%であるXパーセントスライダーを表示するUIのアルゴリズム


11

私が構築しているシステムには、0〜100のスケールを持つ一連のUIスライダー(数値は異なります)が含まれています。スライダーとは、ボリュームコントロールのように、要素をつかんで上下にドラッグするUIを意味します。それらは常に合計100になるようにアルゴリズムで接続されています。そのため、1つのスライダーが上に移動すると、他のスライダーはすべて下に移動し、最終的にゼロになります。1つが下に移動すると、他は上に移動します。常に合計は100でなければなりません。したがって、ここのスライダーにはさまざまな値がありますが、合計は100%です。

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

最初のスライダーが40から70に上に移動した場合、他のスライダーは値を下に移動する必要があります(スライダーをドラッグするときに)。3つのスライダーは20から10に変更され、1つは低くできないためゼロのままでした。

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

もちろん、スライダーが0または100に達すると、それ以上移動できなくなります。これは、私の頭が実際に痛むところです。したがって、スライダーが高くなると、他のスライダーは低くなりますが、スライダーのいずれかがゼロに達すると、まだゼロに達していない残りのスライダーだけが低く移動できます。

この質問は実装ではなくアルゴリズムに固有なので、ここで私はこれを尋ねています。FWIWプラットフォームはAndroid Javaですが、特に関係はありません。

最初の刺し傷で行ったアプローチは、移動したスライダーの変化率を計算することでした。次に、その変更を分割し、それを(他の方向に)他のスライダーの値に適用しました。ただし、問題は、パーセンテージと乗算を使用することにより、いずれかのスライダーがゼロになった場合、ゼロから再び増加させることはできないということです。その結果、個々のスライダーがゼロで動かなくなります。丸めの問題を回避するために0〜1,000,000の範囲のスライダーを使用しましたが、これは役立つようですが、すべてのシナリオを適切に処理するアルゴリズムをまだ作成していません。


2
同様の質問がUXサイトで行われましたが、IMHOは特に満足できる方法で回答していません。良い解決策があるかどうか知りたいです。正直なところ、スライダーを相互にリンクさせると、ユーザーにとっても価値があるよりも問題が発生することがわかります。既に入力した値を変更し続けると、希望する分布になってしまうことが難しくなります。行く。
ダニエルB

1
1つの提案は、ユーザーが2つのモードを切り替えることを可能にすることです。基本的にはスライダーが互いにリンクされているモードとそうでないモードです(そして「相対モード」に戻すと、分布が再正規化されます)。これはあなたが言及した問題の一部を側は、ステッピングできますが、それだけで値を入力するためのユーザを取得する方が簡単かもしれないとUIにそんなに複雑さを追加します。
ダニエル・B

ユーザーの側で多くの操作を必要とすることは間違いなく正しいですが、この場合、それはスライダーが問題を表す一種の調査アプリなので、問題は値の相対的な重みに関するすべてです。
Ollie C

1
わかりました...私の問題は、60/30/10の分割のようなものを表現するには、3つ以上のアクションが必要になるということです。リストが大きくなると次第に悪化し、頭の中にある数に到達することはないため、非常に曖昧な入力にのみ適しています。
Daniel B

2
あなたのUXポイントから、私は合計スコアをスライダーの横に大きな数字として提示することをお勧めします。1つのスライダーを上にスライドすると、合計スコアが「100」を超え、ユーザーが別のスライダーを下にスライドして合計スコアを下げるまで、「次へ」ボタンは無効になります。ユーザーが1つのスライダーを上にスライドしたときに、他の4つのスライダーのどれを減らしたいかは決して分からないので、試さないでください。
グラハム

回答:


10

貪欲なアルゴリズム

スライダーが上(下)に移動すると、他のすべてのスライダーは下(上)に移動する必要があります。それぞれに移動可能なスペースがあります(下-位置、上:100位置)。

したがって、1つのスライダーが移動するとき、他のスライダーを取り、移動可能なスペースで並べ替え、単純にそれらを繰り返します。

各反復で、必要な方向にスライダーを移動します(左に移動する合計/キューに残っているスライダー)または移動できる距離のいずれか小さい方。

これは複雑さが線形です(一度並べ替えると、同じ順序付けされたキューを何度も使用できるため)。

このシナリオでは、スライダーが動かなくなることはありません。すべてのスライダーは可能な限り移動しようとしますが、公平なシェアまでしか移動しません。

加重移動

別のアプローチは、彼らがする必要がある動きを重み付けすることです。私が考えて、これはあなたの文「スライダーが0で動けなくなる」によって判断すると、あなたがしようとしたものです。私見これはもっと自然です、もっと微調整するだけです。

もう一度憶測しますが、さまざまなスライダーの動きをその位置で重み付けしようとしていると思います(これは、スタック0の問題に直接変換されます)。ただし、スライダーの位置を最初からまたは最後からさまざまな方向から表示できることに注意してください。減少する場合は開始位置から、増加する場合は終了位置から重み付けする場合は、問題を回避する必要があります。

これは、前のパートと用語がよく似ています。スライダーの位置によって移動を重み付けせず、スライダーがその方向に移動するために残したスペースを重み付けしてください。


1
私はより深く見ました、そして、「詰まった」ものは詰まっていないことがわかりました、しかし単にそのような低い値を持っているだけで、パーセンテージの変化はほとんど効果がありません-スライダーが動く値はその値に相対的です。反対の値を使用するというあなたの提案はかなりうまくいくかもしれないと思います。値の合計を100に保つ必要があるだけです。明確にするためにありがとうございます。
Ollie C

1

私が取るアプローチは少し異なり、各スライダーの異なる内部表現を使用する必要があります。

  1. 各スライダーは、そのスライダーの重み係数として使用される0〜100(X)の任意の値をとることができます(%ではありません)

  2. すべてのスライダー値を合計して、合計値(T)を取得します

  3. 各スライダーの表示値を決定するには、ROUND(X * 100 / T)を使用します

  4. スライダーを上下に移動すると、1つのスライダーの値のみが変更されます。そのスライダーの重み付けは、他のすべてのスライダーと比較して増減します。上記の計算により、他のすべてのスライダーへの変更が可能な限り均等に分散されます。


これは、ユーザーが主に多かれ少なかれ正確な分数を作成することを懸念している場合に適したインターフェイスです。ただし、これが他のスライドをその位置に比例して移動させるのと機能的にどのように異なるかはわかりません。
Ordous、2014年

1

「移動」スライダーの変化のパーセンテージで現在の値を調整しようとすると、事態が複雑になりすぎて、パーセンテージのパーセンテージが得られるため、丸め誤差が生じます。

合計値100しか扱っていないことがわかっているので、整数として保持し、100から逆算して、丸めに関する深刻な混乱を回避します。(以下の例では、最後に整数として丸めを処理します)

私のテクニックは、スライダーをシンプルな0-100に設定することです。100から「新しい」値を差し引いて、再配分する量を計算し、その重量に応じて他のスライダーの間で広げ、クリーンアップします)

これは、私の知る限り、有効なAndroidコードではありません: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 

これは本質的に0または100の値を処理する必要があります


0

ラウンドロビンアプローチはどうですか?1つのスライダーに値を追加するとピアから減少することを保証するトランザクションを作成します。およびその逆。

次に、スライダーを変更するたびに、異なるピアスライダーでトランザクションを実行します(ピアスライダーを順番に返すイテレーターを作成します)。ピアスライダーがゼロの場合は、次のスライダーに進みます。


0

@Ordousからの貪欲アルゴリズムのすばらしい答えについて詳しく説明します。手順の内訳は次のとおりです。

  1. スライダーを動かす
  2. 移動した差分値をnewValue - oldValueとして保存します(正の値は上に移動し、他のスライダーは下に移動する必要があることを意味します)。
  3. ソート他人から、リスト内の少なくとも最もまで距離は、彼らが移動することができますによって必要な方向にdifferenceAmount正または負であること。
  4. amountToMove = differenceAmount / 残りの OthersCountを設定します
  5. 各スライダーをループして、移動できる量またはamountToMoveのいずれかで移動し ます。どちらか小さい方
  6. スライダーの移動量をamountToMoveから減算し、新しく 残った OthersCountで割ります。
  7. 完了する、他のスライダー間のdifferenceAmountが正常に配分されました。

ここで素晴らしい答え。stackoverflow.com/a/44369773/4222929
Sean

-3

簡単な方法の1つは、スライダー値の合計に対するスライダー値のパーセンテージを計算し、計算されたそれぞれのパーセンテージにスライダー値を再割り当てすることです。このようにして、スライダーの値は再調整されます。

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

丸め誤差が発生しますが、スライダーの値を正確かつ常に合計100にする必要がある場合に対処できます。

angularjsを使用してこれをデモンストレーションするためにフィドルをセットアップしました。デモをご覧ください


2
...これがジェフリーの答えです。重複を防ぐために、最初に他の回答を読んでください。
Jan Doggen、2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.