N-королевы в Haskell без обхода списка

Можно использовать любую новую функцию C# 3.0, которая обрабатывается компилятором путем испускания совместимого с 2.0 IL и не ссылается ни на один из новых 3,5 блоков:

  • Лямбды (используемый в качестве Func<..>, не Expression>)
  • Дополнительные методы (путем объявления пустой Системы. Время выполнения. CompilerServices. ExtensionAttribute)
  • свойства Automatic
  • Объектные Инициализаторы
  • Инициализаторы Набора
  • LINQ к Объектам (путем реализации IEnumerable< T> дополнительные методы, см. LinqBridge)

17
задан hugomg 20 August 2009 в 03:27
поделиться

3 ответа

В общем, вам, вероятно, придется платить налог за сложность O (log n) за функциональную неразрушающую реализацию, иначе вам придется смягчиться и использовать (IO | ST | STM) UArray .

Строгие чистые языки, возможно, должны будут заплатить налог O (log n) за нечистый язык, который может писать в ссылки, реализуя ссылки через структуру, подобную карте; ленивые языки могут иногда уклоняться от этого налога, хотя в любом случае нет доказательств того, достаточно ли дополнительной силы, предлагаемой ленью, чтобы всегда уклоняться от этого налога - даже если есть серьезные подозрения, что лень недостаточно сильна.

В этом случае трудно увидеть механизм, с помощью которого можно было бы использовать лень, чтобы избежать уплаты справочного налога. А также, ведь именно поэтому у нас в первую очередь монада ST . ;)

Тем не менее, вы могли бы исследовать, можно ли использовать какую-нибудь молнию по диагонали доской для использования локальности обновлений - использование локальности в застежке-молнии - это распространенный способ попытаться отбросить логарифмический член.

5
ответ дан 30 November 2019 в 14:00
поделиться

Вероятно, наиболее простым способом было бы использовать UArray (Int, Int) Bool для записи безопасных / небезопасных битов. Хотя для копирования это O (n 2 ), для малых значений N это самый быстрый из доступных методов.

Для больших значений N есть три основных варианта:

  • Data.DiffArray удаляет накладные расходы на копирование до тех пор, пока вы больше никогда не будете использовать старые значения после их изменения . То есть, если вы всегда выбрасываете старое значение массива после его изменения, модификация будет O (1). Если, однако, вы получите доступ к старому значению массива позже (даже только для чтения), O (N 2 ) оплачивается полностью.
  • Data.Map и Data.Set разрешает O (lg n) модификации и поиск. Это меняет алгоритмическую сложность, но часто бывает достаточно быстро.
  • Data.Array.ST ' s STUArray s (Int, Int) Bool предоставит вам императивные массивы, позволяющие реализовать алгоритм классическим (нефункциональным) способом.
6
ответ дан 30 November 2019 в 14:00
поделиться

Основная потенциальная проблема этого подхода состоит в том, что массивы диагоналей необходимо изменять каждый раз, когда ставится ферзь. Небольшое улучшение постоянного времени поиска диагоналей не обязательно стоит дополнительной работы по постоянному созданию новых модифицированных массивов.

Но лучший способ узнать настоящий ответ - это попробовать, поэтому я немного поигрался и пришел со следующим:

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 он имеет примерно такое же время выполнения, что и версия, о которой идет речь.

3
ответ дан 30 November 2019 в 14:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: