web-dev-qa-db-ja.com

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

私が構築しているシステムには、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の範囲のスライダーを使用しましたが、これは役立つようですが、すべてのシナリオを適切に処理するアルゴリズムをまだ作成していません。

11
Ollie C

グリーディアルゴリズム

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

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

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

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

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

加重移動

別のアプローチは、彼らがする必要がある動きに重みを付けることです。私thinkこれは、「スライダーが0でスタックする」という判断から、あなたがしようとしたことです。私見これはもっと自然なことなので、もっと微調整する必要があります。

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

これは、前のパートと用語が非常に似ています。スライダーのpositionで行う移動に重みを付けず、その方向に移動するために彼らが残したスペース

10
Ordous

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

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

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

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

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

1
Jeffrey Kemp

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

あなたは合計値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の値を処理する必要があります

1
Jon Story

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

  1. スライダーを動かす
  2. differenceAmount移動したnewValue-oldValueとして保存します/(正の値は上に移動し、他のスライダーは下に移動する必要があることを意味します)
  3. リストのothers最小から最大の順に並べ替えます移動できる距離で必要な方向に並べ替えます/ differenceAmountは正または負です。
  4. amountToMove=differenceAmount/残りを設定します= OthersCount
  5. ループして各スライダーをどちらか移動量can移動するか、amountToMoveで移動します。 小さい方
  6. スライダーの量did moveからamountToMoveを引いて、newで割ります/ 残り OthersCount
  7. 完了したら、他のスライダー間でdifferenceAmountを正常に分配しました。
0
Sean

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

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

0
michaelbn