Интервью Вопрос: Найти медиану от мега числа целых чисел - PullRequest
35 голосов
/ 26 августа 2010

Существует файл, который содержит 10G (1000000000) целых чисел, пожалуйста, найдите медиану этих чисел. вам дают 2G памяти, чтобы сделать это. Кто-нибудь может придумать разумный путь? Спасибо!

Ответы [ 9 ]

37 голосов
/ 26 августа 2010

Создать массив из 8-байтовых длин, который имеет 2 ^ 16 записей. Возьмите свои входные числа, сдвиньте младшие шестнадцать бит и создайте гистограмму.

Теперь вы будете считать в этой гистограмме, пока не дойдете до корзины, которая покрывает среднюю точку значений.

Пройдите снова, игнорируя все числа, у которых нет такого же набора старших бит, и составьте гистограмму младших бит.

Считайте по этой гистограмме до тех пор, пока не достигнете корзины, которая покрывает среднюю точку (всего списка) значений.

Теперь вы знаете медиану в O(n) времени и O(1) пространстве (на практике, до 1 МБ).

Вот пример кода Scala, который делает это:

def medianFinder(numbers: Iterable[Int]) = {
  def midArgMid(a: Array[Long], mid: Long) = {
    val cuml = a.scanLeft(0L)(_ + _).drop(1)
    cuml.zipWithIndex.dropWhile(_._1 < mid).head
  }
  val topHistogram = new Array[Long](65536)
  var count = 0L
  numbers.foreach(number => {
    count += 1
    topHistogram(number>>>16) += 1
  })
  val (topCount,topIndex) = midArgMid(topHistogram, (count+1)/2)
  val botHistogram = new Array[Long](65536)
  numbers.foreach(number => {
    if ((number>>>16) == topIndex) botHistogram(number & 0xFFFF) += 1
  })
  val (botCount,botIndex) =
    midArgMid(botHistogram, (count+1)/2 - (topCount-topHistogram(topIndex)))
  (topIndex<<16) + botIndex
}

и здесь он работает над небольшим набором входных данных:

scala> medianFinder(List(1,123,12345,1234567,123456789))
res18: Int = 12345

Если у вас хранятся 64-битные целые числа, вы можете использовать одну и ту же стратегию за 4 прохода.

12 голосов
/ 26 августа 2010

Вы можете использовать алгоритм Медианы медиан .

4 голосов
/ 26 августа 2010

Если файл в текстовом формате, вы можете разместить его в памяти, просто преобразовав вещи в целые числа при их чтении, поскольку целое число, хранящееся в символах, может занимать больше места, чем целое число, хранящееся как целое числов зависимости от размера целых чисел и типа текстового файла.РЕДАКТИРОВАТЬ: Вы редактировали свой оригинальный вопрос;Теперь я вижу, что вы не можете прочитать их в память, см. Ниже.

Если вы не можете прочитать их в память, вот что я придумал:

  1. Узнайте, сколько у вас целых чисел.Вы можете знать это с самого начала.Если нет, то это займет всего один проход через файл.Допустим, это S.

  2. Используйте 2 ГБ памяти, чтобы найти x самых больших целых чисел (сколько бы вы ни подходили).Вы можете сделать один проход по файлу, сохранив x по величине в отсортированном списке какого-либо рода, отбрасывая остальные по мере продвижения.Теперь вы знаете x-е по величине целое число.Вы можете отказаться от всего этого, кроме x-го наибольшего, которое я назову x1.

  3. Сделайте еще один проход, найдя следующие x наибольших целых чисел меньше x1, наименьшее из которых - x2.

  4. Я думаю, вы можете видеть, куда я иду с этим.Через несколько проходов вы прочитаете (S / 2) -ое по величине целое число (вам нужно будет отследить, сколько целых чисел вы нашли), что является вашей медианой.Если S четное, то вы будете усреднять два в середине.

3 голосов
/ 26 августа 2010
  1. Выполните на диске внешнюю сортировку слиянием для файла, чтобы отсортировать целые числа (считая их, если это еще не известно).
  2. Как только файл отсортирован, ищите среднее число (нечетный регистр) или усредните два средних числа (четный регистр) в файле, чтобы получить медиану.

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

Учитывая n = количество целых чисел в исходном файле:

  • Продолжительность: O(nlogn)
  • Память: O(1), регулируемая
  • Диск: O(n)
3 голосов
/ 26 августа 2010

Сделайте проход по файлу и найдите количество целых и минимальное и максимальное целочисленное значение.

Возьмите среднюю точку min и max и получите число, min и max для значений по обе стороны от средней точки - поснова читая файл.

количество разделов> count => медиана лежит внутри этого раздела.

Повторите для раздела, учитывая размер «разделов влево» (легко поддерживать), а также отслеживание min = max.

Я уверен, что это будет работать и для произвольного числа разделов.

1 голос
/ 26 августа 2010

Проверьте метод Торбена здесь: http://ndevilla.free.fr/median/median/index.html. Он также имеет реализацию в C в нижней части документа.

0 голосов
/ 14 декабря 2018

Мне также задавали тот же вопрос, и я не мог сказать точный ответ, поэтому после интервью я просмотрел несколько книг об интервью, и вот что я нашел в книге интервью Cracking The Coding.

Пример: числа генерируются случайным образом и сохраняются в (расширяющемся) массиве.Как бы вы могли отслеживать медиану?

Наш мозговой штурм структуры данных может выглядеть следующим образом:

• Связанный список?Возможно нет.Связанные списки, как правило, не очень хорошо подходят для доступа и сортировки номеров.

• Массив?Возможно, но у вас уже есть массив.Не могли бы вы как-то сохранить элементы отсортированными?Это, наверное, дорого.Давайте подождем и вернемся к нему, если это необходимо.

• Двоичное дерево?Это возможно, поскольку двоичные деревья довольно хорошо справляются с упорядочением.На самом деле, если бинарное дерево поиска идеально сбалансировано, вершина может быть медиана.Но будьте осторожны - если есть четное количество элементов, медиана на самом деле является средним из двух средних элементов.Средние два элемента не могут быть оба наверху.Вероятно, это работоспособный алгоритм, но давайте вернемся к нему.

• Куча?Куча действительно хороша в базовом упорядочении и отслеживании макс и мин.Это действительно интересно - если бы у вас было две кучи, вы могли бы отслеживать большую половину и меньшую половину элементов.Большая половина хранится в минимальной куче, так что самый маленький элемент в большей половине находится в корне. Меньшая половина хранится в максимальной куче, так что самый большой элемент меньшей половины находится в корне.Теперь, с этими структурами данных, у вас есть потенциальные медианные элементы в корнях.Если кучи уже не одного размера, вы можете быстро «перебалансировать» кучи, вытолкнув элемент из одной кучи и вытолкнув его на другую.

Обратите внимание, что чем больше проблем вы делаете, тем болееВаш инстинкт, на какую структуру данных будет применяться.Вы также разработаете более точно настроенный инстинкт относительно того, какой из этих подходов является наиболее полезным.

0 голосов
/ 17 ноября 2017

Вот алгоритм, описанный @Rex Kerr, реализованный в Java.

/**
 * Computes the median.
 * @param arr Array of strings, each element represents a distinct binary number and has the same number of bits (padded with leading zeroes if necessary)
 * @return the median (number of rank ceil((m+1)/2) ) of the array as a string
 */
static String computeMedian(String[] arr) {

    // rank of the median element
    int m = (int) Math.ceil((arr.length+1)/2.0);

    String bitMask = "";
    int zeroBin = 0;

    while (bitMask.length() < arr[0].length()) {

        // puts elements which conform to the bitMask into one of two buckets
        for (String curr : arr) {
            if (curr.startsWith(bitMask))
                if (curr.charAt(bitMask.length()) == '0')
                    zeroBin++;
        }

        // decides in which bucket the median is located
        if (zeroBin >= m)
            bitMask = bitMask.concat("0");
        else {
            m -= zeroBin;
            bitMask = bitMask.concat("1");
        }

        zeroBin = 0;
    }

    return bitMask;
}

Некоторые тестовые примеры и обновления алгоритма можно найти здесь .

0 голосов
/ 26 августа 2010

Мое предположение, что вероятностная медиана медиан будет самой быстрой.Рецепт:

  1. Возьмите следующий набор из N целых чисел (N должно быть достаточно большим, скажем, 1000 или 10000 элементов)
  2. Затем вычислите медиану этих целых чисел и назначьте ее переменной X_new.
  3. Если итерация не первая - вычислите медиану двух медиан:

    X_global = (X_global + X_new) / 2

  4. Когда вы увидите, что X_global колеблется незначительно - это означает, что вы нашли приблизительную медиану данных.

Но есть некоторые примечания:

  • вопросвозникает - допустима медиана ошибки или нет.
  • целые числа должны быть распределены случайным образом равномерным образом, чтобы решение работало

РЕДАКТИРОВАТЬ: Я игралнемного с этим алгоритмом, немного изменил идею - в каждой итерации мы должны суммировать X_new с уменьшением веса, например:

X_global = k * X_global + (1.-k) * X_new:

k из [0.5 .. 1.] и увеличивается в каждой итерации.

Нужно сделатьвычисление медианы, чтобы быстро сходиться к некоторому числу за очень небольшое количество итераций.Так что очень приблизительная медиана (с большой ошибкой) найдена между 100000000 элементами массива всего за 252 итерации !!! Проверьте этот эксперимент на C:

#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#define ARRAY_SIZE 100000000
#define RANGE_SIZE 1000

// probabilistic median of medians method
// should print 5000 as data average
// from ARRAY_SIZE of elements
int main (int argc, const char * argv[]) {
    int iter = 0;
    int X_global = 0;
    int X_new = 0;
    int i = 0;
    float dk = 0.002;
    float k = 0.5;
    srand(time(NULL));

    while (i<ARRAY_SIZE && k!=1.) {
        X_new=0;
        for (int j=i; j<i+RANGE_SIZE; j++) {
            X_new+=rand()%10000 + 1;
        }
        X_new/=RANGE_SIZE;

        if (iter>0) {
            k += dk;
            k = (k>1.)? 1.:k;
            X_global = k*X_global+(1.-k)*X_new;

        }
        else {
            X_global = X_new;
        }

        i+=RANGE_SIZE+1;
        iter++;
        printf("iter %d, median = %d \n",iter,X_global);
    }

    return 0;

}

Кажется, я говорю о Oppsзначит, не медиана.Если это так, а вам нужна именно медиана, а не значит - игнорируйте мой пост.В любом случае среднее значение и медиана являются очень взаимосвязанными понятиями.

Удачи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...