Алгоритм узгодження чисел із мінімальною кількістю ходів


11

Це свого роду питання про відстань до редагування, і це дуже просто. Я просто мертвий з цього приводу і не можу цього розібратися.


Дано ряд чисел, напр

[3, 1, 1, 1]

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

У наведеному вище прикладі найбільш ефективними рухами будуть:

[1, 1, 1, 1]

Для цього знадобиться 2 ходи, зменшивши перше число вдвічі.

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

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

Я думаю, я міг зрозуміти:

  1. Середня,
  2. Режим,
  3. Середня

і отримати відстань редагування кожного з них, вибравши мінімальну відстань. Однак я не впевнений, що це було б правильно у кожному окремому випадку. Як я можу знати?


Якщо домен обмежений, ви можете спробувати всі можливості від min до max. В іншому випадку ви можете спробувати використовувати режим або медіану.
Бартош Пшибильський

Дякуємо @Bartek Здається, що спробувати всі можливості було б надзвичайно неефективно, якщо мати справу з сотнями чи тисячами чисел. Я перевірю режим / медіану. Але чи певні вони дають результати у кожному випадку? Це моє головне питання. Я шукаю певний, ефективний алгоритм.
dthree

Чи повинно число бути в наборі чисел, або це може бути будь-яке ціле число?
TCSGrad

@TCSGrad Це може бути будь-яке ціле число, але очевидно, ви хочете вибрати одне, що знаходиться між мінімальним та максимальним числом. У цьому випадку або 1, 2, або 3.
День

Відповіді:


10

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

Це алгоритм, який вирішує проблему (спочатку написав dc2 ):

function median(arr) {
  arr.sort(function(a, b) { return a - b; });
  var half = floor(arr.length/2);
  if ( arr.length % 2 ) {
    return arr[half];
  } else {
    return (arr[half-1] + arr[half]) / 2.0;
  }
}

function minl1(arr) {
  var moves = 0;
  var mdn = median(arr);
  for ( var i = 0; i < arr.length; ++i ) {
    moves += Math.abs(mdn - arr[i]);
  }
  return moves;
}

minl1([3, 1, 1, 1]); // -> 2

Так, це вдалося. Смішно, як це працює. Не здається, що медіана це зробить, але ей. Дуже дякую.
dthree

1
Дивіться моєї відповіді для підтвердження.
Yuval Filmus

@ dc2: Ви не можете "переконатися", "спробувавши це".
Рафаель

1
Просто зауважте: ви можете обчислити медіану часу O (n)
Bartosz Przybylski

1
@Raphael Чи добре включити код ОП до якоїсь іншої відповіді, не маючи посилання на ОП?
thefourtheye

10

Як згадує TCSGrad, даючи список цілих чисел , ви шукаєте ціле число мінімізація Доцільно обчислити : Оскільки іде від до , кількість переходить від до . Більше того, він перемикає значення лише в точкахx1,,xnm

δ(m)=i=1n|mxi|.
δ(m+1)δ(m)
δ(m+1)δ(m)=i=1n{+1mxi1m<xi=#{i:mxi}#{i:m<xi}.
m+δ(m+1)δ(m)nnx1,,xn. Не важко перевірити, що оптимальне значення - мінімальна точка, у якій . Ця мінімальна точка є однією з , тому відстань редагування становить .mδ(m+1)δ(m)0ximin(δ(x1),,δ(xn))

Припустимо також, що всі виразні, а - непарне. Нехай - медіана . Тоді тоді як , і тому є єдиним оптимумом. Якщо дорівнює, то подібний розрахунок показує, що ми можемо вибрати будь-яку точку інтервалу, що з'єднує медіани. Подібні, але більш детальні міркування показують, що будь-яка медіана є оптимальною, навіть коли не відрізняються. Тому насправді немає необхідності обчислювати на всіх .xinmxiδ(m+1)δ(m)=1δ(m)δ(m1)=1mnxiδxi


Можливо, ви пропустили це, але ця відповідь (майже) доводить, що медіана є оптимальним вибором.
Yuval Filmus

1
Ваша відповідь була чудовою, і я її схвалив. На жаль для мене, це занадто чудово, тому що я не так добре розбираюся в наукових нотаціях, залишаючи більшість із них, як винесених на сміття. Це моя проблема, а не ваша.
dthree

5

Проблема може бути сформульована як проблема LP:

Давши набір з чисел , розв’яжіть наступний LP:n[a1,a2...an]

min|aix|

(Видалено обмеження на , які не були потрібні, як вказував Рафаель)x

Після вирішення LP ви отримаєте значення відповідає рішенню. Якщо - ціле число, ви закінчили - ще, округлете його до найближчого цілого числа.xx

EDIT : Як зазначено в коментарях, цільова функція повинна бути суміщена над абсолютними різницями. Щоб перетворити його на стандартний LP, ми можемо переписати LP як:

minai

на тему:

a ia i - x i a i , x 0 i

aiaix i
aiaix i
ai,x0 i

При оптимальному рішенні , і ми можемо отримати значення з рішення.xai=|aix| ix


Отже, якщо я правильно це розумію, у моєму прикладі х було б 1 - 3, і тому я знайшов би відстань редагування 1, 2 і 3, а потім зробить хв на це?
dthree

@ dc2: Це дозволило б мінімізувати суму відстаней між кожним числом і , де - збіжне число. Обмеження гарантують, що LP швидко закінчується, а не шукати всі цілі числа! хxx
TCSGrad

Чому необхідні обмеження?
Рафаель
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.