Обчисліть медіану мільярда чисел


127

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

Одне з моїх рішень:

  • Розділіть набір порівну між комп’ютерами.
  • Сортуйте їх.
  • Знайдіть медіани для кожного набору.
  • Сортуйте набори за медіанами.
  • Об’єднайте одночасно два набори від найнижчої до найвищої медіани.

Якщо ми m1 < m2 < m3 ...спочатку злилися, Set1і Set2в отриманому наборі ми можемо відкинути всі числа нижче медіани Set12(злиті). Тож у будь-який момент ми маємо рівні за розміром набори. До речі, це неможливо зробити паралельно. Будь-які ідеї?


3
@John Boker: насправді проблема складається з двох підпроблем: 1) сортування списку та 2) отримання елемента з індексом 5'000'000'000. Я навряд чи вірю, що числа сортуються.
Роман

3
@ Роман: проблема не повинна складатися з двох описаних підпроблем, наприклад, швидкого вибору. Але швидкий вибір не паралельний, принаймні, не тривіально. І звичайно ви праві, що якщо цифри заздалегідь відсортовані, це досить безглузде питання.
Стів Джессоп

5
@fmsf: Я не думаю, що жодна англомовна країна використовує довгий мільярд англійською мовою для будь-яких офіційних цілей. Наприклад, тут, у Великобританії, ми припинили його використовувати в 1974 році. Я вважав би використання "мільярда" означати мільйон мільйонів, в англійській мові - це химерне хитрість, а не "реальний мільярд" взагалі. Звичайно, французькою мовою це було б зовсім інше, але питання не у французькій.
Стів Джессоп

5
Вам не потрібно сортувати! en.wikipedia.org/wiki/…
glebm

2
1 мільярд чисел - це лише кілька гігабайт даних, для вирішення цього завдання вам не потрібні кілька ПК та складні алгоритми. Не надмірно ускладнюйте.
користувач626528

Відповіді:


54

Ах, мій мозок тільки штовхнувся, у мене зараз є розумна пропозиція. Можливо, пізно, якби це було інтерв'ю, але неважливо:

Машина 1 називається "машиною управління", і заради аргументу вона або починається з усіх даних, і надсилає їх рівними посилками на інші 99 машин, інакше дані починають рівномірно розподілятися між машинами, і це надсилає 1/99 своїх даних кожному з інших. Перегородки не повинні бути рівними, просто близько.

Кожна інша машина сортує свої дані і робить це таким чином, що сприяє пошуку перших нижчих значень. Так, наприклад, швидкий вибір, завжди спочатку сортуючи нижню частину розділу [*]. Він записує свої дані в керуючу машину в порядку збільшення якнайшвидше (використовуючи асинхронний IO, щоб продовжувати сортування, і, ймовірно, з Nagle on: трохи експериментуйте).

Контрольна машина здійснює 99-ти напрямне злиття даних, коли вони надходять, але відкидає об'єднані дані, просто зберігаючи підрахунок кількості значень, які вона побачила. Він обчислює медіану як середнє значення 1/2 мільярду та 1/2 мільярда плюс одне число.

Це страждає від "найповільнішої в стаді" проблеми. Алгоритм не може завершитись, поки сортувальна машина не надішле кожне значення, менше за медіану. Існує обгрунтований шанс, що одна така величина буде досить високою в межах своїх пакетів даних. Отже, коли початковий розподіл даних завершений, передбачуваний час роботи - це комбінація часу для сортування 1/99-ї даних та відправлення їх назад на керуючий комп'ютер, і часу для керування для зчитування 1/2 даних . "Комбінація" десь між максимумом і сумою тих часів, ймовірно, близькою до макс.

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

Оскільки мережевий ввод-вивід, ймовірно, пов'язаний, можливо, ви можете зіграти деякі хитрощі, принаймні для даних, що повертаються до керуючої машини. Наприклад, замість того, щоб надсилати "1,2,3, .. 100", можливо, сортувальна машина могла б надіслати повідомлення із значенням "100 значень менше 101". Потім контрольна машина може виконати модифіковане злиття, в якому вона знаходить найменше з усіх цих найвищих значень діапазону, а потім повідомляє всім сортувальним машинам, що це було, щоб вони могли (а) розповісти керуючій машині, як багато значень "підрахувати" нижче цього значення та (b) відновити надсилання своїх відсортованих даних з цієї точки.

Загалом, напевно, є розумна гра вгадування виклику-відповіді, що контрольна машина може грати з 99 машинами сортування.

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

[*] доступний стек, що дозволяє - ваш вибір, яку частину зробити спочатку обмежений, якщо у вас немає O (N) додаткового місця. Але якщо у вас достатньо додаткового місця, ви можете взяти свій вибір, а якщо у вас недостатньо місця, ви можете принаймні використовувати те, що ви робите, щоб вирізати кути, зробивши спочатку невелику частину для перших кількох перегородок.


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

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

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

@Gullydwarf: багатостороннє злиття відкидає лише найменше з 99 значень, які він має в руці, кожне з яких є найменшим залишковим значенням на одній з інших машин. Якщо один з розділів повністю більший за медіану, то він не стане найменшим з цих 99 значень, поки медіана не минула (на якій ми закінчили). Так його не відкинуть.
Стів Джессоп

52
sort -g numbers | head -n 500000001 | tail -n 2 | dc -e "1 k ? ? + 2 / p"

2
ЛОЛ. Це справді працює, або вбивця ОМУ занурить його до того, як він завершиться? (на будь-якому розумному комп’ютері)
Ісак Саво

