Алгоритм для інтерфейсу користувача, що показує X відсоткових повзунків, пов'язані значення яких завжди становлять 100%


11

Система, яку я будую, включає набір повзунків користувальницького інтерфейсу (кількість варіюється), кожен зі шкалою 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, хоча в IMHO на них не відповідали особливо задоволення. Мені цікаво побачити, чи є якісь хороші рішення. Хоча, чесно кажучи, я вважаю, що повзунки повзунків один з одним завдають більше проблем, ніж це варто навіть користувачеві - стає важко закінчитися потрібним дистрибутивом, оскільки ви постійно змінюєте свої вже введені значення. йти.
Даніель Б

1
Однією пропозицією було б дозволити користувачеві перемикатися між двома режимами, по суті, одним, де повзунки пов'язані один з одним, і тим, де їх немає (а перехід назад у "відносний режим" повторно нормалізує розподіл). Це дозволило б побіжно переходити до деяких згаданих вами проблем, але додасть стільки складних можливостей інтерфейсу користувача, що користувачеві може бути просто простіше вводити значення.
Даніель Б,

Ви, безумовно, праві, що для користувача потрібно багато хитрощів, але в цьому випадку це форма програми опитування, де слайдери представляють проблеми, тому питання ВСЕ щодо відносної ваги значень.
Оллі C

1
Я розумію ... моє питання полягає лише в тому, що для вираження чогось подібного розколу 60/30/10 потрібно більше 3-х дій, тому що при спільному виконанні частини 30/10 60 продовжує рухатися. З більшими списками воно стає прогресивнішим і підходить лише для надзвичайно розпливчастого введення, оскільки ви ніколи не отримаєте його до тієї кількості, яку маєте в голові.
Даніель Б

2
З точки зору UX, я б запропонував, щоб загальний бал був представлений у великій кількості в бік повзунків. Коли ви пересуньте один повзунок вгору, загальний бал збільшується на "100", і незалежно від вашої "наступної" кнопки не буде вимкнено, поки користувач не скочить інший слайдер вниз, щоб зменшити загальний бал. Ви ніколи не дізнаєтеся, який з інших 4 повзунків користувач хоче зменшити, коли він пересуває один повзунок вгору, тому навіть не намагайтеся.
Грем

Відповіді:


10

Жадібний алгоритм

Коли повзунок рухається вгору (вниз), всі інші повинні рухатися вниз (вгору). У кожного є деякий простір, який він може переміщувати (для вниз - їх положення, для вгору: 100 позицій).

Отже, коли один повзунок рухається, візьміть інші повзунки, відсортуйте їх за простором, в якому вони можуть переміщатися, і просто перейдіть через них.

На кожній ітерації переміщуйте повзунок у потрібному напрямку на (загалом для переміщення вліво / повзунки ліворуч у черзі) або на відстань, яку він може перемістити, залежно від того, яка менша кількість.

Це лінійно за складністю (оскільки ви можете використовувати одну і ту ж упорядковану чергу знову і знову, як тільки вона буде відсортована).

У цьому сценарії жоден слайдер не застряє, всі намагаються рухатись настільки, наскільки можуть, але лише до справедливої ​​частки.

Зважений хід

Іншим підходом було б зважити рух, який їм потрібно зробити. Я думаю, що це ви намагалися зробити, судячи з вашої заяви "повзунки застрягли в 0". ІМХО, це більш природно, потрібно просто зробити більше налаштувань.

Ще раз замислюючись, я б сказав, що ви намагаєтеся зважувати рухи різних повзунків за їх положенням (це безпосередньо перекладається на вашу проблему з 0). Однак зауважте, що ви можете переглядати положення повзунка з різних напрямків - з початку або з кінця. Якщо ви важите за позицією від початку при зменшенні та позицією від кінця при збільшенні, вам слід уникати своєї проблеми.

Це термінологічно дуже схоже з попередньою частиною - не важуйте рух, що рухається за положенням повзунків, а обтяжуйте його місцем, яке їм залишилось для переміщення в цьому напрямку.


1
Я подивився глибше, і виявляється, що "застряглі" не застрягли, а просто мають такі низькі значення, зміна відсотка майже не впливає - сума, яку рухає повзунок, є відносно його значення. Я підозрюю, що ваша пропозиція використовувати протилежне значення може спрацювати набагато краще, мені просто потрібно зберегти загальне значення на рівні 100. Дякую за введення ясності.
Оллі C

1

Підхід, який я б взяв, дещо інший і передбачає використання різного внутрішнього представлення кожного слайдера.

  1. кожен повзунок може приймати будь-яке значення від 0..100 (X), яке використовується як коефіцієнт зважування для цього слайдера (не%)

  2. додайте всі значення слайдера, щоб отримати загальну цифру (T)

  3. щоб визначити відображене значення кожного слайдера, використовуйте КРУГЛО (X * 100 / T)

  4. коли повзунок переміщується вгору або вниз, ви змінюєте лише значення одного слайдера ; зважування цього слайдера збільшуватиметься чи зменшуватиметься відносно всіх інших повзунків, а вищезгаданий калькулятор забезпечить, щоб зміни на всі інші слайдери були розподілені максимально рівномірно.


Це був би хороший інтерфейс, якщо користувач в основному турбується про створення більш-менш точних дробів. Однак я не бачу, наскільки це функціонально відрізняється від того, щоб інші слайди рухалися пропорційно їхньому положенню.
Звичайний

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

Що з підходом Round Robin? Створіть транзакцію, яка гарантує, що додавання вартості одному слайдеру зменшиться від його однорангового. і навпаки.

Потім щоразу, коли зміна повзунка виконує транзакцію з іншим одноранговим повзунком (я б створив ітератор, який повертає повзунок однорангових по черзі). Якщо повзунок рівних дорівнює нулю, перейдіть до наступного.


0

Просто для детальної розробки великої відповіді на жадібний алгоритм від @Ordous. Ось розбивка кроків.

  1. Переміщення повзуна
  2. Зберегти в differenceAmount він переїхав в NEWVALUE - OldValue (засіб позитивного значення він переміщається вгору і інші повзунки повинні рухатися вниз і навпаки)
  3. Відсортувати інші в списку від найменш до найбільш від відстані вони можуть рухатися в потрібному напрямку з допомогою differenceAmount бути позитивним або негативним.
  4. Встановіть amountToMove = differenceAmount / час, що залишився OthersCount
  5. Цикл по кожному і перемістити повзунок з допомогою яких суми він може рухатися або amountToMove . Що б менше
  6. Відніміть суму повзунок зробив крок від amountToMove і розділити на новий залишився OthersCount
  7. Закінчивши, ви успішно розподілили різницю між іншими повзунками.

Тут фантастична відповідь. 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
... що є відповіддю Джефрі. Будь ласка, прочитайте спочатку відповіді, щоб уникнути дублікатів.
Ян Догген
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.