Я пытаюсь сравнить элементы двух карт с помощью функции сравнения. Результирующее решение должно вернуть список пар ключ-значение из первой карты, которые похожи на некоторые значения из второй (сравниваются только значения). Ввод выглядит следующим образом:
let a = fromList [(0,15),(1,150),(2,39),(3,18)]
let b = fromList [(0,151),(1,39),(2,0),(3,1)]
-- Function that performs comparison. Returns True if items are similar
isSimilar :: Int -> Int -> Bool
isSimilar a b = abs (b - a) <= 1
Результат должен быть следующим:
result = [(1,150),(2,39)]
Я закончил со следующей реализацией:
import qualified Data.IntMap.Strict as M
import Test.Hspec
type Elem = M.IntMap Int
-- Function that does all the work
getSimilarities :: Elem -> Elem -> [(Int,Int)]
getSimilarities cur intersected = M.foldrWithKey (\k p lst -> if M.null (check p)
then lst
else (k, p) : lst) [] cur
where check pnt = M.filter (isSimilar pnt) intersected
-- Comparator that helps to filter out unnecessary values
isSimilar :: Int -> Int -> Bool
isSimilar a b = abs (b - a) <= 1
-- Main function that runs the test
main :: IO ()
main = hspec spec
spec :: Spec
spec = do
it "Get similarities test" $ do
let xs = [3,31,0,151,25,120]
let mp = M.fromList $ zip [0..(length xs)] xs
let xs' = [5,17,32,150,9,90]
let mp' = M.fromList $ zip [0..(length xs')] xs'
let expected = [(1, 31),(3, 151)]
getSimilarities mp mp' `shouldBe` expected
Однако мне не очень нравится функция «проверить», так как мне не нужно формировать там новую карту, мне нужно просто проверить значение. Не могли бы вы предложить, есть ли более эффективный и производительный способ сделать то, что мне нужно? Спасибо.
lookupGE
, который можно использовать для поиска похожих ключей), но для значений у нас ничего нет. Возможно, вам следует построить набор значений второй карты (стоимость: N*log N), а затем использовать этот набор для выполнения множества поисков (N поисков, журнал N каждый). Это может быть лучше, чем подход N^2, которому вы сейчас следуете. - person chi   schedule 14.01.2020isSimilar
. Это занимает O(n^2) времени. Если вы используете контейнеры ordered и можете предположить, что пары кандидатов имеют форму(x+/-d, x+/-d)
для некоторой константыd
(в вашем случае здесьd == 1
), вы можете построить список кандидатов только за O(n lg n) время. (Я думаю, что во втором чтении это то, что предлагает @chi.) - person chepner   schedule 14.01.2020lookupGE
и друзей, быстро. - person chi   schedule 14.01.2020IntSet
(это нужно сделать только один раз). Затем используйтеIntSet.lookupGE
для выполненияcheck
. Я не могу гарантировать, что это будет быстрее, но может быть, особенно для больших карт/наборов. - person chi   schedule 14.01.2020isSimilar (-9223372036854775388) 420
оценивается какTrue
на 64-битных машинах (иisSimilar (-2147483228) 420
на 32-битных машинах). Мой ответ предполагает, что это просто ошибка, и я не буду отмечать их как похожие; если это действительно предполагаемое поведение, все становится намного сложнее. - person Daniel Wagner   schedule 14.01.2020