5
Треба робити. sort знає, як зробити непрофільне сортування, тому у нього не залишиться пам'яті.
DrPizza

6
@Zagfai Я не думаю, що це займе занадто багато часу; мільярд чисел - це лише 4 Гб для 32-бітових входів / поплавців, 8 ГБ для 64-бітових входів / пар. Жоден з них не здається надзвичайно оподатковуваним.
DrPizza

13
Щойно приміряв Intel i5-4200M при 3,1 ГГц (4 ядра). Відповідно до timeкоманди, застосованої до всього трубопроводу, це зайняло real=36m24s("час настінного годинника"), user=113m15s ("паралельний час", всі ядра додані). Найдовша команда, набагато випереджаючи інших, була sort, навіть якщо вона на 100% приєдналася до моїх чотирьох ядер. Споживання оперативної пам'яті було дуже прийнятним.
Morgan Touverey Quilling

12
Потім запустіть на 100 комп’ютерах, щоб ви могли бути в 100 разів впевнені, що результат правильний :)
доз

27

Я ненавиджу бути протилежним тут, але я не вважаю, що сортування не потрібно, і я думаю, що будь-який алгоритм, що включає сортування мільярдів / 100 чисел, буде повільним. Розглянемо алгоритм на одному комп’ютері.

1) Виберіть 1000 мільйонів навмання з мільярда і використовуйте їх для отримання уявлення про розподіл чисел, особливо діапазону.

2) Замість того, щоб сортувати значення, розподіліть їх у відра, грунтуючись на щойно розрахованому розподілі. Кількість відра вибирається таким чином, щоб комп'ютер міг ними ефективно працювати, але в іншому випадку він повинен бути настільки ж великим, як зручним. Діапазони відра повинні бути такими, що приблизно в однакові кількості значень входить у кожне відро (це не важливо для алгоритму, але це сприяє ефективності. 100 000 відра може бути доречним). Зверніть увагу на кількість значень у кожному відрі. Це O (n) процес.

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

4) Знайдіть фактичну медіану, вивчивши значення у цьому відрі. Тут ви можете використовувати сорт, якщо вам подобається, оскільки ви сортуєте лише 10 000 чисел. Якщо кількість значень у цьому відрі велика, то ви можете використовувати цей алгоритм ще раз, поки у вас не буде достатньо невеликого числа для сортування.

Цей підхід тривіально паралельно розподіляє значення між комп'ютерами. Кожен комп’ютер повідомляє підсумки в кожному відрі на "контрольний" комп'ютер, який робить крок 3. На кроці 4 кожен комп'ютер надсилає (відсортовані) значення у відповідному відрі на керуючий комп'ютер (ви можете робити обидва ці алгоритми паралельно, але це, мабуть, не варто).

Загальний процес є O (n), оскільки обидва етапи 3 і 4 є тривіальними, за умови, що кількість відра достатньо велика.


1
Я думаю, що це щось середнє між медіаною медіанів та алгоритмами швидкого вибору. en.wikipedia.org/wiki/Selection_algorithm
Дімат

На кроці 4 відра можуть містити не лише 10000. Може статися так, що розподіл перекошений до середини, в якому він може містити, скажімо, 80% даних, що все ще величезна кількість.
justhalf

Відредаговано, щоб врахувати це.
DJClayworth

4
Продуктивність не є O (n) у цьому алгоритмі: ви могли б отримати більшість цифр у "середній" відро, і це могло б виконати так само погано, як і сортування всього.
Sklivvz

1
@WULF Відмінне запитання. Це ключ до алгоритму, і крок 1 вирішує його. Вибір чисел для встановлення розподілу - це найкраще, що я придумав.
DJClayworth

12

Один мільярд - це насправді досить нудне завдання для сучасного комп’ютера. Ми говоримо про 4 Гб цілих 4-байтних цілих чисел ... 4 ГБ ... це ОЗУ деяких смартфонів.

public class Median {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();

        int[] numbers = new int[1_000_000_000];

        System.out.println("created array after " +  (System.currentTimeMillis() - start) + " ms");

        Random rand = new Random();
        for (int i = 0; i < numbers.length; i++) {
            numbers[i] = rand.nextInt();
        }

        System.out.println("initialized array after " + (System.currentTimeMillis() - start) + " ms");

        Arrays.sort(numbers);

        System.out.println("sorted array after " + (System.currentTimeMillis() - start) + " ms");

        if (numbers.length % 2 == 1) {
            System.out.println("median = " + numbers[numbers.length / 2 - 1]);
        } else {
            int m1 = numbers[numbers.length / 2 - 1];
            int m2 = numbers[numbers.length / 2];
            double m = ((long) m1 + m2) / 2.0;
            System.out.println("median = " + new DecimalFormat("#.#").format(m));
        }
}

Вихід на мою машину:

created array after 518 ms
initialized array after 10177 ms
sorted array after 102936 ms
median = 19196

Таким чином, це завершується на моїй машині протягом менш ніж двох хвилин (1:43 з яких 0:10 - генерувати випадкові числа), використовуючи одне ядро, і це навіть робить повний сорт. Нічого фантазійного насправді.

Це, безумовно, є цікавим завданням для більших наборів чисел. Я просто хочу тут зазначити: один мільярд - це арахіс. Тому подумайте двічі, перш ніж почати кидати складні рішення на дивно прості завдання;)


це те , що я сказав в моїй обороні тут :-) stackoverflow.com/a/31819222/363437
vidstige

