与えられた並べ替えを実現するあみだくじの生成
Posted feedbacks - Haskell
ああそうか. inversion を求めて,隣接するものを採用,残りを採用したもので変換, というのを繰り返せばいいのか.これで多分,横棒数,段数ともに最小になるはず? 印字系は手抜きです(縦棒10本まで対応).置換を与えると,阿弥陀籤を印字します. 実行結果 *Main> :main [3,5,2,4,0,1] 0 1 2 3 4 5 | |-| | |-| |-| |-| | | | |-| |-| | |-| |-| |-| | |-| |-| | 3 5 2 4 0 1 *Main> :main [5,4,3,2,1,0] 0 1 2 3 4 5 |-| |-| |-| | |-| |-| | |-| |-| |-| | |-| |-| | |-| |-| |-| | |-| |-| | 5 4 3 2 1 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | module Main (main) where
import Data.List
import System.Environment
main :: IO ()
main = do { a:_ <- getArgs
; let { e = read a :: [Int]
; s = sort e
; caption = putStrLn . concat . intersperse " " . map show
}
; caption s >> putStr (showAmida (length e - 1) (amida e)) >> caption e
}
amida :: [Int] -> [[(Int,Int)]]
amida p = reverse $ fst $ head $ dropWhile (not . null . snd) $ iterate f ([],invs)
where invs = inversions p
f (acc,invs) = case select invs of
(xs,ys) -> (xs:acc, foldr map ys (map exchange xs))
inversions :: [Int] -> [(Int,Int)]
inversions [] = []
inversions (y:ys) = map (flip (,) y) (filter (y >) ys) ++ inversions ys
select :: [(Int,Int)] -> ([(Int,Int)],[(Int,Int)])
select invs = case partition ((1==) . uncurry subtract) $ sort $ invs of
([] ,ys) -> ([],ys)
(x:xs,ys)
-> case partition (share x) (xs++ys) of
(zs,ws) -> case select ws of
(us,vs) -> (x:us,zs++vs)
share :: (Int,Int) -> (Int,Int) -> Bool
share (x,y) (p,q) = x == p || x == q || y == p || y == q
exchange :: (Int,Int) -> (Int,Int) -> (Int,Int)
exchange (x,y) pq@(p,q) | x == p = (y,q)
| x == q = (p,y)
| y == p = (x,q)
| y == q = (p,x)
| otherwise = pq
showAmida :: Int -> [[(Int,Int)]] -> String
showAmida n = unlines . map (showStep n)
showStep :: Int -> [(Int,Int)] -> String
showStep n xs = '|':step n 0 xs
where step n m xs
| n == m = ""
| otherwise = case xs of
[] -> nostep $ step n (m+1) xs
(i,_):rs | m == i -> astep $ step n (m+1) rs
| otherwise -> nostep $ step n (m+1) xs
nostep = (' ':).('|':)
astep = ('-':).('|':)
|
ナイーブ版です。 あみだはなんだかバブルソートに似てると思ったので、そこからいきました。面白かったです。 Haskellによく合う問題のような気がします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | module Main
where
combine :: [a] -> ([a], [b]) -> ([a], [b])
combine xs1 (xs2, is) = (xs1 ++ xs2, is)
bPath :: [Int] -> Int -> [Int] -> ([Int], [Int])
bPath [] _ is = ([], is)
bPath [x] _ is = ([x], is)
bPath (a:b:xs) i is
| a <= b = combine [a] $ bPath (b:xs) (i + 1) is
| otherwise = combine [b, a] $ bPath xs (i + 2) (i:is)
bSort :: [Int] -> [[Int]] -> ([Int], [[Int]])
bSort xs iss = recurse
where
(xs', is) = bPath xs 0 []
recurse
| null is = (xs', iss)
| otherwise = bSort xs' ([is] ++ iss)
dumpRow :: Int -> [Int] -> IO()
dumpRow n xs = putStrLn $ " |" ++ dumpRowInner n 0 xs
where
dumpRowInner :: Int -> Int -> [Int] -> String
dumpRowInner n i xs
| n == i + 1 = ""
| elem i xs = "-|" ++ dumpRowInner n (i + 1) xs
| otherwise = " |" ++ dumpRowInner n (i + 1) xs
dumpAmida :: Int -> [[Int]] -> IO()
dumpAmida n [] = return ()
dumpAmida n (x:xs) = do
dumpRow n x
dumpAmida n xs
main :: IO()
main = do
putStrLn $ show goal
dumpAmida (length lst) ops
putStrLn $ show lst
where
lst = [3, 5, 2, 4, 0, 1]
(goal, ops) = bSort lst []
[0,1,2,3,4,5]
| |-| | | |
|-| |-| |-|
| |-| |-| |
|-| |-| |-|
| |-| |-| |
[3,5,2,4,0,1]
|
バブルソートの並列版みたいなのになりました。 アルゴリズムは#4923の匿名さんと同じかな? こちらも逆順で求めてからひっくりかえしています。 *Main> :main [3,5,2,4,0,1] 0 1 2 3 4 5 | |-| | | | |-| |-| |-| | |-| |-| | |-| |-| |-| | |-| |-| | 3 5 2 4 0 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | -- http://ja.doukaku.org/109/
import List(intersperse,sort,unfoldr)
import System(getArgs)
main = do
a:_ <- getArgs
let goal = (read a :: [Int])
putStrLn $ show_amida goal $ resolve goal
resolve :: [Int] -> [[Int]]
resolve q = unfoldr b_sort q
where
b_sort q | null ops = Nothing
| otherwise = Just (ops,next_q)
where
(next_q,ops) = f 0 q [] []
f n (a:b:rest) q' ops | a > b = f (n+2) rest (a:b:q') (n:ops)
| otherwise = f (n+1) (b:rest) (a:q') ops
f _ r q' ops = (reverse q' ++ r,ops)
show_amida :: [Int] -> [[Int]] -> String
show_amida q result = join "\n" ([header (sort q)] ++ body ++ [header q])
where
join a ls = concat (intersperse a ls)
header = (join " ") . (map show)
body = reverse $ map make_line result
make_line bars = "|" ++ line ++ "|"
where
line = join "|" $
map (\n->if elem n bars then "-" else " ") [0..(length q-2)]
|




shiro
#4704()
Rating13/13=1.00
お題#4476を見て思いつきました。
0からn (n>=1) までの数字を任意の順で並べたリストが与えられた時、0からnまでが順に並んだ状態から出発して、与えられたリストの順で結果が得られるようなあみだくじを作成して出力するプログラムを書いてください。
与えられたリストが (3 5 2 4 0 1) の場合、出力の1例を示します:
一応、制約条件を示しておきます。
一つのリストに対して複数の解があり得ます。ナイーブな解に飽き足らなければ出力行数をなるべく少なくする解を求める方法を考えてみてください。
[ reply ]