Поиск N ближайших номеров
У вас есть двумерный массив, например -
a[0] = [ 0 , 4 , 9 ]
a[1] = [ 2 , 6 , 11 ]
a[2] = [ 3 , 8 , 13 ]
a[3] = [ 7 , 12 ]
Необходимо выбрать один элемент из каждого из подматриц таким образом, чтобы результирующий набор чисел был самым близким, то есть разница между наивысшим числом и наименьшим числом в наборе минимальна.
Ответ на сказанное выше будет = [ 9 , 6 , 8 , 7 ]
.
Сделали алгоритм, но не чувствуем его хорошего.
Что было бы эффективным алгоритмом для этого с точки зрения сложности времени и пространства?
EDIT - Мой алгоритм (в python) -
INPUT - Dictionary : table{}
OUTPUT - Dictionary : low_table{}
#
N = len(table)
for word_key in table:
for init in table[word_key]:
temp_table = copy.copy(table)
del temp_table[word_key]
per_init = copy.copy(init)
low_table[init]=[]
for ite in range(N-1):
min_val = 9999
for i in temp_table:
for nums in temp_table[i]:
if min_val > abs(init-nums):
min_val = abs(init-nums)
del_num = i
next_num = nums
low_table[per_init].append(next_num)
init = (init+next_num)/2
del temp_table[del_num]
lowest_val = 99
lowest_set = []
for x in low_table:
low_table[x].append(x)
low_table[x].sort()
mini = low_table[x][-1]-low_table[x][0]
if mini < lowest_val:
lowest_val = mini
lowest_set = low_table[x]
print lowest_set
Ответы
Ответ 1
собрать все значения для создания одной упорядоченной последовательности, причем каждый элемент помечен массивом, из которого он пришел:
0 (0), 2 (1), 3 (2), 4 (0), 6 (1),... 12 (3), 13 (2)
затем создайте окно через них, начиная с первого (0 (0)) и заканчивая его в первой позиции, которая заставляет окно охватывать все массивы (0 (0) → 7 (3))
тогда сверните это окно, увеличив начало окна на единицу и увеличив его до конца, пока не появится окно, которое охватывает все элементы.
затем снова сверните его: (2 (1), 3 (2), 4 (0),... 7 (3)) и т.д.
на каждом шаге следить за разницей между самыми большими и наименьшими. В конце концов вы найдете ту, которая находится в самом маленьком окне. У меня такое чувство, что в худшем случае это O (n ^ 2), но это просто предположение.
Ответ 2
Дословная версия алгоритма Haskell с помощью whiterook6:
import Data.List (minimumBy,sortBy)
import qualified Data.Map as M (fromList,toList,adjust,lookup)
f arrays = g (zip arrays [1..]) [] h [(100,0),(0,0)] where
n = length arrays
h = (M.fromList $ zip [1..n] (repeat 0))
g arrays sequence indexes best
| any ((==0) . snd) (M.toList indexes) =
g (foldr comb [] arrays) (next:sequence) (M.adjust (+1) ind indexes) best
| otherwise =
if null (drop 1 arrays)
then best'
else g (foldr comb [] arrays)
(next:init trimmedSequence)
(foldr (M.adjust (+1)) h (ind : (map snd $ init trimmedSequence)))
best'
where
best' = minimumBy comp [best,trimmedSequence]
[email protected](val,ind) = minimum $ map (\(arr,i) -> (head arr,i)) arrays
comb [email protected](seq,i) b = if i == ind
then if null (drop 1 seq)
then b
else (drop 1 seq,i) : b
else a : b
comp a b = compare (fst (head a) - fst (last a)) (fst (head b) - fst (last b))
trimSequence [] _ = []
trimSequence (x:xs) h
| any ((==0) . snd) (M.toList h) =
case M.lookup (snd x) h of
Just 0 -> x : trimSequence xs (M.adjust (+1) (snd x) h)
otherwise -> trimSequence xs h
| otherwise = []
trimmedSequence = trimSequence sequence (M.fromList $ zip [1..n] (repeat 0))
Вывод:
*Main> f [[0,4,9],[2,6,11],[3,8,13],[7,12]]
[(9,1),(8,3),(7,4),(6,2)]
Ответ 3
У меня есть вариант алгоритма whiterook, который, по моему мнению, проще (и, кроме начального шага сортировки, он более четко O (N)).
Итерация minval через все значения по порядку. При этом мы сохраняем индекс наименьшего значения в каждом массиве, который больше или равен minval). Мы также сохраняем максимальное значение элементов в этом индексе в своих соответствующих массивах.
Когда мы рассмотрели какой-то конкретный minval, мы можем увеличить индекс для всех массивов, содержащих minval, и при необходимости обновить maxval.
Вот реализация. Обратите внимание, что (за исключением начальной сортировки) это O (N), потому что внутренний цикл выполняется не более одного раза за каждое значение в каждом массиве.
def minspan(aa):
allnums = sorted(set(sum(aa, [])))
ntoi = dict((x, []) for x in allnums)
for i in xrange(len(aa)):
for x in aa[i]:
ntoi[x].append(i)
indexes = [0] * len(aa)
maxval = max(a[0] for a in aa)
best = None
for minval in allnums:
if best is None or best[0] > maxval - minval:
best = maxval - minval, minval, maxval
for i in ntoi[minval]:
indexes[i] += 1
if indexes[i] >= len(aa[i]):
return [min(x for x in a if x >= best[1]) for a in aa]
maxval = max(maxval, aa[i][indexes[i]])
aa = [[0,4,9], [2,6,11], [3,8,13], [7,12]]
print minspan(aa)