
アルゴリズム実装をAI処理に置き換えて席替えロジックを作ってみた
こんにちは、リテールアプリ共創部の戸田駿太です。
今回は席替えアルゴリズムをAIに置き換えてみた時の結果とメリット・デメリットをご紹介します。
弊社メンバーが日常的に実践するAI駆動開発のナレッジやTipsを共有するために、AI駆動開発 Advent Calendar 2025を開催しています。
本ブログは、この企画の19日目の記事になります。
もしAI駆動開発の最先端を知りたい方は、この1ヶ月間、ぜひ本アドベントカレンダーをチェックしてみてください。
利用したアプリのリポジトリ
一般的なエンジニアの思考
エンジニアの皆さんは1次配列を逆順で並び替えてください、と言われたら自分でアルゴリズムを考えてコードで実装すると想像する方が多いかと思います。
実際に私もそのように考えます。
const array = [1, 2, 3, 4, 5];
const reversedArray = array.reverse();
console.log(reversedArray); // [5, 4, 3, 2, 1]
ではこれが逆順ではなく1が先頭、5が最後尾になり、そのほかはランダムな値にするとなった場合はどうでしょうか。
新しく関数を作り実装を考える人が多いと思います。
このように要件が変わるたびに新しい関数を作って実装することになります。
アルゴリズムをAIに任せてみる
要件が変わるたびにコードを編集するのはエンジニアの仕事ですが、そのアルゴリズムを毎回作るのは効率的ではありません。
そこでAIに任せることで、要件を自然言語で説明するだけで、適切な出力を得られるようにチャレンジしてみます。
まずはシンプルにチャットでClaude sonnet 4.5を使って試してみます。
田中, 佐藤, 鈴木, 高橋, 伊藤, 渡辺, 中村, 小林, 加藤, 吉田, 山本, 佐々木, 清水, 斎藤, 松本, 井上, 木村, 林, 山崎, 森
このメンバーをランダムに並び替えて
田中, 佐藤, 鈴木, 高橋, 伊藤, 渡辺, 中村, 小林, 加藤, 吉田, 山本, 佐々木, 清水, 斎藤, 松本, 井上, 木村, 林, 山崎, 森
をランダムに並び替えました:
**山崎, 清水, 伊藤, 木村, 吉田, 佐々木, 高橋, 渡辺, 林, 斎藤, 小林, 森, 加藤, 井上, 鈴木, 松本, 中村, 山本, 田中, 佐藤**
このようにAIに任せることで、コードを書かなくても並べ替えをしてくれました。
少し難しくしてみます。
田中, 佐藤, 鈴木, 高橋, 伊藤, 渡辺, 中村, 小林, 加藤, 吉田, 山本, 佐々木, 清水, 斎藤, 松本, 井上, 木村, 林, 山崎, 森
このメンバーを5*5でランダムに並び替えて
条件
田中と吉田は隣になるようにして
山本と山崎は上下の関係になるようにして
足りない部分は空席と表示して
田中, 佐藤, 鈴木, 高橋, 伊藤, 渡辺, 中村, 小林, 加藤, 吉田, 山本, 佐々木, 清水, 斎藤, 松本, 井上, 木村, 林, 山崎, 森
を5×5でランダムに並び替えました:
---
佐々木 林 高橋 渡辺 小林
鈴木 田中 吉田 木村 斎藤
伊藤 山本 中村 加藤 空席
松本 山崎 清水 井上 空席
森 佐藤 空席 空席 空席
---
**条件の確認:**
- ✓ 田中と吉田:横に隣接(2行目)
- ✓ 山本と山崎:上下に隣接(3行目と4行目の2列目)
- ✓ 足りない5席を「空席」と表示
この結果を見ると少し難しい条件でも正しく並べ替えをしてくれていることがわかります。
席替えのアプリを作ってAIを導入してみる
こんなアプリを作ってみました。

メンバーリストをCSV形式で入力をして、要件を入力すると、席替えの結果を生成してくれます。
システムプロンプトと出力のスキーマ
const result = await generateObject({
model: model,
schema: z.object({
seats: z.array(
z.array(
z
.object({
name: z.string().nullable(),
color: z.string().optional().describe("座席の背景色(例: pink, lightblue, yellow)"),
})
.nullable()
)
),
}),
prompt: `以下の条件で座席配置を生成してください。
メンバー: ${JSON.stringify(memberInfo)}
要求: ${userRequest}
ルール:
- 各メンバーは1回だけ配置
- 全員を配置すること
- 空席はnull
- 2次元配列で行ごとに左から右へ配置
例: [
[{"name": "田中"}, {"name": "佐藤"}],
[{"name": "鈴木"}, null]
]`,
});
少しだけ条件をつけて席替えをしてみます。
このメンバーを5*5でランダムに並び替えて
条件
田中と吉田は隣になるようにして
山本と山崎は上下の関係になるようにして
足りない部分は空席と表示して

Claude sonnet 4.5での結果
ちゃんと条件を守って席替えをしてくれました。
次は少し改良して女性の場合は背景色をピンクにするようにしてみます。

Claude sonnet 4.5での結果
こちらも意図通りに席替えをしてくれました。
モデルを変えてみる
Claude Haiku 4.5
AnthropicのClaude Haiku 4.5を使ってみます。

Claude Haiku 4.5での結果

Claude Haiku 4.5での結果
このようにClaude Haiku 4.5でも席替えをしてくれました。
ただ、「女性はピンクの背景色にして」という指示をした場合はなぜか女性と男性が隣接することが多いように思えました。
これくらいがClaude Haiku 4.5の限界かもしれません。
Gemini 3 Pro

Gemini 3 Proでの結果

Gemini 3 Proでもしっかりと席替えをしてくれました。
Gemini 3 Flash
なぜか性別や背景色を指定していないのに勝手に背景色を変えてくれました🫠

