用于显示X个百分比滑块的UI的算法,这些滑块的链接值始终总计为100%


11

我正在构建的系统包括一组UI滑块(数量有所不同),每个滑块的比例为0-100。滑块是指一个用户界面,您可以在其中抓取一个元素并将其上下拖动,就像音量控件一样。它们通过一种算法进行连接,以确保它们始终总为100。因此,当一个滑块向上移动时,其他所有滑块都向下移动,最终变为零。当一个向下移动时,其他向上移动。在任何时候,总数必须为100。因此,此处的滑块具有不同的值,但总计为100%:

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

如果第一个滑块然后从40向上移动到70,则其他滑块的值必须向下移动(拖动滑块时)。请注意,三个滑块从20更改为10,一个滑块保持在零,因为它不能降低。

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

当然,当任何滑块达到0或100时,它都无法进一步移动,这就是我的头部开始真正受伤的地方。因此,如果滑块向高处移动,则其他滑块向低处移动,但是当其中任何一个达到零时,只有尚未达到零的其余滑块才能向低处移动。

我在这里问这个问题,因为这个问题特定于算法而不是实现。FWIW该平台是Android Java,但这并不特别相关。

我第一次刺中的方法是计算移动的滑块的百分比变化。然后,我拆分该更改并将其(沿另一个方向)应用于其他滑块值。但是,问题在于,如果使用百分比乘以,如果任何滑块滑到零,就永远不会再从零开始增加-最终结果是各个滑块卡在零上。为了避免舍入问题,我使用了范围为0-1,000,000的滑块,这似乎很有用,但是我还没有创建一种能够很好地处理所有情况的算法。


2
尽管恕我直言,还没有以特别令人满意的方式回答过类似的问题,但在UX网站上也曾提出过类似的问题。我很好奇,看看是否有好的解决方案。坦白地说,我发现将滑块相互链接会给用户带来更多麻烦,甚至超过用户的价值。由于您不断更改自己输入的值,因此很难获得所需的分布走。
Daniel B

1
一种建议是允许用户在两​​种模式之间进行切换,实质上是一种在其中滑块彼此链接的模式,而另一种在它们之间没有链接的模式(切换回“相对模式”将重新标准化分布)。这将允许侧步进一些你提到的问题,但增加了这么多复杂的用户界面,它可能只是更容易得到用户的输入值。
丹尼尔乙

您肯定是对的,这需要用户进行很多摆弄,但是在这种情况下,这是一种调查应用程序,其中滑块代表问题,因此问题只在于值的相对权重。
Ollie C

1
我明白...我的问题是,只是表达有点像60/30/10分将需要更多比3分的动作,因为与30/10部分摆弄时,60不断走动。随着列表的增加,它会变得越来越糟,并且它实际上仅适用于非常模糊的输入,因为您将永远无法达到头脑中的数字。
Daniel B

2
从用户体验的角度来看,我建议将总分以较大的数字显示在滑块的侧面。当您向上滑动一个滑块时,总得分增加到“ 100”以上,而您的“下一个”按钮将被禁用,直到用户向下滑动另一个滑块以降低总得分为止。您将永远不会知道用户在向上滑动一个滑块时想要减少其他四个滑块中的哪个,所以甚至不要尝试。
格雷厄姆

Answers:


10

贪婪算法

当滑块向上(向下)移动时,所有其他滑块都需要向下(向上)移动。每个人都有一定的空间可以移动(向下-他们的位置,向上:100位)。

因此,当一个滑块移动时,请拿住其他滑块,将其按可移动的空间排序,然后简单地遍历它们。

在每次迭代中,将滑条沿所需的方向移动(总计向左移动/滑条在队列中向左移动)或其可移动的距离(以较小者为准)。

这是线性的复杂性(因为一旦排序,就可以反复使用相同的有序队列)。

在这种情况下,没有滑块被卡住,所有滑块都尽其所能地移动,但只能达到其应有的份额。

加权移动

另一种方法是权衡他们需要采取的行动。我认为这是您尝试执行的操作,根据您的“滑块停留在0”语句来判断。恕我直言,这是更自然的,您只需要做更多的调整即可。

再次推测,我会说您尝试通过不同滑块的位置加权它们的位置(这将直接转化为您陷入0问题)。但是请注意,您可以从不同的方向查看滑块的位置-从头到尾。如果减少时从头开始按权重,增加时从头开始按权重,则应避免出现问题。

这在术语上与上一部分非常相似-不要按滑块的位置加权要移动的位置,不要按滑块在该方向上留下的空间加权。


1
我看得更深,结果发现“卡住”的那些并没有卡住,只是具有如此低的值,百分比变化几乎没有作用-滑块移动的量与其值有关。我怀疑您建议使用相反的值可能会更好,我只需要将总值保持在100即可。谢谢您的说明。
Ollie C

1

我采用的方法略有不同,并且涉及到每个滑块使用不同的内部表示形式。

  1. 每个滑块可以取0..100(X)中的任何值,用作该滑块的加权因子(不是%)

  2. 将所有滑块值相加以获得总值(T)

  3. 要确定每个滑块的显示值,请使用ROUND(X * 100 / T)

  4. 当滑块向上或向下移动时,您只能更改一个滑块的值;该滑块的权重将相对于所有其他滑块增加或减少,并且上述计算将确保对所有其他滑块的更改将尽可能平均地分布。


如果用户主要关注制作或多或少的精确分数,那么这将是一个很好的界面。但是,我看不到这与使其他幻灯片按其位置成比例地移动有什么功能上的区别。
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

循环法又如何呢?创建一个事务,以确保向一个滑块增加价值将使其同行减少。反之亦然。

然后,每次滑块更改都使用其他对等滑块运行事务(我将创建一个迭代器,该迭代器依次返回对等滑块)。如果对等滑块为零,则继续进行下一个。


0

只是为了详细说明@Ordous的贪婪算法答案。这是步骤的细分。

  1. 移动滑块
  2. 将其移动的分量保存为newValue - oldValue(正值表示它向上移动,其他滑块需要向下移动,反之亦然)
  3. 排序其他列表中从最低到最高距离,他们可以移动通过所需方向differenceAmount是正还是负。
  4. 设置amountToMove = differenceAmount / 剩余 OthersCount
  5. 循环并通过移动每个滑块或者它的量可以移动或 amountToMove。以较小者为准
  6. 减去滑条确实amountToMove 移动的量并除以新的 剩余 OthersCount
  7. 完成后,您已成功在其他滑块之间分配了differenceAmount

很棒的答案在这里。stackoverflow.com/a/44369773/4222929
肖恩

-3

一种简单的技术是计算滑块值相对于滑块值总和的百分比,然后将滑块值重新分配给相应的计算百分比。这样,滑块值将被重新调整,例如

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

尽管它会引入舍入误差,但是可以解决这种情况,以防我们需要滑块值精确且始终等于100。

我已经设置了一个小提琴来使用angularjs进行演示。请访问演示


2
...这就是杰弗里的答案。请先阅读其他答案,以防止重复。
Jan Doggen'3
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.