【電脳少女プログラミング2088】 F#で解いた |> Geminiを混乱させた

paizaのプログラミングゲーム【電脳少女プログラミング2088 ─壊レタ君を再構築─】を F# で解いてみました。
ランクAだけ終わりませんでした。
ちょっと悔しいです。

# D ランク
## 郊外のスラム街
```fsharp
stdin.ReadLine()
|> int
|> (fun price -> price / 2 + 100)
|> printfn "%d"
```
## ネオン街の裏路地
```fsharp
let readInt () = stdin.ReadLine() |> int
let n = readInt ()
Seq.init n (fun _ -> readInt ()) |> Seq.max |> printfn "%d"
```
## カジノ
```fsharp
Seq.init 3 (fun _ -> stdin.ReadLine() |> int)
|> Seq.zip [ 1; 5; 10 ]
|> Seq.sumBy (fun (num, price) -> num * price)
|> printfn "%d"
```
# Cランク
## 自然の残る公園
```fsharp
type Coordinate = { posX: int; posY: int }

let splitReadInt () =
    stdin.ReadLine().Split(' ') |> Array.map int

let distance pos1 pos2 =
    let dx = pos1.posX - pos2.posX
    let dy = pos1.posY - pos2.posY
    dx * dx + dy * dy

let n, initPos =
    let [| n; x; y |] = splitReadInt ()
    n, { posX = x; posY = y }

Seq.init n (fun _ -> splitReadInt ())
|> Seq.map (fun arr -> { posX = arr.[0]; posY = arr.[1] })
|> Seq.map (distance initPos)
|> Seq.indexed
|> Seq.minBy snd
|> fst
|> ((+) 1)
|> printfn "%d"
```
一気にコードが長くなりました。他の解かれた皆さんはどうだったでしょうか。
## 廃マンションの一室
実は下のコードを提出する前に、パズルを解くようにコードをこねくり回して、長～いコードを提出しました。
そちらも正解にはなったのですが、特殊とはいえ3進法なのに3進法をあまり活用していないような気がして、3進法を調べ直しました。
そうしたところ平衡三進法(天秤三進法とも)を偶然見つけ、これを一度学び直してから、最初からプログラミングし直しました。
その結果がこちらです。
```fsharp
open System
let readFloat () = stdin.ReadLine() |> float

let toTernary input =
    let rec toTernary' input' str =
        let quotient = Math.Floor(input' / 3.0)

        let alterNumber =
            match Math.IEEERemainder(input', 3.0) with
            | 0.0 -> "2"
            | 1.0 -> "0"
            | _ -> "1"

        let newStr = alterNumber + str

        match quotient with
        | 0.0 -> newStr
        | _ -> toTernary' (quotient + 1.0) newStr

    toTernary' (input + 1.0) ""

toTernary (readFloat ()) |> printfn "%s"
```
このコードF#の通常の除算と剰余算を使うと、入力値がマイナスだったときに答えが合いません。
通常の除算の代わりに System.Math.Floor と / , 普通の剰余算 % の代わりに System.Math.IEEERemainder を使う必要があります。
float 使わなくても良さそうなのに float に変換しているのは、この2つの関数を使うためです。
使わない場合はコード中央付近の | 0.0 -> "2" から始まるマッチパターンを調整する必要があります。
Pythonでは通常の除算、剰余算のままで大丈夫なようです。
その他の言語は分かりません。

で、自分は平衡三進法を学んだあとコードをこねくり回してこのコードにたどり着いたのですが、このコードだと問題なく動くものの、なぜ平衡三進法を求める部分に毎回1を加えて渡す( input + 1.0 と quotient + 1.0 の部分)とうまく動くのか分かりませんでした。

コードを直すに疲れてしまった自分はこの質問を Gemini に投げてしまったのですが、その結果 Gemini を混乱させてしまいました。
その混乱っぷりが面白かったのですが、それは本件の趣旨からはちょっと外れるので、最後に載せます。
# Bランク
## ギャングのアジト
```fsharp
let n = stdin.ReadLine() |> int
let art = Array.init n (fun _ ->
    stdin.ReadLine().Split()
    |> Array.map int)

let isSinmetric =
    [ 0 .. n - 1 ]
    |> List.forall (fun i ->
    [ 0 .. n - 1 ]
    |> List.forall (fun j ->
    art.[j].[i] = art.[j].[n - 1 - i]))

printfn "%s" (if isSinmetric then "Yes" else "No")
```
## 一番通りの繁華街
```fsharp
let n = stdin.ReadLine() |> int

let areaMap =
    Array.init n (fun _ -> stdin.ReadLine() |> Seq.map (fun c -> c = '.') |> Seq.toArray)

let countSquares =
    seq {
        for x in 0 .. n - 2 do
            for y in 0 .. n - 2 do
                if areaMap.[x].[y] then
                    let maxDelta = min (n - x - 1) (n - y - 1)

                    for delta in 1..maxDelta do
                        if
                            areaMap.[x + delta].[y]
                            && areaMap.[x].[y + delta]
                            && areaMap.[x + delta].[y + delta]
                        then
                            yield 1
    }
    |> Seq.sum

printfn "%d" countSquares
```
# Sランク
## 思い出の屋上
とここまで解いたところで時間切れ、23,24日は時間がとれないから今日中にQiitaに投稿までしないと間に合わないと思いました。
ただSランクの問題なんて見たこともないから、ちょっと見てみようと思い覗いてみました。
案の定コードの目安が立たない。
こうやって経路探索すればいいのかなと思うけど、それじゃあこんな配置だったらどうするの？自問自答の結果振り出しに。
諦めかけたときフッと、「縄張りがどこにどれだけ広がっているかなんて考えずに、全部の点から全部の縄張りまｄの距離を求めちゃえばいいんじゃないの」と考えつき、大急ぎでコーディングしました。
その結果がこちら
```fsharp
let readArrayInt () =
    stdin.ReadLine().Split() |> Array.map int

let height, width, gangNumber =
    let input = readArrayInt ()
    input.[0], input.[1], input.[2]

let territories =
    Array.init gangNumber (fun _ ->
        let input = readArrayInt ()
        (input.[0], input.[1], input.[2]))

let calcDistance (x1, y1) (x2, y2, d) = abs (x1 - x2) + abs (y1 - y2) - d - 1

let calcMaxDistance =
    seq {
        for x in 1..height do
            for y in 1..width do
                let distance =
                    territories
                    |> Array.map (fun (x1, y1, d) -> calcDistance (x, y) (x1, y1, d))
                    |> Array.min

                yield distance
    }
    |> Seq.max

printfn "%d" calcMaxDistance
```
ある1点(x,y)からどれかのギャングの縄張りの中央(x1, y1)マンハッタン距離は問題文に書かれている通り|x1-x|+|y1-y|、これよりその地点から作成できる縄張りは、両者の距離からギャングの縄張りの広さdと1を減算すれば求まります。

全てのギャングの縄張りとの間に作成できる縄張りの最小の距離を求め(最小の距離以外のギャングまでは、その最小の距離のギャングの位置が邪魔して縄張りを広げられない)、さらにその全ての最小の距離の中の最大値を求めれば、答えに辿りつけます。

paiza の F# の実行環境は F# Interactive なのでコンパイルされてませんが、全部計算させても Test case 4 が 0.10 秒なだけで、その他は 0.03 秒でした。

![正解.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985288/d021e5ed-61ae-4e89-b486-cb825c5f2efd.jpeg)
# (余談) Geminiが混乱した！
廃マンションの一室のところで書いたとおり、なぜ平衡三進法を求める部分に毎回1を加えて渡すとうまく動くのか分からず Gemini に「どうしてうまくいくの？」と質問してしまいました。
Gemini は Web版の Gemini Flash Thinking Experomental です。
ただし今回の 2,1,0 を使う特殊な平衡三進法ではなく、-0+ を使うより一般的な平衡三進法にして質問しました。
最初に少し「いい質問です」などと講釈を述べたあと、説明に入りました。

![三進法0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985288/0a6c3fe4-1b5a-4608-8cbb-225fe8181f74.png)

すべり出しは順調です。

![三進法2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985288/53ee8dd0-420e-449a-a379-ca6655265440.png)
コンピューターが「あれ？」って返すのあり？

![三進法3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985288/c71e0d52-42d4-462b-90ff-1e24f7f6bff5.png)
「... まだ違う ...」「... 計算が合わない...」って普通推論部分で済ますことじゃないの？

![三進法4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985288/a9d7a9d5-f7c2-48ec-8620-13c3b62b0f4c.png)
'-+-'でないと、同じことを繰り返してます。

![三進法5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985288/7f4c2e8a-2a45-4644-ab27-e05f6e6c3b03.png)
期待してるよ。

![三進法6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985288/407a8654-294c-4197-bcd6-2d65b5b2f2cb.png)
敵に襲われて四次元ポケットの中身をばら撒くドラ◯もん並の混乱っぷりです。

![三進法7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985288/036eca5d-f611-485e-8787-c913890546aa.png)
「ああ、情けない」とか「ハッ！」とかAIからの答えで見るとは思いませんでした。
DeepSeek R1 が出てきたとき、推論部分に Wait!(待て) と書かれていて、AIなのに人間臭いと書かれてた X の投稿を見た覚えがありますが、Gemini は人間を通り越して猿並みです。
このあとも間違えを繰り返しますが、途中であれだけ固執していた -5 の平衡三進法（ここではバランス三進法）表記を求めるのを諦めて

![三進法8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985288/7fdaff2b-a84d-4ad1-a440-b224d3859103.png)
魔法かい！魔法って言葉、コンピューターが使っていいの？異世界転生したら n + 1 が魔法だったってやつ？
まあ「魔法」が日本語に訳される前は Magic かなにかで、数値のマジックのような意味してたんじゃないのかなと予想しますが。
で最後は

![三進法9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985288/c1d572a6-34b9-4555-8f99-9b58ccabdd45.png)
無理だよ～。自信満々によくそんなことが言えるな！
こんなAIの回答は初めて見ました。
AIの問題はハルシネーションだけではなく、分からない問題を分からないと答えられないことかもと思いました。