I ke kauwela i hala, ua komo au
Ma ke ʻano he mea komo ma Google Summer of Code 2019, ua hana wau i kahi papahana i loko o ka waihona
Ma kēia pou e kamaʻilio wau e pili ana i kaʻu hoʻokō ʻana i kahi algorithm no ka nānā ʻana i kahi pakuhi no ka bipartiteness ma Haskell. ʻOiai ʻo ka algorithm kekahi o nā mea maʻamau, ʻo ka hoʻokō ʻana me ka nani i ke ʻano hana i lawe iaʻu i nā ʻano hou a makemake nui i ka hana. ʻO ka hopena, ua hoʻoholo wau i kahi hoʻokō me nā mea hoʻololi monad.
Noʻu
ʻO Vasily Alferov koʻu inoa, he haumāna ʻehā au ma St. Petersburg HSE. Ma mua o ka blog aʻu i kākau ai
E pili ana i ka hoʻokō ʻana i ka algorithm
Kauwehe
Paipai nui ʻia nā haumāna e komo ana i ka papahana e blog. Hāʻawi lākou iaʻu i kahi kahua no ka blog
Hiki ke loaʻa ke noi huki me ke code i nīnau ʻia
Hiki iā ʻoe ke heluhelu e pili ana i nā hopena o kaʻu hana (ma ka ʻōlelo Pelekania)
Ua manaʻo ʻia kēia pou e hoʻomaʻamaʻa i ka mea heluhelu i nā manaʻo kumu i ka hoʻolālā hana, ʻoiai e hoʻāʻo wau e hoʻomanaʻo i nā huaʻōlelo āpau i hoʻohana ʻia ke hiki mai ka manawa.
Ke nānā ʻana i nā kiʻi no ka bipartiteness
Hāʻawi pinepine ʻia kahi algorithm no ka nānā ʻana i ka pakuhi no ka bipartiteness ma kahi papa e pili ana i nā algorithms ma ke ʻano he mea maʻalahi loa. Maikaʻi kona manaʻo: hoʻokomo mua mākou i nā vertices ma ka ʻaoʻao hema a ʻākau paha, a ke ʻike ʻia kahi ʻaoʻao hakakā, ʻōlelo mākou ʻaʻole bipartite ka pakuhi.
ʻO kahi kikoʻī hou aʻe: kau mua mākou i kahi vertex ma ka ʻaoʻao hema. ʻIke loa, pono nā hoalauna āpau o kēia vertex e moe i ka lobe ʻākau. Eia kekahi, pono e moe nā hoalauna a pau o kēia vertex ma ka lobe hema, a pēlā aku. Ke hoʻomau nei mākou i ka hāʻawi ʻana i nā ʻāpana i nā vertex ke loaʻa nā vertex i ka ʻāpana pili o ka vertex a mākou i hoʻomaka ai me ka mea ʻaʻole mākou i hoʻonohonoho i nā hoalauna. A laila hana hou mākou i kēia hana no nā ʻāpana pili.
Inā he kaʻe ma waena o nā vertices e hāʻule i loko o ka pā hoʻokahi, ʻaʻole paʻakikī ke ʻimi i kahi pōʻaiapuni ʻē aʻe i ka pakuhi, i ʻike nui ʻia (a maopopo loa) hiki ʻole i ka pakuhi bipartite. A i ʻole, loaʻa iā mākou kahi ʻāpana kūpono, ʻo ia hoʻi he bipartite ka pakuhi.
ʻO ka maʻamau, hoʻokō ʻia kēia algorithm me ka hoʻohana ʻana
No laila, ua hiki mai mākou i ka papahana aʻe. Hele mākou i nā piko o ka pakuhi me ka huli ʻana i ka hohonu-mua a hāʻawi i nā ʻāpana iā lākou, e hoʻololi ana i ka helu o ka mahele i ko mākou neʻe ʻana ma ka lihi. Inā hoʻāʻo mākou e hāʻawi i kahi ʻāpana i kahi vertex i loaʻa i kahi ʻāpana i hāʻawi ʻia, hiki iā mākou ke ʻōlelo palekana ʻaʻole bipartite ka pakuhi. ʻO ka manawa i hāʻawi ʻia nā vertices a pau i kaʻana a ua nānā mākou i nā ʻaoʻao āpau, loaʻa iā mākou kahi ʻāpana maikaʻi.
Maʻemaʻe o ka helu ʻana
Ma Haskell manaʻo mākou ʻo nā helu āpau maʻemaʻe. Eia naʻe, inā ʻoiaʻiʻo kēia, ʻaʻohe o mākou ala e paʻi i kekahi mea i ka pale. I nā mea āpau, maʻemaʻe moloā loa ka helu ʻana a ʻaʻole hoʻokahi maʻemaʻe kumu e helu ai i kekahi mea. Hoʻokomo ʻia nā helu ʻana a pau i loko o ka papahana "maemae" monad IO.
He ala ʻo Monads e hōʻike i ka helu ʻana me hopena ma Haskell. ʻO ka wehewehe ʻana i ke ʻano o kā lākou hana ʻana ma waho o ke kiko o kēia pou. Hiki ke heluhelu ʻia kahi wehewehe maikaʻi a maopopo ma ka ʻōlelo Pelekania
Maanei, makemake wau e kuhikuhi i ka wā e hoʻokō ʻia ai kekahi mau monads, e like me IO, ma o ka compiler magic, aneane pau nā mea ʻē aʻe i hoʻokō ʻia i nā polokalamu a pau nā helu i loko o ia mau mea maʻemaʻe.
Nui nā hopena a loaʻa i kēlā me kēia me kāna monad ponoʻī. He manaʻo ikaika a nani loa kēia: hoʻokō nā monad a pau i ka interface like. E kamaʻilio mākou e pili ana i kēia mau monad ʻekolu:
- ʻO ka ea ka helu ʻana e hoʻihoʻi i kahi waiwai o ke ʻano a a i ʻole e hoʻolei i kahi ʻokoʻa o ke ʻano e. Ua like loa ke ano o keia monad me ka lawelawe ana ma na olelo koʻikoʻi: hiki ke hopu a hoʻolilo ʻia nā hewa. ʻO ka ʻokoʻa nui ʻo ia ka hoʻokō pono ʻana o ka monad i ka hale waihona puke ma Haskell, aʻo nā ʻōlelo koʻikoʻi e hoʻohana pinepine i nā ʻōnaehana ʻōnaehana.
- ʻO ka mokuʻāina sa he helu helu e hoʻihoʻi i kahi waiwai o ke ʻano a a loaʻa iā ia ke ʻano hoʻololi o ke ʻano s.
- Malia paha a. Hōʻike ka Maybe monad i ka helu ʻana i hiki ke hoʻopau ʻia i kēlā me kēia manawa ma ka hoʻihoʻi ʻana ʻAʻohe mea. Eia naʻe, e kamaʻilio mākou e pili ana i ka hoʻokō ʻana o ka papa MonadPlus no ke ʻano ʻo Maybe, e hōʻike ana i ka hopena kū'ē: he helu ia e hiki ke hoʻopau ʻia i kēlā me kēia manawa ma ka hoʻihoʻi ʻana i kahi waiwai kikoʻī.
Ka hoʻokō ʻana i ka algorithm
Loaʻa iā mākou ʻelua ʻano ʻikepili, ʻo Graph a a me Bigraph ab, ʻo ka mea mua e hōʻike ana i nā kiʻi me nā vertices i hōʻailona ʻia me nā waiwai o ke ʻano a, a ʻo ka lua e hōʻike ana i nā kiʻi bipartite me nā ʻaoʻao hema hema i hōʻailona ʻia me nā waiwai o ke ʻano a me ka ʻākau. -ʻaoʻao vertice i hōʻailona ʻia me nā waiwai o ke ʻano b.
ʻAʻole kēia nā ʻano mai ka waihona Alga. ʻAʻohe o Alga i hōʻike no nā kiʻi bipartite kuhikuhi ʻole. Ua hana au i nā ʻano like me kēia no ka maopopo.
Pono mākou i nā hana kōkua me nā pūlima aʻe:
-- Список соседей данной вершины.
neighbours :: Ord a => a -> Graph a -> [a]
-- Построить двудольный граф по графу и функции, для каждой вершины
-- выдающей её долю и пометку в новой доле, игнорируя конфликтные рёбра.
toBipartiteWith :: (Ord a, Ord b, Ord c) => (a -> Either b c)
-> Graph a
-> Bigraph b c
-- Список вершин в графе
vertexList :: Ord a => Graph a -> [a]
Сигнатура функции, которую мы будем писать, выглядит так:
type OddCycle a = [a]
detectParts :: Ord a => Graph a -> Either (OddCycle a) (Bigraph a a)
He mea maʻalahi ke ʻike inā ʻike mākou i kahi ʻaoʻao hakakā i ka wā o ka hulina mua, aia ka pōʻai ʻē aʻe ma luna o ka waihona recursion. No laila, no ka hoʻihoʻi hou ʻana, pono mākou e ʻoki i nā mea āpau mai ka recursion stack a hiki i ka hanana mua o ka vertex hope.
Hoʻokō mākou i ka hulina hohonu-mua ma ka mālama ʻana i kahi hui hui o nā helu kaʻana no kēlā me kēia vertex. E mālama pono ʻia ka waihona hoʻihoʻi ma o ka hoʻokō ʻana i ka papa Functor o ka monad a mākou i koho ai: pono mākou e kau i nā vertices a pau mai ke ala i ka hopena i hoʻihoʻi ʻia mai ka hana recursive.
ʻO kaʻu manaʻo mua e hoʻohana i ka Either monad, ka mea e hoʻokō pono i nā hopena e pono ai mākou. ʻO ka hoʻokō mua aʻu i kākau ai ua kokoke loa i kēia koho. ʻO kaʻoiaʻiʻo, ua loaʻa iaʻu nā hoʻokō ʻokoʻa ʻelima i hoʻokahi manawa a ua hoʻoholo hope i kekahi.
ʻO ka mea mua, pono mākou e mālama i kahi hui hui o nā mea hōʻike - he mea kēia e pili ana i ka Moku'āina. ʻO ka lua, pono mākou e hiki ke hoʻōki ke ʻike ʻia kahi hakakā. Hiki iā ia ke Monad no Either, a i ʻole MonadPlus no paha. ʻO ka ʻokoʻa nui ʻo ia ka hiki ke hoʻihoʻi i kahi waiwai inā ʻaʻole i hoʻōki ʻia ka helu ʻana, a hiki paha ke hoʻihoʻi wale i ka ʻike e pili ana i kēia ma kēia hihia. No ka mea ʻaʻole pono mākou i kahi waiwai ʻokoʻa no ka kūleʻa (ua mālama ʻia ma ka Mokuʻāina), koho mākou ʻo paha. A i ka manawa e pono ai mākou e hoʻohui i nā hopena o ʻelua mau monads, puka mai lākou
No ke aha wau i koho ai i kēlā ʻano paʻakikī? ʻElua kumu. ʻO ka mea mua, ua like ka hoʻokō ʻana me ka imperative. ʻO ka lua, pono mākou e hoʻololi i ka waiwai hoʻihoʻi i ka hihia i ka wā e hoʻi ai mai ka recursion e hoʻihoʻi i ka loop loop, ʻoi aku ka maʻalahi o ka hana ʻana i ka monad Maybe.
No laila, loaʻa iā mākou kēia hoʻokō.
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE ScopedTypeVariables #-}
data Part = LeftPart | RightPart
otherPart :: Part -> Part
otherPart LeftPart = RightPart
otherPart RightPart = LeftPart
type PartMap a = Map.Map a Part
type OddCycle a = [a]
toEither :: Ord a => PartMap a -> a -> Either a a
toEither m v = case fromJust (v `Map.lookup` m) of
LeftPart -> Left v
RightPart -> Right v
type PartMonad a = MaybeT (State (PartMap a)) [a]
detectParts :: forall a. Ord a => Graph a -> Either (OddCycle a) (Bigraph a a)
detectParts g = case runState (runMaybeT dfs) Map.empty of
(Just c, _) -> Left $ oddCycle c
(Nothing, m) -> Right $ toBipartiteWith (toEither m) g
where
inVertex :: Part -> a -> PartMonad a
inVertex p v = ((:) v) <$> do modify $ Map.insert v p
let q = otherPart p
msum [ onEdge q u | u <- neigbours v g ]
{-# INLINE onEdge #-}
onEdge :: Part -> a -> PartMonad a
onEdge p v = do m <- get
case v `Map.lookup` m of
Nothing -> inVertex p v
Just q -> do guard (q /= p)
return [v]
processVertex :: a -> PartMonad a
processVertex v = do m <- get
guard (v `Map.notMember` m)
inVertex LeftPart v
dfs :: PartMonad a
dfs = msum [ processVertex v | v <- vertexList g ]
oddCycle :: [a] -> [a]
oddCycle c = tail (dropWhile ((/=) last c) c)
ʻO kahi poloka ke kumu o ka algorithm. E ho'āʻo wau e wehewehe i nā mea e hana nei i loko.
- ʻO inVertex ka ʻāpana o ka hulina hohonu-mua kahi a mākou e kipa ai i ka vertex no ka manawa mua. Maanei mākou e hāʻawi ai i kahi helu kaʻana i ka vertex a holo ma onEdge ma nā hoalauna āpau. ʻO kēia hoʻi kahi e hoʻihoʻi ai mākou i ka waihona kelepona: inā hoʻihoʻi ʻo msum i kahi waiwai, e pana mākou i ka vertex v ma laila.
- ʻO onEdge ka ʻāpana a mākou e kipa aku ai i ka lihi. Kāhea ʻia ʻelua no kēlā me kēia ʻaoʻao. Maanei mākou e nānā inā ua kipa ʻia ka vertex ma kēlā ʻaoʻao, a kipa aku inā ʻaʻole. Inā kipa ʻia, nānā mākou inā kūʻē paha ka ʻaoʻao. Inā ʻo ia, e hoʻihoʻi mākou i ka waiwai - ka luna loa o ka waihona recursion, kahi e kau ʻia ai nā vertices ʻē aʻe a pau i ka hoʻi ʻana.
- Nānā processVertex no kēlā me kēia vertex inā ua kipa ʻia a holo i loko o Vertex inā ʻaʻole.
- holo ka dfs i ka processVertex ma nā piko a pau.
ʻo ia wale nō.
Moolelo o ka huaolelo INLINE
ʻAʻole ka huaʻōlelo INLINE i ka hoʻokō mua ʻana o ka algorithm; ua ʻike ʻia ma hope. I koʻu hoʻāʻo ʻana e ʻimi i kahi hoʻokō ʻoi aku ka maikaʻi, ʻike wau ua ʻoi aku ka lohi o ka mana non-INLINE ma kekahi mau kiʻi. Ke noʻonoʻo nei e hana like nā hana i ka semantically, ua kāhāhā nui kēia iaʻu. ʻO ka malihini, ma kahi mīkini ʻē aʻe me kahi ʻano ʻē aʻe o GHC ʻaʻohe ʻokoʻa ʻike.
Ma hope o ka hoʻolilo ʻana i hoʻokahi pule i ka heluhelu ʻana i ka puka GHC Core, ua hiki iaʻu ke hoʻoponopono i ka pilikia me hoʻokahi laina o INLINE. I kekahi manawa ma waena o GHC 8.4.4 a me GHC 8.6.5 ua ho'ōki ka mea hoʻoponopono i ka hana ʻana ma kāna iho.
ʻAʻole wau i manaʻo e hālāwai me ia lepo i ka polokalamu Haskell. Eia nō naʻe, i kēia lā, hana hewa ka poʻe optimizers i kekahi manawa, a ʻo kā mākou hana ke hāʻawi iā lākou i nā hōʻailona. Eia kekahi laʻana, maʻaneʻi mākou eʻike pono e hoʻokomoʻia ka hana no ka mea ua hoʻokomoʻia i ka mana imperative, a he kumu kēia e hāʻawi ai i ka mea hōʻuluʻulu i kahi hint.
He aha ka hope?
A laila ua hoʻokō au i ka algorithm Hopcroft-Karp me nā monads ʻē aʻe, a ʻo ia ka hopena o ka papahana.
Mahalo iā Google Summer of Code, ua loaʻa iaʻu ka ʻike kūpono i ka hoʻolālā hana, ʻaʻole ia i kōkua wale iaʻu e loaʻa i kahi internship ma Jane Street i ke kauwela e hiki mai ana (ʻaʻole maopopo iaʻu i ka kaulana o kēia wahi ma waena o ka poʻe ʻike ʻike o Habr, akā hoʻokahi nō. o nā wahi liʻiliʻi e hiki ai iā ʻoe ke kauwela e komo i ka hoʻolālā hana), akā ua hoʻolauna pū iaʻu i ka honua kupaianaha o ka hoʻohana ʻana i kēia paradigm i ka hana, ʻokoʻa loa mai koʻu ʻike ma nā ʻōlelo kuʻuna.
Source: www.habr.com