Найти подмножество с элементами, которые расположены дальше всего друг от друга

У меня есть вопрос интервью, который я не могу понять. Учитывая массив размера N, найдите подмножество размера k таким образом, чтобы элементы в подмножестве были наиболее удалены друг от друга. Другими словами, максимизируйте минимальное попарное расстояние между элементами.

Example:

Array = [1,2,6,10]
k = 3

answer = [1,6,10]

Для метода brutforce необходимо найти все подмножества размера k, которые экспоненциальны во время выполнения.

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

  • Возьмите первый и последний элементы
  • найти разницу между ними (в этом случае 10-1) и разделить это на k ((10-1)/3 = 3)
  • перемещайте 2 указателя внутрь с обоих концов, выбирая элементы, которые +/- 3 из вашего предыдущего выбора. Итак, в этом случае вы начинаете с 1 и 10 и находите самые близкие элементы к 4 и 7. Это будет 6.

Это основано на интуиции, что элементы должны быть как можно более равномерно распределены. Я не знаю, как доказать, что это работает/не работает. Если кто-то знает, как или лучше алгоритм, разделите. Спасибо!

Ответ 1

Это может быть разрешено в полиномиальное время с использованием DP.

Первый шаг, как вы сказали, сортирует список A. Пусть X [i, j] является решением для выбора j элементов из первых я элементов A.

Теперь X [i + 1, j + 1] = max (min (X [k, j], A [i + 1] -A [k])) над k <= i.

Я оставлю шаг инициализации и запоминание шага подмножества, над которым вы должны работать.

В вашем примере (1,2,6,10) он работает следующим образом:

    1    2   6   10
1   -    -   -    -
2   -    1   5    9
3   -    -   1    4
4   -    -   -    1

Ответ 2

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

Я не могу придумать полиномиальный алгоритм для решения этой проблемы, поэтому я бы предложил один из двух вариантов.

Один из них заключается в использовании алгоритма поиска, стиля ветки и привязки, поскольку у вас есть хорошая эвристика: верхняя граница для любого решения - это минимальный размер промежутка между элементами, выбранными до сих пор, поэтому первый (равномерно распределенные ячейки, как вы сказали) могут дать вам хорошую базовую линию, которая сразу же сократит большинство ветвей. Это будет отлично работать при меньших значениях k, хотя наихудшая производительность - O(N^k).

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

Последнее решение может быть быстрее, когда массив большой, а k также относительно велик, но элементы в массиве относительно малы. Если они связаны некоторым значением M, этот алгоритм будет принимать время O(N*M) или, при небольшом улучшении, O(N*log(M)), где N - размер массива.

Как указывает в своем ответе Евгений Клюев, есть также хорошая верхняя граница максимального попарного расстояния, которое может быть использовано в любом из этих алгоритмов. Таким образом, сложность последнего на самом деле O(N*log(M/k)).

Ответ 3

Я предполагаю, что ваш набор упорядочен. Если нет, мой ответ будет немного изменен.

Let suppose you have an array X = (X1, X2, ..., Xn)

Energy(Xi) = min(|X(i-1) - Xi|, |X(i+1) - Xi|), 1 < i <n

j <- 1
while j < n - k do
    X.Exclude(min(Energy(Xi)), 1 < i < n)
    j <- j + 1
    n <- n - 1
end while

Ответ 4

$length = length($array);
sort($array); //sorts the list in ascending order
$differences = ($array << 1) - $array; //gets the difference between each value and the next largest value
sort($differences);  //sorts the list in ascending order
$max = ($array[$length-1]-$array[0])/$M; //this is the theoretical max of how large the result can be
$result = array();
for ($i = 0; i < $length-1; $i++){
    $count += $differences[i];
    if ($length-$i == $M - 1 || $count >= $max){ //if there are either no more coins that can be taken or we have gone above or equal to the theoretical max, add a point
        $result.push_back($count);
        $count = 0;
        $M--;
    }
}
return min($result)

Для людей без кода: сортируйте список, найдите различия между двумя последовательными элементами, сортируйте этот список (в порядке возрастания), затем пропустите его, суммируя последовательные значения, пока вы не пройдете теоретический максимум или нет arent осталось достаточно элементов; затем добавьте это значение в новый массив и продолжайте, пока не попадете в конец массива. затем верните минимум вновь созданного массива.

Это всего лишь быстрый проект. С быстрым взглядом любая операция здесь может быть выполнена в линейном времени (сортировка по методу radix для сортировки).

Например, с 1, 4, 7, 100 и 200 и M = 3 мы получаем:

$differences = 3, 3, 93, 100
$max = (200-1)/3 ~ 67
then we loop:
$count = 3, 3+3=6, 6+93=99 > 67 so we push 99
$count = 100 > 67 so we push 100
min(99,100) = 99

Это простое упражнение, чтобы преобразовать это в заданное мною решение, которое я оставляю читателю (P.S. после того, как все время читал это в книге, я всегда хотел сказать это: P)