maze

Table of Contents

暑いなあ

TVに水中花が写っていた。涼しげでいいな。ふと、水の中で花って、受精は誰 が手助けしてくれるのと思ったぞ。水中蝶なんて聞いた事ないし。Bingに聞い てみた。

水中花の受精には、どんな生物が関わっていますか

水中花の受精には、水中で生活する生物が関わっています。水中花は、水中で花を咲かせ、花粉を水中に放出します。その後、水中にいる生物が花粉を運び、受精が行われます。例えば、ミジンコやミドリムシなどが関わっているとされています¹³。

ソース: Bing との会話 2023/8/27 (1) あすなろ学習室 - 発展 体外受精と体内受精. https://gakusyu.shizuoka-c.ed.jp/science/chu_3/seimei/ikimononohuekata/huekata3/taigai-tainai-jyusei.html. (2) 植物の生殖 - Wikipedia. https://ja.wikipedia.org/wiki/%E6%A4%8D%E7%89%A9%E3%81%AE%E7%94%9F%E6%AE%96. (3) 受粉 - Wikipedia. https://ja.wikipedia.org/wiki/%E5%8F%97%E7%B2%89.


エッジも暫く使わない間にエッジが効くようになったな。BボタンでBing検索。 結果のURLをクリックで、左窓にそのサイトが表示される。なかなか便利。

脅威を覚えたググルは、さかんにログインして検索して下さいとお願いモード 炸裂。第二の熱い検索戦争ですかね。

何時もググルさんを利用させて貰っているので、少し宣伝して応援する。何気 にbook検索が便利だぞ。世界中の本をピンポイントで立ち読みできる。昔から 有った機能かも知れないけど、最近になって気付いた次第。

そうそう、昭和の叔父さんは、愛の水中花 松坂慶子の曲 こんなのを思い出したぞ。

図書館へ避暑に行ったら、涼しい本特集をやってた。恒例のお化けか物かと思っ たら、もっとジャンルが広かった。アマルフィって本を借りてきた。昔、織田 君と天海さんで映画になってたな。どこが涼しいねん。まあ、司書の人も大変 だな。前回は、山の日特集で、山の本を多数並べていたしな。ひょっとして、 裏ツールとして、Bingを使っていないだろうか?

自分で調べてミレ。 涼しい本 校長先生の訓話集みたいに、アンチョコが有るじゃん。世の中、便利だ脳。

prompt

haskellで標準偏差を出力する関数を、作成して。
関数の型は、次の通り
std :: [Int] -> Double

前回のChatGPTへのプロンプト。これって、構造が、haskellで関数を書く時と そっくりだぞ。一行目は、主文。二行目は、where。三行目は、副関数だ。知 らず知らずのうちに、haskell体質になってるのか脳。

注目は、関数の型が綺麗に表現できてる事。

一般には、関数プロトタイプ として、知られているが、なんか汚ない形式だ。 それをそのまま、ChatGPTに指示するよりも、関数名は、std。入力は、整数の 配列。出力はダブルでお願いします、と、面倒そう。バーテンにウィスキーで も頼む雰囲気になっちゃう。ChatGPTも目を白黒させちゃうぞ。

!!

前回取り挙げたmedian関数の中に、こんなのが出てくる。

in if even l then
    fromIntegral ((ms !! (n - 1)) + (ms !! n)) / 2
   else
    fromIntegral (ms !! n)

この !! は、リストを配列と見做して、エレメントをインディックス番号でア クセスする演算子だ。余り使ってほしくないので、ビックリマークを2個重ね るいじわるをしてる。いわゆるhaskellはschemeと違うんですと言う主張。