1
@vidstige Я, чесно, не прочитав, але ти маєш рацію. моя відповідь, безумовно, більш практична, хоча люди, схоже, цінують трохи більше;)
sfussenegger

Це не середній , хоча, медіана , (numbers[numbers.length / 2]+numbers[numbers.length / 2+1])/2якщо numbers.lengthнавіть і numbers[numbers.length / 2]тільки якщо numbers.lengthнепарне.
Склівз

@Sklivvz правильний, але він не повинен помітно впливати на час, необхідний для обчислення медіани.
vidstige

1
@Sklivvz ти, звичайно, маєш рацію. Я щойно оновив середній розрахунок. Однак це не змінює решту відповідей.
sfussenegger

10

Оцінка порядкових статистик , як медіани і 99 - й процентиль може бути ефективно розподілено з алгоритмами , такими як трет-дайджест або Q-дайджест .

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

Цей підхід використовується еластичним пошуком і, імовірно, BigQuery (йдеться за описом функції QUANTILES).


5

Медіана для цього набору чисел

2, 3, 5, 7, 11, 13, 67, 71, 73, 79, 83, 89, 97

є 67.

Медіана для цього набору чисел

2, 3, 5, 7, 11, 13, 67, 71, 73, 79, 83, 89

становить 40.

Припустимо, що питання стосувалося близько 1 000 000 000 цілих чисел (x), де 0> = x <= 2,147,483,647, і що ОП шукав (елемент (499,999,999) + елемент (500 000 000)) / 2 (якщо числа були відсортовані). Крім того, якщо всі 100 комп'ютерів були рівними.

за допомогою мого ноутбука та GigE ...

Я знайшов, що мій ноутбук може сортувати 100000000 Int32 за 1,3 секунди. Тож приблизною оцінкою буде те, що сорт мільярдів потребує 100 х 1,3 секунди (2 хвилини 10 секунд);).

Оцінка односторонньої передачі файлу 40 МБ на гігабітному Ethernet складає .32 секунди. Це означає, що відсортовані результати з усіх комп'ютерів будуть повернені приблизно за 32 секунди (комп'ютер 99 отримав свій файл лише через 30 секунд після запуску). Звідти не слід довго забирати найнижчі 499,999,998 цифри, додавати наступні 2 і ділити на 2.


3
Коментар виборців? Це допомогло б мені зрозуміти, як я можу зробити краще.
dbasnett

5
Я не виборець, але сортування мільярдів чисел не займе 100 разів, ніж сортування 10 мільйонів, тому що найгірша складність сортування списку - це O (n log n). Сортування також на порядок повільніше, коли у вас не вистачає пам'яті та починати сортування на диску.
Річард Пул

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

