Понимание алгоритма "медиана медиан" - PullRequest
55 голосов
/ 29 февраля 2012

Я хочу понять алгоритм «медиана медиан» на следующем примере:

У нас есть 45 различных чисел, разделенных на 9 групп по 5 элементов в каждом.

48 43 38 33 28 23 18 13 8

49 44 39 34 29 24 19 14 9 

50 45 40 35 30 25 20 15 10

51 46 41 36 31 26 21 16 53

52 47 42 37 32 27 22 17 54
  1. Первый шаг - сортировка каждой группы (в этом случае они уже отсортированы)
  2. Второй шаг рекурсивно, найдите «истинную» медиану медиан (50 45 40 35 30 25 20 15 10), т. Е. Набор будет разделен на2 группы:

    50 25
    
    45 20 
    
    40 15
    
    35 10
    
    30
    

    сортировка этих 2 групп

    30 10
    
    35 15 
    
    40 20
    
    45 25
    
    50
    

медианы равны 40 и 15 (в случае, если числа равны, мы взяли левую медиану), таквозвращаемое значение равно 15, однако «истинная» медиана медиан (50 45 40 35 30 25 20 15 10) равна 30, более того, есть 5 элементов, меньших 15, что значительно меньше 30% из 45, упомянутых в википедии

и так T(n) <= T(n/5) + T(7n/10) + O(n) не удается.

Кстати, в примере из Википедии я получаю результат рекурсии как 36. Однако истинная медиана равна 47.

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

Ответы [ 2 ]

33 голосов
/ 29 февраля 2012

Проблема в том, что вы говорите, чтобы найти истинную медиану медиан.В вашем примере у вас были следующие медианы:

50 45 40 35 30 25 20 15 10

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

50 45 40 35 30

и

25 20 15 10

с последующим нахождением медианы каждого блока.Вместо этого алгоритм медианы медиан будет рекурсивно вызывать себя для полного набора данных 50 45 40 35 30 25 20 15 10.Внутренне это разделит группу на блоки по пять и рассортирует их и т. Д., Но делает это для определения точки разделения на этапе разделения, и именно на этом этапе разделения рекурсивный вызов найдет истинную медиану медиан., который в данном случае будет равен 30. Если вы используете 30 в качестве медианы в качестве шага разделения в исходном алгоритме, вы действительно получите очень хорошее разделение по мере необходимости.

Надеюсь, это поможет!

26 голосов
/ 22 января 2015

Вот псевдокод для алгоритма медианы медиан (немного изменен в соответствии с вашим примером). Псевдокод в википедии не отображает внутреннюю работу вызова функции selectIdx.

Я добавил комментарии к коду для объяснения.

// L is the array on which median of medians needs to be found.
// k is the expected median position. E.g. first select call might look like:
// select (array, N/2), where 'array' is an array of numbers of length N

select(L,k)
{

    if (L has 5 or fewer elements) {
        sort L
        return the element in the kth position
    }

    partition L into subsets S[i] of five elements each
        (there will be n/5 subsets total).

    for (i = 1 to n/5) do
        x[i] = select(S[i],3)

    M = select({x[i]}, n/10)

    // The code to follow ensures that even if M turns out to be the
    // smallest/largest value in the array, we'll get the kth smallest
    // element in the array

    // Partition array into three groups based on their value as
    // compared to median M

    partition L into L1<M, L2=M, L3>M

    // Compare the expected median position k with length of first array L1
    // Run recursive select over the array L1 if k is less than length
    // of array L1
    if (k <= length(L1))
        return select(L1,k)

    // Check if k falls in L3 array. Recurse accordingly
    else if (k > length(L1)+length(L2))
        return select(L3,k-length(L1)-length(L2))

    // Simply return M since k falls in L2
    else return M

}

Если взять ваш пример:

Функция медианы медиан будет вызываться по всему массиву из 45 элементов, например (с k = 45/2 = 22):

median = select({48 49 50 51 52 43 44 45 46 47 38 39 40 41 42 33 34 35 36 37 28 29 30 31 32 23 24 25 26 27 18 19 20 21 22 13 14 15 16 17 8 9 10 53 54}, 45/2)
  1. При первом вызове M = select({x[i]}, n/10) массив {x[i]} будет содержать следующие числа: 50 45 40 35 30 20 15 10. В этом вызове n = 45, и, следовательно, вызов функции выбора будет M = select({50 45 40 35 30 20 15 10}, 4)

  2. Во второй раз, когда вызывается M = select({x[i]}, n/10), массив {x[i]} будет содержать следующие числа: 40 20. В этом вызове n = 9 и, следовательно, вызов будет M = select({40 20}, 0). Этот вызов выбора вернется и присвоит значение M = 20.

    Теперь, когда у вас возникли сомнения, мы теперь разбиваем массив L вокруг M = 20 на k = 4.

    Запомните массив L здесь: 50 45 40 35 30 20 15 10.

    Массив будет разбит на L1, L2 и L3 в соответствии с правилами L1 < M, L2 = M и L3 > M. Следовательно:
    L1: 10 15
    L2: 20
    L3: 30 35 40 45 50

    С k = 4 оно больше length(L1) + length(L2) = 3. Следовательно, поиск будет продолжен следующим рекурсивным вызовом:
    return select(L3,k-length(L1)-length(L2))

    что переводится как:
    return select({30 35 40 45 50}, 1)

    который вернет 30 в результате. (поскольку L имеет 5 или меньше элементов, следовательно, он вернет элемент в kth, то есть в 1-й позиции отсортированного массива, равной 30).

Теперь M = 30 будет получено при первом вызове функции select по всему массиву из 45 элементов, и та же логика разделения, которая разделяет массив L вокруг M = 30, будет применяться для окончательного получения медианы медиан.

Уф! Я надеюсь, что был достаточно многословен и ясен, чтобы объяснить алгоритм медианы медиан.

...