challenge 与えられた並べ替えを実現するあみだくじの生成

お題#4476を見て思いつきました。

0からn (n>=1) までの数字を任意の順で並べたリストが与えられた時、0からnまでが順に並んだ状態から出発して、与えられたリストの順で結果が得られるようなあみだくじを作成して出力するプログラムを書いてください。

与えられたリストが (3 5 2 4 0 1) の場合、出力の1例を示します:

 0 1 2 3 4 5
 | | |-| |-|
 | |-| |-| |
 |-| |-| | |
 | |-| |-| |
 | | |-| |-|
 | | | |-| |
 3 5 2 4 0 1

一応、制約条件を示しておきます。

  • あみだの横棒は縦棒をまたぐことはできません。常に隣接する縦棒同士の交換となります 。
  • 同じ行に複数の横棒があっても良いですが、ひとつの縦棒の同じ点からふたつ横棒が出ることはありません。

一つのリストに対して複数の解があり得ます。ナイーブな解に飽き足らなければ出力行数をなるべく少なくする解を求める方法を考えてみてください。

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)]

Index

Feed

Other

Link

Pathtraq

loading...