Якщо припустити, що вони мають той самий коефіцієнт (який, мабуть, не виникає через проблеми з пам'яттю) тоді a*(1e7)log(1e7) = 1.3sec=> a = 1.6e-9sec => a*(1e9)log(1e9) ~ 167sec, тож ваша оцінка не була такою.
bcorso

Ваші оцінки занадто грубі. По-перше, деякі алгоритми сортування відповідають рівню o (n ^ 2) у гіршому випадку (наприклад, загальновживаний кваксорт). По-друге, ви вибрали тестовий набір даних про розмір кешу L2. Це перекосує результати. По-третє, ви (як і багато інших відповідей) припускаєте, що "число" означає "ціле число". Це може означати плаваючу, подвійну чи десяткову, які мають дуже різні характеристики продуктивності.
Склівз

5

Це може здивувати людей, але якщо кількість цілих чисел досить мала, щоб вміститися всередині 32-розрядного (або меншого) розміру - просто зробіть сортування відра! Потрібно лише 16 ГБ оперативної пам’яті для будь-якої кількості 32-бітових входів і працює в O (n), що повинно перевершити будь-які розподілені системи за розумні n, наприклад мільярд.

Отримавши відсортований список, вибирати медіану нереально. Насправді, вам не потрібно складати відсортований список, а лише дивлячись на відра, це слід робити.

Проста реалізація показана нижче. Працює лише для 16-бітних цілих чисел, але розширення до 32-бітового має бути простим.

#include <stdio.h>
#include <string.h>

int main()
{
    unsigned short buckets[65536];
    int input, n=0, count=0, i;

    // calculate buckets
    memset(buckets, 0, sizeof(buckets));
    while (scanf("%d", &input) != EOF)
    {
        buckets[input & 0xffff]++;
        n++;
    }

    // find median 
    while (count <= n/2)
    {
        count += buckets[i++];
    }

    printf("median: %d\n", i-1);

    return 0;
}

Використання текстового файлу з мільярдом (10 9 ) цифрами і працює з timeподібним чином

time ./median < billion

дає час роботи на моїй машині 1m49.293s. Більшість часу роботи - це, мабуть, IO диска.


Це насправді не відповідає на питання, і воно спирається на припущення. Наприклад, ви навіть не знаєте, що це цілі числа.
Sklivvz

Яким чином це не відповідає на запитання? І так, моя відповідь передбачає, що числа є цілими числами. Я спробував чітко висловити свої припущення.
vidstige

Ви, схоже, не заявляєте, що наявність цілих чисел є припущенням, і ви не вирішуєте, як використовувати 100 комп'ютерів, про які запитує ОП. Можна розрахувати медіану на одному вузлі, але це не найкраще рішення, якщо ви не покажете чому. Крім того, сортування radix не є o (n), якщо кількість цифр змінюється, що в цьому випадку, безумовно, робить, згідно en.wikipedia.org/wiki/Radix_sort#Efficiency , це o (n log n)
Sklivvz

Я починаю з того, що "якщо цілі числа досить малі, щоб вміститись у 32-бітове ціле число " ... Сортування Radix є O (n) для постійного розміру слова w, як описано з великою чіткістю у посиланні, яке ви розмістили. Тут я припускаю постійний розмір слова 32.
відстиж

1
Що ви робите з 99 іншими комп'ютерами, у цій відповіді не має значення. Ви можете скласти їх один на одного, щоб утворити піраміду або спалити їх. Або просто ігнорувати їх.
vidstige

3

Як не дивно, я думаю, що якщо у вас достатньо комп'ютерів, вам краще сортувати, ніж використовувати O(n)алгоритми середнього пошуку. (Якщо тільки ваші сердечники не дуже, дуже повільні, я б просто скористався ним і застосував O(n)алгоритм медіанного пошуку лише для номерів 1e9; якщо у вас 1e12, то це може бути менш практичним.)

У будь-якому випадку, припустимо, у нас є більше ніж ядер n ядер, щоб вирішити цю проблему, і ми не піклуємось про споживання енергії, просто отримаємо відповідь швидко. Давайте припустимо, що це машина SMP з усіма даними, вже завантаженими в пам'ять. (Наприклад, 32-ядерні машини Sun мають такий тип.)

Один потік розбиває список сліпо на шматки рівних розмірів і вказує іншим потокам M сортувати їх. Ці нитки старанно роблять це (n/M) log (n/M)вчасно. Потім вони повертають не тільки своїх медіанів, але, скажімо, 25-й і 75-й процентилі (викривлені гірші випадки краще, якщо ви виберете трохи інші числа). Тепер у вас є 4M діапазони даних. Потім ви сортуєте ці діапазони і працюєте вгору по списку, поки не знайдете таке число, що, якщо викинете кожен діапазон, менший за число або містить його, ви викинете половину своїх даних. Це ваша нижня межа для медіани. Зробіть те ж саме для верхньої межі. Це займає щось на кшталт M log Mчасу, і всі сердечники повинні дочекатися цього, так що це дійсно даремноM^2 log Mпотенційний час. Тепер у вас єдина нитка, скажіть іншим викинути всі дані за межі діапазону (ви повинні викинути близько половини на кожен прохід) і повторити - це тривіально швидка операція, оскільки дані вже сортовані. Вам не доведеться повторювати це не log(n/M)раз, перш ніж швидше просто захопити дані, що залишилися, і використати на них стандартний O(n)медіанний пошук.

Отже, загальна складність - це щось подібне O((n/M) log (n/M) + M^2 log M log (n/M)). Таким чином, це швидше, ніж O(n)середня сортування на одному ядрі, якщо M >> log(n/M)і M^3 log M < n, що справедливо для описаного вами сценарію.

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


o (n / M log (n / M)) - це, буквально, o (n log n), тому що o (n / M log (n / M)) = 1 / M o (n (log n - log M) ) = o (n журнал n). Ви насправді не можете порівнювати це з подібним o (n), оскільки "o" в основному означає "пропорційний для великого дуже n з якоюсь невизначеною константою". Якщо ви не знаєте цих констант, ви не можете порівнювати, однак для досить великих N константи не є домінуючими. Для менших цифр усі ставки відключені, o (1) може бути повільніше, ніж o (n!).
Sklivvz

@Sklivvz - nі Mце змінні, які можуть довільно масштабувати, тому одна включає обидва. Зокрема, я постулював це M> log n, маючи на увазі, що якщо вам байдуже, що це n log nзамість просто n, то вам також потрібно подбати M.
Рекс Керр

3

Це можна зробити швидше, ніж алгоритм, проголосований (n журнал n)

- Статистика алгоритму розподілу статистики замовлень - O (n)
Спростіть задачу до вихідної проблеми пошуку k-го числа в несортованому масиві.
- Підрахунок гістограми сортування O (n)
Ви повинні припустити деякі властивості щодо діапазону чисел - чи може діапазон вміститись у пам'яті? - Зовнішній сорт злиття - O (n log n) - описаний вище
Ви в основному сортуєте числа на першому проході, а потім знаходите медіану на другому.
- Якщо щось відомо про розподіл чисел, можуть бути вироблені інші алгоритми.

Більш детальну інформацію та реалізацію див:
http://www.fusu.us/2013/07/median-in-large-set-across-1000-servers.html


2

Одного комп’ютера більш ніж достатньо для вирішення проблеми.

Але припустимо, що є 100 комп’ютерів. Єдине складне, що вам слід зробити - це сортувати список. Розділіть його на 100 частин, відправте одну частину на кожен комп’ютер, нехай вони там будуть відсортовані, а потім з’єднайте частини.

Потім візьміть номер із середини відсортованого списку (тобто з індексом 5 000 000 000).


3
У будь-якому випадку, моя репутація досить кругла :)
Роман

Об’єднання в кращому випадку O (n), і ви можете знайти медіану на одному ядрі в O (n), тому це, здається, створює багато зайвої роботи без виграшу.
Рекс Керр

2

Це залежить від ваших даних. Найгірший сценарій - це рівномірно розподілені числа.

У цьому випадку ви можете знайти медіану за О (N) часом, як у цьому прикладі:

Припустимо, ваші цифри - 2,7,5,10,1,6,4,4,6,10,4,7,1,8,4,9,9,3,4,3 (діапазон - 1-10) .

Створюємо 3 відра: 1-3, 4-7, 8-10. Зверніть увагу, що верх і низ мають рівні розміри.

Наповнюємо відра цифрами, підраховуємо, скільки випадає в кожному, макс і хв

  • низький (5): 2,1,1,3,3, хв 1, макс. 3
  • середина (10): 7,5,6,4,4,6,4,7,4,4, хв 4, макс 7
  • високий (5): 10, 10, 8, 9, 9, хв 8, макс 10

Середнє значення падає в середнє відро, ми залишаємо без уваги решту

Ми створюємо 3 відра: 4, 5-6, 7. Низький розпочнеться з підрахунку 5, а з максимуму 3, а високий - з хв 8 і з 5.

Для кожного числа підраховуємо, скільки потрапляє у низьке та високе відро, максимум та хв, та тримаємо середнє відро.

  • старий низький (5)
  • низький (5): 4, 4, 4, 4, 4, макс 4
  • середина (3): 5,6,6
  • високий (2): 7, 7, хв 7
  • старий високий (5)

Тепер ми можемо розрахувати медіану безпосередньо: у нас така ситуація

old low    low          middle  high  old high
x x x x x  4 4 4 4 4 4   5 6 6  7 7   x x x x x

тому медіана становить 4,5.

Припускаючи, що ви трохи знаєте про розподіл, ви можете точно налаштувати, як визначити діапазони для оптимізації швидкості. У будь-якому випадку продуктивність повинна йти з O (N), тому що 1 + 1/3 + 1/9 ... = 1,5

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

Всі ці операції можна паралелізувати, ви можете надати 1/100 даних кожному комп'ютеру і обчислити 3 відра в кожному вузлі, а потім розподілити відро, яке ви зберігаєте. Це ще раз змушує вас ефективно використовувати мережу, оскільки кожне число передається в середньому в 1,5 рази (тому O (N)). Можна навіть перемогти це, якщо ви передасте лише мінімальні числа серед вузлів (наприклад, якщо у вузла 1 є 100 чисел, а у вузла 2 є 150 номерів, то вузол 2 може дати 25 номерів вузлу 1).

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


1
Чи не справжній гірший випадок (для вашого алгоритму), коли всі числа рівні? Якщо я маю рацію, жодне з ваших відра ніколи не заповниться середнім, усіма елементами. Таким чином, вам доведеться кожного разу перетинати всі елементи, швидко просуваючись до середини інтервалу. Я вважаю, що це було б O(n log n)у такому випадку. Чи є сенс? До речі, мені подобається ваша ідея
Dici

1
@Dici не дуже: по-перше, ви можете легко клацнути "все той же" сценарій, оскільки ви знаєте min та max. Як я вже говорив у відповіді, знаючи, що розподіл може призвести до вашого вибору ковзання; по-друге, все одно знадобиться те, o(n)+o(n/3)+o(n/9)+...що досі є, o(n)а ні o(n log n).
Sklivvz

З іншого боку, мабуть, є інший найгірший сценарій, розподіл у формі U. Мені потрібно трохи подумати над цим, формалізувати найгірший випадок, але це, можливо, піде гірше, ніж o(n)у тому випадку, при наївному розподілі.
Sklivvz

Ммм, так, мін і макс допоможуть легко розібратися зі справою "все те саме"
Dici

2

Простіший метод - мати зважені числа.

  • Розділіть великий набір серед комп'ютерів
  • Сортувати кожен набір
  • перебирайте малий набір і обчислюйте ваги для повторних елементів
  • об'єднайте кожен 2 набори в 1 (кожен вже відсортований) оновлення ваг
  • продовжуйте об’єднувати набори, поки не отримаєте лише один набір
  • повторюйте цей набір ваги, поки ви не досягнете OneBillion / 2

1

Розділіть 10 ^ 9 чисел, 10 ^ 7 на кожному комп’ютері ~ 80 Мб на кожному. Кожен комп'ютер сортує свої номери. Потім комп'ютер 1 злиття - сортує власні номери з номерами з комп’ютера 2, комп’ютера 3 і 4 тощо. Потім комп'ютер 1 записує половину чисел назад до 2, 3 до 4 тощо. Потім 1 злиття сортує числа з комп'ютерів 1,2,3,4, записує їх назад. І так далі. Залежно від розміру оперативної пам’яті на комп’ютерах, у яких ви можете уникнути, якщо на кожному кроці не записувати всі номери на окремі комп’ютери, можливо, ви зможете накопичити числа на комп’ютері 1 протягом декількох кроків, але ви зробите математику.

О, нарешті, отримайте середнє значення 500000000 та 500000001st (але перевірте, чи там вистачає 00, у мене немає).

РЕДАКТ: @ Роман - ну якщо ви не можете в це повірити, навіть це правда, то немає сенсу в моєму розкритті істини чи неправдивості судження. Що я хотів сказати, це те, що жорстока сила іноді б’є спритно в гонці. Мені знадобилося близько 15 секунд, щоб розробити алгоритм, який я впевнений, що зможу реалізувати, який буде працювати, і який буде пристосований до широкого діапазону розмірів входів і чисельності комп'ютерів, і налаштовується на характеристики комп'ютерів і організація мереж. Якщо вам потрібно, чи хтось інший, скажіть за 15 хвилин, щоб розробити більш складний алгоритм, я маю перевагу 14m45s, щоб кодувати своє рішення і запустити його.