Gemini 3 Flashでの結果
なぜか女性の背景色を指定すると適切に席替えをしてくれます。

Gemini 3 Flashでの結果
色々なパターンで試してみる
モデルの中で一番精度が高く感じたClaude Sonnetを使って色々なパターンで試してみました。 4.5
メンバーは今までと同じです。
このメンバーを5*5でランダムに並び替えて
条件
女性は左右の端に寄るようにして
女性はピンクの背景色にして
足りない部分は空席と表示して

このメンバーを5*5で男女交互に並び替えて
条件
女性はピンクの背景色にして
足りない部分は空席と表示して

このメンバーを5*5でランダムに並び替えて
条件
4つ角は空席にして
一行目の中央は先生で黄色の背景にしてください

モデル比較とコスト感
実際に使ってみた時のトークンとコストの集計をしました。
| モデル | 並び替えの対応 | 背景色の対応 | 1リクエストコスト(USD) |
|---|---|---|---|
| Claude Sonnet 4.5 | ◎ | ◎ | $0.00835 |
| Claude Haiku 4.5 | ○ | △ | $0.00277 |
| Gemini 3 Pro | ◎ | ◎ | $0.03207 |
| Gemini 3 Flash | ○ | △ | $0.01116 |
| モデル | 平均入力 | 平均出力 | 平均推論 |
|---|---|---|---|
| Claude Haiku | 1,268 | 301 | 0 |
| Claude Sonnet 4.5 | 1,268 | 303 | 0 |
| Gemini 3 Flash | 256 | 305 | 3,371 |
| Gemini 3 Pro | 256 | 334 | 2,296 |
Claude(Bedrock)の料金
Geminiの料金
従来のアプローチとの違い
以前だったら
// パターン1: ランダム配置
function randomSeating(members: string[]): string[][] {
// 実装...
}
// パターン2: 性別で分ける
function genderSeating(members: Member[]): string[][] {
// 実装...
}
// パターン3: 特定の人を隣に
function adjacentSeating(members: string[], pairs: [string, string][]): string[][] {
// 実装...
}
// パターン4, 5, 6, 7...
// → 要件が増えるたびに関数が増える
メリット
- 再現性が高い
- 影響範囲が絞れる
- パターンごとのテストができる
デメリット
- 新しいパターンが出るたびに実装が必要
- 複合条件(性別 + 隣接 + 色指定)は複雑になる
- メンテナンスコストが高い
AIアプローチ
// 1つの関数で無限のパターンに対応
async function generateSeating(
members: Member[],
request: string // 自然言語で要求を記述
): Promise<Seat[][]> {
return await ai.generateObject({
schema: seatSchema,
prompt: `メンバー: ${members}\n要求: ${request}`
});
}
メリット
- 要件を自然言語で書くだけ
- 新しいパターンに即座に対応
- 実装コードは変更不要
デメリット
- プロンプトによって左右される
- 実行回数が多すぎるとコストが高くなる
- 完全な再現性は保証されない
実装のポイント
構造化データ生成
Zodスキーマで出力形式を定義して、JSONデータを生成させています。
const result = await ai.generateObject({
schema: z.object({
seats: z.array(z.array(z.object({
name: z.string().nullable(),
color: z.string().optional()
})))
}),
prompt: "席替えして"
});
プロンプトには具体例を含める
AIは具体例があると理解しやすいです。
想定されるパターンをもっと多くプロンプトに書くことで、AIがより正確に席替えをしてくれます。
実際に特殊なパターン(女性は左右の両端の席に配置する際の例)などをプロンプトに書くことで、そのパターンの場合は正確に席替えをしてくれました。
prompt: `以下の条件で座席配置を生成してください。
メンバー: ${JSON.stringify(members)}
要求: ${userRequest}
ルール:
- 各メンバーは1回だけ配置
- 全員を配置すること
- 空席はnull
例: [
[{"name": "田中"}, {"name": "佐藤"}],
[{"name": "鈴木"}, null]
]`
いつ使うべき?使わないべき?
✅ AIアプローチが向いているケース
1. パターンが多様で予測不可能
- ユーザーごとに異なる要求
- 〇〇さんと〇〇さんは隣になるようにして
- 頻繁に変わる仕様がある場合
- エンタメ系のアプリケーションなどはAIを利用して複雑なパターンを生成するのは適しているかもしれないです
2. 単発・低頻度の実行
- 年に数回の席替え
- 一度きりのデータ整理
- 実行回数が多すぎるとコストが高くなる
3. 完璧でなくても許容される
- 「唯一の正解」はないものに利用することでユーザーの不満を最小限に抑えられる
- 席替えは決定的な正解がないので利用するのにちょうどいいと判断しました
- 人間が最終調整できる
❌ 従来実装が必要なケース
1. 大量・高頻度の実行
- リアルタイムAPI(ミリ秒単位のレスポンス)
- 早めのレスポンスを必要とする場合は生成AIは向いていない
- 毎分実行するバッチ処理
- 何度も実行されるとコストが高くなります
2. 完全な再現性が必要な場合
- 金融システム、法的な記録、監査が必要なシステム
- 今回は席替えで結果が多少ずれても許容されますが、厳密な再現性が求められるシステムには向いていません
3. パターンが固定
- 10種類の固定パターンを繰り返し使用
- エンジニアが10種類のアルゴリズムを考えて実装コストのみで対応できます。
まとめ
今回席替えアルゴリズムをAIに任せたことで多種多様なパターンの席替えが実現できました。
確実性が低いため頻繁に利用することはないと思われますが、今後AIが進化して安く正確に実行できるようになれば、このようにアルゴリズムの一部を任せることも増えてくるかもしれません。
参考リンク:










