Давайте нарисуем пример сетки для вашей проблемы, чтобы помочь подумать об этом.Вот примерный график f
для каждого x
и y
.Он монотонен в каждом аргументе, что является интересным ограничением, с которым мы могли бы сделать что-то умное.
+------- x --------->
| 0 0 1 1 1 2
| 0 1 1 2 2 4
y 1 1 3 4 6 6
| 1 2 3 6 6 7
| 7 7 7 7 7 7
v
Поскольку мы ничего не знаем о свойстве, мы не можем сделать лучше, чемперечислить значения в диапазоне f
в порядке убывания.Вопрос в том, как сделать это эффективно.
Первое, что приходит на ум, - это пройтись по нему, как график, начинающийся с правого нижнего угла.Вот моя попытка:
import Data.Maybe (listToMaybe)
maximise :: (Ord b, Num b) => (Int -> Int -> b) -> (b -> Bool) -> Int -> Int -> Maybe b
maximise f p xLim yLim =
listToMaybe . filter p . map (negate . snd) $
enumIncreasing measure successors (xLim,yLim)
where
measure (x,y) = negate $ f x y
successors (x,y) = [ (x-1,y) | x > 0 ] ++ [ (x,y-1) | y > 0 ] ]
Сигнатура не такая общая, как могла бы быть (Num
не нужно, но мне нужно было отменить функцию меры, потому что enumIncreasing возвращает скорее растущий, чем убывающий список - я мог бы также сделать это с помощью обертки нового типа).
Используя эту функцию, мы можем найти наибольшее нечетное число, которое можно записать как произведение двух чисел <=
100:
ghci> maximise (*) odd 100 100
Just 9801
Я написал enumIncreasing , используя meldable-heap
для взлома, чтобы решить эту проблему, но она довольно общая.Вы можете настроить вышеупомянутое, чтобы добавить дополнительные ограничения на домен и т. Д.