Але я вільно визнаю, що це все твердження, я нічого не міряв.


тут ми просто об'єднуємо всі числа. Чи можемо ми зробити це кращим чином, використовуючи: - "ми можемо знайти медіану двох відсортованих списків у час входу. N - довжина кожного списку."
анонім

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

ти справді зробив це за 7 хвилин? Я не можу повірити, що навіть якщо це правда. Я зробив аналогічне завдання (це було завдання на університет), і на реалізацію та тестування всіх видаляючих матеріалів знадобилося близько 2 годин (я використовував Java RMI).
Роман

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

"смутно правдоподібно" - це досить добре для мене @Steve! Особливо у відповідь на невиразно неправдоподібне запитання.
Марка високої продуктивності

1

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

Є 1 батьківський вузол та 99 дочірніх вузлів. Дочірні вузли мають два дзвінки api:

  • stats (): повертає min, max та count
  • порівняти (median_guess): повертає значення відповідності підрахунку, підраховує менше значення і рахує більше значення

Батьківський вузол викликає stats () на всіх дочірніх вузлах, відзначаючи мінімум та максимум усіх вузлів.

Зараз двійковий пошук може здійснюватися наступним чином:

  1. Розріжте мінімальне та максимальне округлення вниз - це серединна «здогадка»
  2. Якщо більший за кількість більше, ніж кількість, встановіть мінімум на здогадку
  3. Якщо більший за рахунок менше, ніж менший за кількість, встановіть максимум для здогадки
  4. Якщо підрахунок є непарним закінченням, коли мінімальний і максимальний рівні
  5. Якщо підрахунок закінчується, коли максимум <= мінімум + здогадка.match_count Це можна зробити на вузлах, використовуючи несортовані дані (скажімо, з файлів журналів) наступним чином.

Є 1 батьківський вузол та 99 дочірніх вузлів. Дочірні вузли мають два дзвінки api:

  • stats (): повертає min, max та count
  • порівняти (median_guess): повертає значення відповідності підрахунку, підраховує менше значення і рахує більше значення

Батьківський вузол викликає stats () на всіх дочірніх вузлах, відзначаючи мінімум та максимум усіх вузлів.

Зараз двійковий пошук може здійснюватися наступним чином:

  1. Розріжте мінімальне та максимальне округлення вниз - це серединна «здогадка»
  2. Якщо більший за кількість більше, ніж кількість, встановіть мінімум на здогадку
  3. Якщо більший за рахунок менше, ніж менший за кількість, встановіть максимум для здогадки
  4. Якщо підрахунок є непарним закінченням, коли мінімальний і максимальний рівні
  5. Якщо підрахунок буде рівним, коли максимум <= мінімум + здогадка.матч_рахунок

Якщо статистику () та порівняння () можна було попередньо обчислити за допомогою сортування O (N / Mlogn / M), то попередній розрахунок O (N / M) зі складністю пам'яті O (N) для попереднього розрахунок. Тоді ви могли б порівнювати () у постійному часі, так що вся справа (включаючи попередній розрахунок) буде працювати в O (N / MlogN / M) + O (logN)

Дайте мені знати, чи я помилився!


так, я просто бинарний пошук. Збережеться пропускна здатність мережі лише зателефонувавши до кожного комп'ютера кілька разів. Крім того, кожна машина може мати "поворот", де вона заміняє номери з будь-якої сторони стрижня, щоб заощадити час. (стрижень був би попередньою оцінкою медіани, тому наступного разу потрібно лише пройти всі числа на одній стороні стрижня)
Роберт Кінг

0

Як щодо цього: - кожен вузол може приймати 1 мільярд / 100 чисел. На кожному вузлі елементи можна сортувати та знаходити медіану. Знайдіть медіану медіанів. ми можемо, агрегувавши підрахунки чисел, менших за медіану середніх по всіх вузлах, виявити x%: y% розщеплення, яке робить медіана середніх. Тепер попросіть усі вузли видалити елементи, менші за медіану медіанів (наприклад, 30%: 70% розбиття) .30% числа видаляються. 70% 1 мільярда - 700 мільйонів. Тепер усі вузли, які видалили менше 3 мільйонів вузлів, можуть відправити ці додаткові вузли назад на основний комп'ютер. Основний комп'ютер перерозподіляється таким чином, що тепер усі вузли матимуть майже рівну кількість вузлів (7млн.). Тепер, коли проблема зменшується до 700 мільйонів чисел .... продовжується, поки ми не маємо менший набір, який можна обчислити на одному комп.


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

У першій ітерації ми шукаємо 500Мільйонне число. У другій ітерації - якщо кількість видалених чисел становить 300 мільйонів, ми шукаємо 200 мільйонне число і так далі ...
анонім

2
Це здається, що це на правильному шляху, але ви не пояснюєте дуже чітко, як уникнути випадкового відкидання медіани зі своїм розколом на 30% / 70%. Візьміть наступний контрприклад: припустимо, ваші перші 29% - це всі нулі, а всі інші блоки нараховуються до 1000, а кожен набір блоків на один більше, ніж останній. Медіана 30-го відсотка викине всі 29% даних і трохи менше половини 61% даних, що становить 29 + 30% = 59% даних. На жаль, ми просто викинули справжню медіану! Отже, мабуть, ви цього не маєте на увазі, або, принаймні, це маєте на увазі більш розумно, ніж я тлумачив.
Рекс Керр

0

Давайте спочатку розробимо, як знайти медіану з n чисел на одній машині: я в основному використовую стратегію розподілу.

