Алгоритм для інтерфейсу користувача, що показує 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.