我遇到与this类似的问题。但是,它涉及很多更新,我不确定IxSet是否是解决方案。
基本上我正在编写一个优化仓库布局的应用程序。没有数据库或任何东西;它只是简单的数据来操作并在最后生成一个文件。仓库由不同尺寸的货架制成;货架包含不同尺寸的箱子;我们的目标是找到最好的安排(或者至少是一个好的安排),把盒子放在哪里以便它们都适合。
基本模型(实际上并不重要)是:
data Dimension = Dimension {length::Double, width::Double, height::Double}
data Box = Box {boxId :: Int, boxDim:: Dimension }
data Shelf = Shelf {shelfId :: Int, shelfDim :: Dimension, postion :: ... }
现在,第一个问题是有一个货架图。有些架子背靠背。我需要知道它,因为可以调整一个架子的深度(以相反的方式修改后架)。 我还需要知道对面的架子和下一个架子。
对此进行建模的最有效方法是什么?
我的第一个想法是:
data Shelf = Shelf { ... , backShelf :: Maybe Shelf
, frontShelf :: Maybe Shelf
, nextShelf :: Maybe Shelf
}
现在,数据在Haskell中是不可变的,那么如何更新Shelf
?我的意思是,想象一下我有Shelf
的列表;如果我修改一个,那么我需要更新它的所有副本?
我的第二个想法是使用id代替:
newtype ShelfId = Int
data Shelf = Shelf { ..., backShelfId :: Maybe ShelfId ... }
或者我应该使用外部图表?像
这样的东西 back, front, next :: [(shelfId, shelfId)]
第二个问题如何模拟一个盒子属于一个架子的事实。
我想到的可能方法是:
data Box = Box { ..., shelf :: Shelf }
或
data Box = Box { ..., shelfId :: Maybe ShelfId }
或
data Shelf = Shelf { ..., boxes = [Box] }
或
data Shelf = Shelf { ..., boxIds = [BoxId] }
外部图表
boxToShelf :: [(BoxId, ShelfId)]
另外,正如我所说,这些操作涉及大量更新;我可能会逐个向每个架子添加盒子,这对于不可变数据来说效率非常低。
我认为另一种解决方案是使用STRef
或同等的:
data Box = { ..., shelf :: STRef Shelf }
感觉就像使用指针一样,所以这可能不是一个好主意。
这不是XY问题,也不是玩具问题。这是一个真正的仓库应用程序(100个货架和3000个盒子)。它需要能够绘制和加载现有数据(框及其位置)。优化只是其中的一小部分,可能是半手动的。
所以我的问题是关于表示可变对象之间的关系而不是基本的组合问题。
答案 0 :(得分:1)
了解有关优化算法如何工作的更多信息会有所帮助。
问题的核心是一个数据结构,它跟踪哪些盒子在哪个架子上(反之亦然)。我们称之为"配置"。
组合搜索算法在探索所有可能配置的空间时,从旧的配置中创建新配置。在任何时候,内存中都有几种配置 - 一种用于递归搜索的每个堆栈帧。
另一方面,像本地搜索这样的算法只有一个(全局)数据结构,它使用启发式或随机性进行变异,直到找到足够好的解决方案。
您最喜欢的算法是什么?
更新:请注意,可能没有一个表示适用于所有用例。要存储数据,您只需要从架子到盒子的地图。对于显示,您可能会发现它也很方便,也有反向映射(框 - >架子)。为了优化,您可能需要使用可变数组模拟问题以进行有效更新。
更新2:我会尝试使用presistent数据结构方法,看看它的工作情况。
type BoxId = Int
type ShelfId = Int
data Shelf = Shelf { ... just the dimensions of the shelf ... }
data Box = Box { ... just the dimensions of the box ... }
data Configuration = {
shelves :: IntMap Shelf, -- map from shelf id to shelf characterstics
boxes :: IntMap Box, -- map from box id to box characteristics
boxesForShelf :: IntMap [BoxId], -- map from shelf id to box ids
shelfForBox :: IntMap ShelfId -- map from box id to shelf id (or 0)
}
然后写下这个函数:
assignBox :: BoxId -> ShelfId -> Configuration -> Configuration
为了提高效率,您还可以编写如下内容:
assignBoxes :: [BoxId] -> ShelfId -> Configuration -> Configuration
并随意编写其他优化函数,以便通过您的用例在配置中进行其他批量更新。
你可能会发现在Box结构中使用BoxId很方便(并且同样适用于ShelfId / Shelf结构)......但你并不一定需要它。但是,使用单独的地图可以更好地处理盒子和架子之间的关系。
我将boxesForShelf
定义为IntMap [BoxId]
只是因为它听起来每个架子上只有少量的盒子。也许那是无效的。
希望这有帮助。
答案 1 :(得分:1)
为什么不使用persistent
?
我已经以cabal包的形式整理了一个示例应用程序,供您在https://github.com/gxtaillon/Shelves使用。
坚持遵循类型安全和简洁的指导原则, 声明性语法。其他一些不错的功能是:
- 数据库无关。 PostgreSQL,SQLite,MySQL和MongoDB都有一流的支持,支持Redis实验。
- 方便的数据建模。 Persistent允许您建模关系并以类型安全的方式使用它们。默认的类型安全持久性 API不支持连接,允许支持更多的连接 存储层。连接和其他SQL特定功能可以 通过使用原始SQL层(具有非常少的类型)实现 安全)。另外一个库Esqueleto建立在 持久数据模型,添加类型安全的连接和SQL 功能。
- 自动执行数据库迁移
只要您知道需要存储哪些数据,您就会拥有一个有效的数据库,并且能够开始使用该优化算法,而无需担心性能,可扩展性或重新发明轮子。
Shelf
hrId Text
length Double
width Double
height Double
UniqueShelf hrId
deriving Show
Box
hrId Text
length Double
width Double
height Double
UniqueBox hrId
deriving Show
Storage
shelfId ShelfId
boxId BoxId
UniqueStorage shelfId boxId
deriving Show
models
并生成相应类型{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
module Model where
import Database.Persist.Quasi
import Database.Persist.TH
import ClassyPrelude
share [mkPersist sqlSettings, mkMigrate "migrateAll"]
$(persistFileWith upperCaseSettings "models")
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Model
import Control.Monad.IO.Class (liftIO)
import Database.Persist.Sqlite hiding (master, Connection)
main :: IO ()
main = runSqlite ":memory:" $ do
runMigration migrateAll
myShelfId <- insert $ Shelf "ABCD.123" 10.0 1.5 2.0
thatBoxId <- insert $ Box "ZXY.098" 1.0 1.0 1.0
thisBoxId <- insert $ Box "ZXY.099" 2.0 1.0 1.0
_ <- insert $ Storage myShelfId thatBoxId
_ <- insert $ Storage myShelfId thisBoxId
myStorage <- selectList [StorageShelfId ==. myShelfId] []
liftIO $ print (myStorage :: [Entity Storage])
update myShelfId [ShelfWidth +=. 0.5]
thatBox <- get thatBoxId
liftIO $ print (thatBox :: Maybe Box)
myShelf <- get myShelfId
liftIO $ print (myShelf :: Maybe Shelf)
哪个会输出这些内容:
Migrating: [...]
[Entity {entityKey = StorageKey {unStorageKey = SqlBackendKey {unSqlBackendKey = 1}}, entityVal = Storage {storageShelfId = ShelfKey {unShelfKey = SqlBackendKey {unSqlBackendKey = 1}}, storageBoxId = BoxKey {unBoxKey = SqlBackendKey {unSqlBackendKey = 1}}}},Entity {entityKey = StorageKey {unStorageKey = SqlBackendKey {unSqlBackendKey = 2}}, entityVal = Storage {storageShelfId = ShelfKey {unShelfKey = SqlBackendKey {unSqlBackendKey = 1}}, storageBoxId = BoxKey {unBoxKey = SqlBackendKey {unSqlBackendKey = 2}}}}]
Just (Box {boxHrId = "ZXY.098", boxLength = 1.0, boxWidth = 1.0, boxHeight = 1.0})
Just (Shelf {shelfHrId = "ABCD.123", shelfLength = 10.0, shelfWidth = 2.0, shelfHeight = 2.0})
答案 2 :(得分:0)
因此,让我们采取最简单的情况:需要保持一些矩形的矩形,不允许堆叠。然后我们可以提供一个新的&#34;货架&#34;当我们在旧的&#34;架子&#34;:
中放置一个矩形时newtype Box = Box Int Int Int deriving (Eq, Ord, Show)
newtype Shelf = Shelf Int Int Int deriving Show
type ShelfID = Int
type BoxID = Int
type ShelfBox = (ShelfID, BoxID)
fitsOn :: (Int, Box) -> (Int, Shelf) -> Maybe (ShelfID, Shelf)
fitsOn (bid, Box bw bh) (sid, Shelf sw sh)
| bw <= sw && bh <= sh = Just (sid, Shelf (sw - bw) sh)
| otherwise = Nothing
从最宽的框开始,进行深度优先搜索可能效率最高:
import Data.IntMap.Strict (IntMap, (!))
import Data.IntMap.Strict as IntMap
import Data.List (sort)
collect (mx : mxs) = case mx of
Just x -> x : collect mxs
Nothing -> collect mxs
-- need to feed something like `IntMap.fromList $ zip [0..] $ sort bs` to `boxes`:
search :: IntMap Box -> IntMap Shelf -> [ShelfBox] -> Maybe [ShelfBox]
search boxes shelves acc
| IntMap.empty boxes = Just acc
| otherwise = case collect $ (map process) options of
[] -> Nothing
(x : xs) -> Just x
where (box, boxes') = IntMap.deleteFindMax boxes
options = collect [box `fitsOn` shelf | shelf <- IntMap.toList shelves]
process (sid, s') = search boxes' (IntMap.insert sid s') ((sid, fst box) : acc)
现在我们怎么能把两个架子放在彼此之上,总高度为H,否则独立高度?我们可以把这两个写在我们的货架上:
vert_shelves other_shelves h w = [Shelf w (h - i) : Shelf w i : other_shelves | i <- [0..h - 1]]
如果你想要盒子上的盒子,你将开始从fitsOn
(上面和旁边)开始产生两个矩形并试图聚合&#34;上面的#34;盒子进入任何其他来自同一架子并且具有相同高度的盒子,这将需要一些重新设计这个东西。你可能也想要一个无限数量的盒子,如果没有改写那些maybes的传递方式,这将会有点棘手。
答案 3 :(得分:0)