Проблема: вибір (n, n / 2): Знайдіть n / 2-е число від найменшого числа.

Ви вибираєте, наприклад, середній елемент k та дані розділу в 2 підмасиви. 1-й містить всі елементи <k, а 2-й містить усі елементи> = k.

якщо sizeof (1-й під-масив)> = n / 2, ви знаєте, що цей підмасив містить медіану. Потім ви можете скинути другий масив. Вирішіть цю проблему вибору (розмір 1-го під-масиву, n / 2) .

В іншому випадку викиньте цей 1-й підрядний масив і вирішіть виділення (2-й підрядний масив, n / 2 - sizeof (1-й під-масив))

Робіть це рекурсивно.

часова складність - O (n) очікуваний час.

Тепер, якщо у нас є багато машин, для кожної ітерації ми маємо обробити масив для розбиття, ми розподілимо масив на різні машини. Кожна машина обробляє свій фрагмент масиву і повертає резюме до контролюючої машини концентратора, тобто розміру 1-го підмасиву та розміру 2-го підмасива.Машини-концентратори складають підсумки та вирішують, який підмасив (1-й чи 2-й) обробляти далі та другий параметр вибору, і повертає його назад до кожної машини. і так далі.

Цей алгоритм можна реалізувати дуже акуратно за допомогою зменшення карт?

Як це виглядає?


0

Я думаю, що відповідь Стіва Джессопа буде найшвидшим.

Якщо розмір передачі даних в мережі є вузьким місцем, ось ще один підхід.

Divide the numbers into 100 computers (10 MB each). 
Loop until we have one element in each list     
    Find the meadian in each of them with quickselect which is O(N) and we are processing in parallel. The lists will be partitioned at the end wrt median.
    Send the medians to a central computer and find the median of medians. Then send the median back to each computer. 
    For each computer, if the overall median that we just computed is smaller than its median, continue in the lower part of the list (it is already partitioned), and if larger in the upper part.
When we have one number in each list, send them to the central computer and find and return the median.

32 Мб кожен, ви маєте на увазі?
Dici

Що ви маєте на увазі, продовжуючи в нижній частині списку?
Рутвік Вайла

0

Я б це зробив так:

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

коли виявляються найвищі та найнижчі номери, один комп’ютер зчитує дані та розподіляє кожне число рівномірно решті 99; числа розподіляються по рівних інтервалах; (один може зайняти від -100 мільйонів до 0, інший - від 0 до 100 мільйонів тощо);

Отримуючи номери, кожен з 99 комп'ютерів вже їх сортує;

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

:) вуаля

PS Здається, тут багато плутанини; МЕДІАН - це ЧИСЛО В СЕРЕДІ СОРТИРАНОГО СПИСОКУ НОМЕРІВ!


0

Для пошуку медіани можна використовувати метод дерева турнірного дерева. Ми можемо створити дерево з 1000 залишених вузлів таким чином, щоб кожен вузол листя був масивом. Потім ми проводимо n / 2 турнірів між різними масивами. Значення в корені після n / 2 турнірів є результатом.

http://www.geeksforgeeks.org/tournament-tree-and-binary-heap/


0

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

Після цього всі машини повертають свій хеш-набір на головну машину. Основна машина поєднує хеш-набори, підсумовуючи кількість того самого ключа, який знайдено в хеш-наборі. Наприклад, хеш-набір машини №1 мав запис ("1", 7), а хеш-набір машини №2 мав запис ("1", 9), тому головна машина при комбінуванні хеш-наборів робить запис ("1", 16) тощо.

Коли об’єднання хеш-файлів були об'єднані, просто впорядкуйте клавіші, і тепер ви легко зможете знайти (n / 2)-й елемент та (n + 2/2)-й елемент із відсортованого набору хешів.

Цей метод не буде корисним, якщо мільярдні числа будуть чіткими.


0

Ну, припустимо, ви знаєте, що кількість цілих чисел становить (скажімо) 4 мільярди, тоді ви можете згрупувати їх у 64 відра і отримати розподілену кількість для кожного відра з кожної машини в кластері (100 комп'ютерів). Поєднайте всі ці підрахунки. Тепер знайдіть відро з медіаною, і на цей раз попросіть відра лише для 64-кілометрових елементів, які лежали б у вашому цільовому відрі. Для цього потрібні запити O (1) (конкретно 2) над вашим "кластером". : D


0

Моя копійка стоїть, зрештою, що вже виховали інші:

Пошук медіани на одній машині є O (N): https://en.wikipedia.org/wiki/Selection_algorithm .

Відправлення N чисел до 100 машин також O (N). Отже, щоб зробити використання 100 машин цікавим, або зв'язок повинен бути відносно швидким, або N настільки великий, що одна машина не може впоратися з нею, поки N / 100 є виконаною, або ми просто хочемо розглянути математичну задачу, не турбуючись про передача даних.

Таким чином, я вважаю, що в розумних межах ми можемо надсилати / розподіляти номери, не впливаючи на аналіз ефективності.

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

  1. Кожна машина отримує N / 100 чисел, обчислює власну медіану і передає цю інформацію майстру.
  2. Майстер складає відсортований список всіх різних медіанів і надсилає їх назад до кожної машини, визначаючи впорядковану послідовність відер (на кожній машині однакові), по одному для кожного медіанного значення (відро з одним значенням) і по одному на кожен інтервал між сусідні медіани. Звичайно, існують також відра нижнього та вищого рівня для значень нижче найнижчої медіани та вище найвищої.
  3. Кожна машина обчислює, скільки цифр потрапляє в кожне відро і передає цю інформацію назад майстру.
  4. Ведучий визначає, яке відро містить медіану, скільки нижчих значень (загалом) опускаються нижче цього відра і скільки вище.
  5. Якщо вибране відро - це однозначне відро (одне з медіанів) або вибране відро містить лише 1 (N непарних) або 2 (N парних) значень, ми готові. В іншому випадку ми повторимо описані вище кроки із наступними (очевидними) модифікаціями:
  6. Тільки номери з вибраного відра (повторно) розподіляються від ведучого до 100 машин тощо
  7. Ми не збираємось обчислювати (на кожній машині) медіану, а k-ту величину, де ми враховуємо, скільки вищих чисел було відкинуто від загальної кількості та скільки менших чисел. Концептуально кожна машина також має свою частку відкинутих низьких / високих чисел і враховує це при обчисленні нової медіани в наборі, що (концептуально) включає (частку) відкинутих чисел.

Складність часу:

  1. Невелике роздумування переконає вас у тому, що на кожному кроці загальна кількість значень для аналізу зменшується на коефіцієнт щонайменше на два (2 - це досить важкий випадок; ви можете очікувати значно кращого зменшення). З цього ми отримуємо:
  2. Якщо припустити, що знаходження медіани (або k-го значення), що є O (N), займає c * N час, коли збірник c не дуже сильно змінюється на N, щоб ми могли прийняти його як константу на даний момент, Отримаємо наш кінцевий результат не більше ніж 2 * c * N / 100 разів. Таким чином, використання 100 машин дає нам коефіцієнт швидкості 100/2 (принаймні).
  3. Як зазначалося спочатку: час, пов’язаний із передачею номерів між машинами, може зробити привабливішим просто робити все на одній машині. Однак якщо ми підемо на розподілений підхід, загальна кількість номерів, які слід повідомити у всіх кроках разом, не перевищуватиме 2 * N (N вперше, <= N / 2 вдруге, <= половина від цього третій тощо).

-1
  1. Розділіть 1 мільярд чисел на 100 машин. Кожна машина матиме 10 ^ 7 номерів.

  2. Для кожного вхідного номера на машину зберігайте номер у частотній карті, число -> лічильник. Також зберігайте мінімальну кількість у кожній машині.

  3. Знайдіть медіану в кожній машині: починаючи з мінімального числа в кожній машині, підсумовуйте підрахунки, поки не буде досягнуто медіанного індексу. Медіана в кожній машині становитиме бл. менше і більше 5 * 10 ^ 6 чисел.

  4. Знайдіть медіану всіх медіанів, яка буде меншою та більшою приблизно. 50 * 10 ^ 7 чисел, що є медіаною 1 мільярда чисел.

Тепер деяка оптимізація 2-го кроку: Замість того, щоб зберігати в частотній карті, зберігайте підрахунки у змінному бітовому масиві. Наприклад: Скажімо, починаючи з мінімального числа в машині, це підрахунок частоти:

[min number] - 8 count
[min+1 number] - 7 count
[min+2 number] - 5 count

Вищезазначене може зберігатися в бітовому масиві у вигляді:

[min number] - 10000000
[min+1 number] - 1000000
[min+2 number] - 10000

Зауважте, що загалом це буде коштувати приблизно 10 ^ 7 біт для кожної машини, оскільки кожна машина обробляє лише 10 ^ 7 чисел. 10 ^ 7 біт = 1,25 * 10 ^ 6 байт, що становить 1,25 МБ

Таким чином, при наведеному вище підході кожній машині знадобиться 1,25 Мб місця для обчислення локальної медіани. І медіану медіанів можна обчислити з цих 100 місцевих медіанів, в результаті чого медіана становить 1 мільярд чисел.


Що робити, якщо числа плавають?
Sklivvz

-1

Я пропоную метод розрахунку приблизно медіани. :) Якщо ці мільярдні числа в довільному порядку, я думаю, що я можу вибрати 1/100 або 1/10 з одного мільярдного числа випадковим чином, відсортувати їх за 100 машинами, а потім вибрати медіану з них. Або давайте розділимо мільярд чисел на 100 частин, нехай кожна машина вибирає 1/10 кожної частини випадковим чином, обчислює медіану їх. Після цього у нас є 100 чисел і ми можемо прорахувати медіану 100 числа простіше. Просто пропозиція, я не впевнений, чи правильно це математично. Але я думаю, що ти можеш показати результат не дуже хорошому математику.


Це, очевидно, не правильно, і я настійно рекомендую вам ніколи не вважати, що ваш інтерв'ю є дурною свинею, яку ви можете обдурити
Dici

Ха-ха, хоч це не змінює факту, що ваша відповідь неправильна. Це дуже просто довести
Dici

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

-3

Відповідь Стіва Джессопа невірна:

розглянемо наступні чотири групи:

{2, 4, 6, 8, 10}

{21, 21, 24, 26, 28}

{12, 14, 30, 32, 34}

{16, 18, 36, 38, 40}

Медіана - 21, що міститься у другій групі.

Медіана чотирьох груп - 6, 24, 30, 36, Загальна медіана - 27.

Отже після першого циклу чотири групи стануть:

{6, 8, 10}

{24, 26, 28}

{12, 14, 30}

{16, 18, 36}

21 вже неправильно відкидається.

Цей алгоритм підтримує випадок лише тоді, коли є дві групи.

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