Можно использовать любую новую функцию C# 3.0, которая обрабатывается компилятором путем испускания совместимого с 2.0 IL и не ссылается ни на один из новых 3,5 блоков:
Func<..>
, не Expression>
) В общем, вам, вероятно, придется платить налог за сложность O (log n)
за функциональную неразрушающую реализацию, иначе вам придется смягчиться и использовать (IO | ST | STM) UArray
.
Строгие чистые языки, возможно, должны будут заплатить налог O (log n)
за нечистый язык, который может писать в ссылки, реализуя ссылки через структуру, подобную карте; ленивые языки могут иногда уклоняться от этого налога, хотя в любом случае нет доказательств того, достаточно ли дополнительной силы, предлагаемой ленью, чтобы всегда уклоняться от этого налога - даже если есть серьезные подозрения, что лень недостаточно сильна.
В этом случае трудно увидеть механизм, с помощью которого можно было бы использовать лень, чтобы избежать уплаты справочного налога. А также, ведь именно поэтому у нас в первую очередь монада ST
. ;)
Тем не менее, вы могли бы исследовать, можно ли использовать какую-нибудь молнию по диагонали доской для использования локальности обновлений - использование локальности в застежке-молнии - это распространенный способ попытаться отбросить логарифмический член.
Вероятно, наиболее простым способом было бы использовать UArray (Int, Int) Bool
для записи безопасных / небезопасных битов. Хотя для копирования это O (n 2 ), для малых значений N это самый быстрый из доступных методов.
Для больших значений N есть три основных варианта:
STUArray s (Int, Int) Bool
предоставит вам императивные массивы, позволяющие реализовать алгоритм классическим (нефункциональным) способом. Основная потенциальная проблема этого подхода состоит в том, что массивы диагоналей необходимо изменять каждый раз, когда ставится ферзь. Небольшое улучшение постоянного времени поиска диагоналей не обязательно стоит дополнительной работы по постоянному созданию новых модифицированных массивов.
Но лучший способ узнать настоящий ответ - это попробовать, поэтому я немного поигрался и пришел со следующим:
import Data.Array.IArray (array, (//), (!))
import Data.Array.Unboxed (UArray)
import Data.Set (Set, fromList, toList, delete)
-- contains sets of unoccupied columns and lookup arrays for both diagonals
data BoardState = BoardState (Set Int) (UArray Int Bool) (UArray Int Bool)
-- an empty board
board :: Int -> BoardState
board n
= BoardState (fromList [0..n-1]) (truearr 0 (2*(n-1))) (truearr (1-n) (n-1))
where truearr a b = array (a,b) [(i,True) | i <- [a..b]]
-- modify board state if queen gets placed
occupy :: BoardState -> (Int, Int) -> BoardState
occupy (BoardState c s d) (a,b)
= BoardState (delete b c) (tofalse s (a+b)) (tofalse d (a-b))
where tofalse arr i = arr // [(i, False)]
-- get free fields in a row
freefields :: BoardState -> Int -> [(Int, Int)]
freefields (BoardState c s d) a = filter freediag candidates
where candidates = [(a,b) | b <- toList c]
freediag (a,b) = (s ! (a+b)) && (d ! (a-b))
-- try all positions for a queen in row n-1
place :: BoardState -> Int -> [[(Int, Int)]]
place _ 0 = [[]]
place b n = concatMap place_ (freefields b (n-1))
where place_ p = map (p:) (place (occupy b p) (n-1))
-- all possibilities to place n queens on a n*n board
queens :: Int -> [[(Int, Int)]]
queens n = place (board n) n
Это работает и для n = 14 примерно на 25% быстрее, чем версия, которую вы упомянули. Основное ускорение достигается за счет использования массивов без упаковки , рекомендованных bdonian . С обычным Data.Array
он имеет примерно такое же время выполнения, что и версия, о которой идет речь.