gosh$ (list-ref '(a b c d e f) 2)
c

じゃ、!! を使わないで、上記を実現できるのか? はい、涼しげな水羊羹の分 割問題に置き換えます。

長さ6cmの水羊羹を、真2つに切断しましょう。

ghci> splitAt 3 [1..6]
([1,2,3],[4,5,6])

3cmの所に包丁を入れれば、等分に切断できるね。

ghci> (hidari, migi) = splitAt 3 [1..6]
ghci> fromIntegral (last hidari + head migi) / 2
3.5

それに左、右って名前をつける。後は、その切り口の所の値を取り出し てから計算すればOK。

7cmみたいに奇数の場合は? 7 `div` 2 で、3が得られるからね。

ghci> splitAt 3 [1..7]
([1,2,3],[4,5,6,7])

これで、憎っくき !! を撲滅できました。

maze

夏休みもあと少し。日記で苦労したなあ。そんな時は、創作日記です。

ひまわり畑の迷路で苦労したこと

小学3年生の日記を1000文字で書いて下さい。
題名は、ひまわり畑の迷路で苦労した事、です。

ひまわり畑の迷路で苦労したこと

こんにちは、今日はひまわり畑の迷路に行ってきました。とっても楽しいけれど、ちょっと大変なこともあったんだよ。

朝、お父さんとお母さんと一緒に車に乗って、ひまわり畑に着いたよ。畑に入ると、すごくたくさんのひまわりがたくさん咲いていて、きれいな黄色い花が風に揺れていました。それから、迷路に挑戦することになりました。

最初は楽勝かなと思ったんだけど、迷路の中に入ると、どの道が正しい道か分からなくなっちゃった。道には高いひまわりがたくさんあって、どこに進んでいいのか迷子になってしまいました。

お父さんが地図を持っていたけれど、それもどこに行っても全部同じように見えるひまわりばかりで、どこにいるのか分からなくなっちゃったんだよ。お母さんは「こっちがいいかもしれない」と言って進んでみたり、私たちも何回も進んだり戻ったりして、とっても混乱しました。

そして、何回も行き止まりにぶつかったり、同じ場所を何度も通ったりして、正しい出口を見つけるのが難しかったです。でも、諦めずにみんなで協力して進んでいくことで、少しずつ進むことができました。

やっとのことで出口にたどり着いたときは、みんなで喜びました。迷路の中での苦労が報われた気持ちがしたよ。

迷路を抜けて、ひまわり畑全体を見渡すと、頑張って辿り着いた景色がとても素晴らしく感じられました。その後、たくさんのひまわりを見て写真を撮ったり、お土産のひまわりの種をもらったりしました。

今日の経験で、大変な時でもみんなで助け合って頑張ることの大切さを学びま した。迷路での苦労も楽しい思い出になりました。次に行くときは、もっとス ムーズに迷路をクリアしたいなと思います。楽しい一日でした!


オイラーなら、趣味と実益で、とうもろし畑の迷路を選ぶなあ(そんなの有る んか?)。分岐する時に、とうもろこしを もいで目印にする。行き止まりなら、 それを回収して、別な経路を試す。脱出出来た時は、お土産も手元に用意でき ている。

あるいは、戸隠忍法、魔界脱出の術を使うんだ。何も考えずに、左手を壁にあ てて、ひたすら歩く(右手には手裏剣とか刀ってのは忍の世界では常識)。これで脱出できる。入口と出口の間が通路でつながって いるって事は、上から見たら、2つに分離できるって事だ。ただ、通路がギザ ギザになってるだけ。ならば、その片方の切り口にそって進むだけで、抜けら れる。

ふと思い出した。人間ってのは、パイプだぞ。入口は、口ね、出口は肛門。腹 の中はクネクネだけど。パイプに手とか足とか頭等の装飾品を取り付ければ、 人間になる。ああ、人間に限らず生物一般に通用する。

ならばと、数学の先生は考えた。物体の相似って、どういう事と? 取っ手の付 いたコーヒーカップは、浮き輪や真ん中に穴が空いたドーナッツと相似だと。 ドーナッツの輪が、取っ手になる。その一部をビローンと延して、円筒にし、 中央に窪みをつければ、コーヒーカップの出来上がり。

ああ、話がそれたわい。

maze by python and haskell

Pythonで学ぶアルゴリズム< 迷路探索 >

経路の探索

すごいH本にも例が出ていたな。後で再読しておこう。ヒースロー空港なんちゃ らって奴だったと思う。それから、ocaml本には、地下鉄の経路をダイクスト ラのアルゴリズムで解くのが出てた。すっかりご無沙汰だけど。

あれ? 経路探索と迷路探索って微妙に違うような。。。一緒一緒気にするな。 しっかり勉強しとくと、カーナビ屋になれるぞ。首都高迷路をスイスイとな。

暑い所は苦手なので、涼しい所で、迷路探索。そう、ChatGPTのGPUを熱くして やるのさ。

迷路探索 by haskell

10x10の迷路を検索するプログラムをhaskellで出力して下さい。
最初に迷路を表示
次に辿った経路を表示
検索は、深さ優先にしてください

このプログラムは、スタート地点を(1, 1)、ゴール地点を(10, 10)とし、壁を '#'、通路を '.' で表現しています。

深さ優先はHungしたので、幅優先のコードを提示してもらった。

import Data.Array
import Data.List (unfoldr)
import Data.Maybe (isJust, fromJust)

type Point = (Int, Int)
type Maze = Array Point Char

mazeWidth :: Int
mazeWidth = 10

mazeHeight :: Int
mazeHeight = 10

start :: Point
start = (1, 1)

goal :: Point
goal = (mazeWidth, mazeHeight)

initialMaze :: Maze
initialMaze = listArray ((1, 1), (mazeWidth, mazeHeight)) $
              concat [ replicate mazeWidth '.' | _ <- [1..mazeHeight] ]

printMaze :: Maze -> IO ()
printMaze maze = mapM_ putStrLn [ [ maze ! (x, y) | x <- [1..mazeWidth] ] | y <- [1..mazeHeight] ]

bfs :: Maze -> Point -> Point -> Maybe [Point]
bfs maze start goal = bfs' [(start, [])] []
  where
    bfs' [] _ = Nothing
    bfs' ((pos, path) : queue) visited
      | pos == goal = Just (reverse (pos : path))
      | pos `elem` visited = bfs' queue visited
      | otherwise =
          let nextPositions = [(x, y) | x <- [fst pos - 1 .. fst pos + 1],
                                         y <- [snd pos - 1 .. snd pos + 1],
                                         (abs (fst pos - x) + abs (snd pos - y)) == 1]
              validNext = filter (\p -> inRange (bounds maze) p && maze ! p /= '#' && p `notElem` visited) nextPositions
              newQueue = queue ++ [(nextPos, pos : path) | nextPos <- validNext]
          in bfs' newQueue (pos : visited)

main :: IO ()
main = case bfs initialMaze start goal of
    Just path -> do
        putStrLn "迷路:"
        printMaze initialMaze
        putStrLn "経路:"
        printMaze (initialMaze // [(p, '*') | p <- path])
    Nothing -> putStrLn "経路が見つかりませんでした。"

実行例

ghci> :main
迷路:
..........
  :
..........
経路:
*.........
  :
*.........
*.........
**********

苦労も何も無い迷路。迷路を作るのをサボっている。

昔、女房が旅行の途中で、ぶどうの観光農園に入ったそうな。手に届く所には 房が全く無かった、騙された、詐欺にあったと憤慨したとな。 今は、そんな感じだな。全く向日葵が植わってない畑。

迷路作り

ならば、植栽してやる。その前に、植栽方法を勉強。趣味の園芸の時間です。 iniitialMazeから大切な部分を抽出してきて実験。農業試験場であります。

ghci> a = listArray((1,1), (2,2)) [1..4]
ghci> a
array ((1,1),(2,2)) [((1,1),1),((1,2),2),((2,1),3),((2,2),4)]
ghci> a ! (2,1)
3

配列へのアクセスは頻繁にあるだろうから、演算子は、ビックリマークがひと つです。

Arrayの使い方 文献を調べてみた所、Data.Arrayはhugs98時代からの奴だそうです。構造が良 く分かりますなあ。これは配列なんだけど更新は苦手です。まあ、紫外線をあ てると、内容が消えるEPROMみたいな機構と思っていれば、間違いない。念の 為、更新には、// 演算子が用意されている。

実際に植栽。配置が転地されちゃうので、事前に配置図を作って、間違えない ようにしよう。

initialMaze :: Maze
initialMaze = listArray ((1, 1), (mazeWidth, mazeHeight)) "....#.....\
\....#.....\
\....#.....\
\....#.....\
\......#...\
\......#...\
\......#...\
\......#...\
\......#...\
\......#..."

実行結果。

経路:
*.........
*.........
*.........
*****.....
####*.....
...**.....
...*######
...*......
...*......
...*******

下記のような無理の無い記述方法も有るのね。

complexMaze :: Maze
complexMaze = listArray ((1, 1), (mazeWidth, mazeHeight)) $
              concat [ "#.#####.#."
                     , "....#.###."
                     , "#.#...#.#."
                     , "#.#####..."
                     , "#.#...####"
                     , "#...#..##."
                     , "########.#"
                     , "#........#"
                     , "########.#"
                     , "#........#"
                     ]

自動生成させたので実行すると、経路が見つかりませんでした となるって告 げると、コードの方を変更し始めて、ど壺に突入。検証した迷路を提示してる 訳じゃないんだ。これが限界なんだね。

ひょっとして、転置問題? Data.Listに転置があったので、追加してみた。リ ストになってると便利さが倍増するな。

concat $ transpose [ "#.#####.#."
                   , "....#.###."
		   :

けど、やはり駄目だった。

auto gen maze

ならば、別プログラムで迷路を作らせればいいんでないかい。

何度かやりとりしたけど、HungUPしちゃう。最後は、こんな泣き言が。学習し なかったってか、そんな事をやってる事例な無かったんだろうね。

申し訳ありませんが、こちらのプラットフォームではコードを実際に実行して修正することが難しいようです。生成アルゴリズムに問題がある可能性があります。

Haskellのプログラムが無限ループに陥る場合、再帰的な関数呼び出しや状態遷移が正しく制御されていないことが原因となることがあります。深さ優先探索の場合、無限ループに陥らないようにするために、訪問済みのセルを適切に管理することが重要です。

もし自動生成の迷路を試すことが必要であれば、より堅牢な迷路生成アルゴリ ズムを考えるか、既存のライブラリを使用することを検討することをお勧めし ます。ご不便をおかけして申し訳ありません。

まあ、コードには、新らしい事が出てるんで、勉強の足しにはなるわな。

import System.Random
import Control.Monad.State
import Data.Array

type Point = (Int, Int)
type Maze = Array Point Char

mazeWidth :: Int
mazeWidth = 10

mazeHeight :: Int
mazeHeight = 10

start :: Point
start = (1, 1)

goal :: Point
goal = (mazeWidth, mazeHeight)

generateMaze :: IO Maze
generateMaze = do
    gen <- newStdGen
    let maze = evalState (generateMaze' start []) gen
    return maze
  where
    generateMaze' :: Point -> [Point] -> State StdGen Maze
    generateMaze' currentPos visited
        | currentPos == goal = do
            modify (\gen -> snd (next gen))
            return $ listArray ((1, 1), (mazeWidth, mazeHeight))
                     [ if p `elem` visited then '.' else '#'
                     | y <- [1..mazeHeight]
                     , x <- [1..mazeWidth]
                     , let p = (x, y)
                     ]
        | not (inRange ((1, 1), (mazeWidth, mazeHeight)) currentPos) = do
            modify (\gen -> snd (next gen))
            generateMaze' start visited
        | otherwise = do
            modify (\gen -> snd (next gen))
            let nextMoves = [ (x, y) | x <- [fst currentPos - 1 .. fst currentPos + 1],
                                       y <- [snd currentPos - 1 .. snd currentPos + 1],
                                       (abs (fst currentPos - x) + abs (snd currentPos - y)) == 1 ]
                unvisitedMoves = filter (`notElem` visited) nextMoves
            case unvisitedMoves of
                [] -> generateMaze' (head visited) (tail visited)
                _ -> do
                    nextMoveIdx <- state $ randomR (0, length unvisitedMoves - 1)
                    let nextMove = unvisitedMoves !! nextMoveIdx
                    generateMaze' nextMove (currentPos : visited)

printMaze :: Maze -> IO ()
printMaze maze = mapM_ putStrLn [ [ maze ! (x, y) | x <- [1..mazeWidth] ] | y <- [1..mazeHeight] ]

main :: IO ()
main = do
    generatedMaze <- generateMaze
    putStrLn "生成された迷路:"
    printMaze generatedMaze

etc