找到N个最接近的数字

时间:2013-07-16 02:26:07

标签: python algorithm

有一个二维数组,如 -

a[0] = [ 0 , 4 , 9 ]
a[1] = [ 2 , 6 , 11 ]
a[2] = [ 3 , 8 , 13 ]
a[3] = [ 7 , 12 ]

需要从每个子数组中选择一个元素,使得结果数组最接近,即集合中最高数字和最低数字之间的差异最小。

以上答案为= [ 9 , 6 , 8 , 7 ]

已经制定了算法,但感觉不是很好。 在时间和空间复杂性方面,这样做的有效算法是什么?

编辑 - 我的算法(在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

3 个答案:

答案 0 :(得分:11)

收集所有值以创建单个有序序列,每个元素都标记有来自的数组: 0(0),2(1),3(2),4(0),6(1),... 12(3),13(2)

然后在它们之间创建一个窗口,从第一个开始(0(0))并在第一个位置结束,使窗口跨越所有数组(0(0) - > 7(3))

然后通过将窗口的开头递增1来滚动此窗口,并递增窗口的末尾,直到再次有一个覆盖所有元素的窗口。

然后再次滚动:(2(1),3(2),4(0),... 7(3)),依此类推。

在每一步跟踪最大和最小之间的差异。最终你会发现窗口最小的那个。我觉得在最坏的情况下这是O(n ^ 2),但这只是猜测。

答案 1 :(得分:2)

whiterook6的好算法的一个罗嗦的Haskell版本:

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]
     next@(val,ind) = minimum $ map (\(arr,i) -> (head arr,i)) arrays
     comb a@(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)]

答案 2 :(得分:1)

我有一个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)