はてなキーワード: ソートとは
打線組んだみたいに増田の主観を根拠に頭でキャラソートして順位付けしたよ。
1位 月村手毬
キャラ立ちがヤバイのとヤバイ本人から出てくる一曲目というのが刺さったのと
あとぬいの力が絶大すぎる
2位 藤田ことね
あざとカワイイとお金ダイスキでネタにされつつちゃんと人気もある女
コミケ時期にSTEP3投入してセルラン1位取ったくらいには人気
3位 十王星南
実装に向けて1曲目の前に知らん曲が流される女
めっちゃカッコいい面がフィーチャーされる中、やっぱりポンコツやないか!!
手毬とセットではあるものの、単体でもアクが強いことが判明して情緒が溶けてる奴が多いのでこの辺りの順位
5位 篠澤広
6位 花海咲季
正直もっと上に据えたいのだが上の奴らが強すぎてここら辺の順位
7位 花海佑芽
中の人が休業して劇中二曲目出せなかったので遅れをとったのでこの辺りの順位
オタクの現身みたいな頑固女
9位 倉本千奈
出てくるPアイドル半分以上は強すぎんだろみたいな性能してる
アイドルマスターを成長と見るなら千奈がオススメコンテンツかもしれない
10位 紫雲清夏
元P肝いりな女
リー清の清の方
キャラクター、シナリオ強度もだいぶスペック高めだけどここに位置せざるをえない謎
11位 姫崎莉波
はみ出したい、はみ出せない、どっちなんだい!
でもちゃんとやりたいこと決まってよかったね
過程が15点、結果が90点みたいな女
バックグラウンドから来るポテンシャルはかなり高いはずだが、劇中の人格、言動、態度が他と比べて低い水準なのでこの順位
他のキャラだとどんなひどい言動しても「そうそうこいつはそういうキャラだからなw」みたいな感じか人当たりが優しめなのに対し
人当たりが強い言動とかは麻央に集約されてる気がする
麻央から可愛さと正義感を抜いたらただのイキリクソガキなんだよな、みたいな感想になる
良くて咲季の上、悪くて千奈の上か下
Q. 賀陽燐羽が実装されたとしたらどの辺に入りそう?
美鈴の上
写真のことは完全に素人だが、どの写真も「いい…」としか言いようがない。
とりあえずflickrを見てほしい。膨大な枚数が丁寧にソートされており、見やすさからも本人の仕事の几帳面さが窺える。
https://www.flickr.com/people/mikkagashi/
三日画師さんを知ったのは通っていた大学の最寄り駅で大規模な再開発があり、その頃の風景が失われてしまったのがきっかけだった。
当時ガラケーだったのもあり写真が手元に全く無いことに気が付き、せめて気持ちだけでもと検索したところでたまたま三日画師さんのアルバムを見つけた。
しかもまさに自分が通っていた時代の写真が大量にアップされていたのも驚いた。
その後flickrにアップされている写真はほぼ閲覧した。どの写真も凝りに凝った一枚というよりも、そこに住んでいる人の目に入っているであろう風景がありのまま撮影されている。三日画師さんとは生活エリアが近いようで、いくらでも見たことがある光景が出てくる(そしてもう見れない。ほとんど何かしらの再開発が入っているから)。
残念ながら三日画師さんのアカウントは長らく更新がない。twitterのアカウントも検査入院の報告で止まっている。それでもこの誰かの心象風景ときっと重なるであろう貴重な写真がインターネットに埋もれていってしまうのが惜しいので、ひっそりとここにアップしておきたい。
ゴーストオブヨウテイのレビュー漁ってたらまぁーほぼ間違いなく生成AI製だろうなというブログが出てきた。
https://lifematome.blog/goy-metascore-warui/
前半部分はかなり読める。というか、ブログの文章としてはかなり上手くて読みやすいし興味をそそられる。しかしこの時点でなんとなく「文章うますぎない?」「分量多すぎない?」という疑問が頭をよぎる。
そして「ストーリー:期待を超えられなかった物語とキャラクター」のところで生成AI製であることに気づくわけだ。『アイヌの血をひく若き女性:カイ』って誰だよ!主人公は倭人だし、まず名前が違うわ!篤じゃい!
その後の部分もちょくちょく疑問のある部分が出てくる。バグや最適化不足はあまり感じなかったし、アイテムのソート機能が無いことに問題は無い。(装備のプリセット保存数はたりなかったけど)
この例から見える、生成AI製のブログ記事に見られる特徴を挙げてみる。
今後もゲーム関係のブログで生成AI製が増えるんだろうなという諦念が湧くが、おそらくグーグル側も対策をしているのか記事の文章の上手さのわりに検索順位はかなり低い。また、つまるところ問題はAIがゲームをせずに記事を書いているところにあるため、AIがゲームをプレイして必要な情報を収集した上で記事を執筆できるようになったら状況が変わるかもしれない。AIの書くゲーム攻略情報がどんなものになるかは興味深く、果たして人間に再現可能なものを出してくれるかどうかが気になるところ。(全部完璧にパリィするだけで勝てるよ!とか(笑))
1ブクマでのソートなんでどうせ(ほとんど)誰も見ないだろ。無視できる影響をあげつらうな。
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 https://anond.hatelabo.jp/20250908182808# -----BEGIN PGP SIGNATURE----- iHUEARYKAB0WIQTEe8eLwpVRSViDKR5wMdsubs4+SAUCaL6hqQAKCRBwMdsubs4+ SKVFAQDZrWwboL/mNRydwZrNc1KT1N4bVH15VRqeKXkhv3U4mQD/ZpQ6X5gtfHBW /+YrXyC14u61MwBMtHKwpLojZrISEAc= =lLLg -----END PGP SIGNATURE-----
>事前予約は全敗して
7日前予約は人気館ばかり予約せず、第4,第5希望に滑り止めを入れておく。
俺は赤十字館の遅い時間を入れたら、大混雑の8/31でも当選した。
それでも外れたら、自分が外れた分は他の誰かが入れるのだと思って静かに諦める。
成功率が低いので最初から捨てた方が精神衛生上は良いが、先週しつこく試してたらいのちの未来館に行けたので何とも言えない。
>列に並ぼうと思っても並ぶことすらやらせて貰えないみたいなんはどうしたらええんかね
万博GOというサイトの待ち時間情報を「経過時間が短い順」でソートし、規制中でないところへ行く。
食事ならネパール館とアンゴラ館のテイクアウトがあまり待たない。未払いクソ野郎のせいで開館が遅れて大損害だろうからなるべく買ってあげてほしい。
GPT-5:
手がかりありがと!──ただ、ご提示の情報だけだと特定まであと一歩でした。まずは「当たりそうな候補」と「最短で絞り込むやり方」をまとめます。
まず候補(“Z”始まりで日本の成人向け&太線・モノクロ多め)
Zトン / Z-ton:日本の成人向けで有名。太い線・黒ベタ強めの作風のことが多い。pixiv・同人も活発。ゼルダ系二次創作は時期により描いている可能性あり。
(ほかの“Z????”該当はかなり少なく、匿名/短縮名や別名義の可能性も高い)
> 5文字表記という点は「Z-ton(ハイフン含め4~5文字扱い)」やカタカナ「Zトン(3字)」との記憶ズレの可能性もあります。上の特徴だけ見ると最有力はZトンです。
超効率の絞り込み手順(再現性あり)
1. pixivで期間絞り込み
2. 画風フィルタ
- 見つけた作者名で Twitter(X) / FANBOX / booth / note を検索。
- 2023年前後のポストでAIイラストへの所感を発言していないか遡る。
- キーワード例:AI イラスト, 生成AI, Stable Diffusion, NovelAI など。
もし覚えていたら、ここを1つだけ教えて(特定率が一気に上がります)
- そのAI発言を見た場所(Xのポスト、FANBOX、note、pixivのキャプション等)。
- 作品にリンクや固有のタグ(例:特定のヒロイン名、シリーズ名)があったか。
- 名前の表記揺れ(Z-ton / Zトン / Z_ton など)をうっすらでも。
あらためて何も知らないなあと思った。衆院の比例代表と参院の比例代表は何が違うのか?それぞれ党はどのような戦略で候補者を立てるのか?というわけでGeminiに色々教えてもらいながらまとめた。東京都在住なので東京都での数字だと肌感で理解しやすいので、東京都をベースにまとめてみた。
| 選挙 | 選挙区数 | 議席数 | 備考 |
|---|---|---|---|
| 衆議院 小選挙区 | 東京30(全国289) | 東京30(全国289) | ドブ板選挙 |
| 衆議院 比例代表 | 東京1(全国11) | 東京19(全国176) | 小選挙区救済・少数政党 |
| 参議院 選挙区 | 東京1(全国45) | 東京6(全国74)[改選毎] | 都道府県知名度 |
| 参議院 比例代表 | (全国1区のみ) | (全国50)[改選毎] | 全国知名度 |
さて、こうやって表にしてもよく分からない。
まずは比例代表は特殊ルールという点を理解することが糸口になる。そもそも1993年からの選挙改革によって紆余曲折を経て比例代表制が導入されたわけで、それまでは選挙区による選挙だけだった。比例代表とは?については後述するので、まずは選挙区による選挙を比べてみよう。すると、衆議院 小選挙区、参議院 選挙区に注目することになる。比較対象として東京都議会もみてみよう。
| 選挙 | 選挙区数 | 議席数 | 備考 |
|---|---|---|---|
| 衆議院 小選挙区 | 東京30(全国289) | 東京30(全国289) | ドブ板選挙 |
| 参議院 選挙区 | 東京1(全国45) | 東京6(全国74)[改選毎] | 都道府県知名度 |
ここで際立つのが参議院選挙区の特殊性である。衆議院や東京都議会は東京を30-40エリアに分割してその狭いエリアでの選挙戦が繰り広げられるのに対し、参議院は東京丸ごとがフィールドとなる。ご存知だろうが東京は広い。一番密集しているであろう東京でこれなので、それ以外の地方では当然さらに広い。告示から選挙までの期間は、参議院では17日間である(衆議院は12日間)。つまり約2週間しかない。この期間に東京都の全有権者に対して顔を見せてアピールするのはほぼ不可能である。つまりあらかじめ名前が有権者に浸透しているか、他の代替戦略が必要になる(後述)。一方衆院選は12日間と期間は短いものの、東京が30の選挙区に分割されているため、選挙活動をしなければ行けない面積はかなり狭い。最も狭い東京26区では11km^2しかない。歩きでも一日で一周できる。
なお、衆議院小選挙区と東京都議会のエリア分けは、小選挙区が30、東京都議会が42と微妙に異なる。これは衆議院小選挙区はなるべく小選挙区ごとの有権者数を等しくするように分割しているのに対し、東京都議会の選挙区は行政単位、つまり市区町村の境界に一致させているためである。しかし衆議院小選挙区と東京都議会の本質的な違いは境界線ではない。最も重要なのは当選者数である。小選挙区では必ず一人しか当選しないのに対し、東京都議会の選挙区では住民の人数に合わせて複数名が当選することがある。例えば世田谷区は東京都議会に8つの議席を持つ。一方衆議院小選挙区では区民の多さから東京5区と6区に概ね分割され、当選者数はそれぞれで1名ずつである。
では改めて衆議院小選挙区と参議院選挙区の比較に戻ろう。まずはエリアの広さと当選人数の違いで、先ほど確認したように衆議院小選挙区は狭いエリアで一人だけが選ばれるバトルロワイヤルが繰り広げられるのに対し、参議院選挙区は東京全土を対象として6個の議席が争われる。ちなみに「小」選挙区の「小」とは”当選人数が一人に限定される”ことを意味する。つまり選挙区の広さそのものではなく当選人数によって定められる言葉であることに注意が必要である。
もう一つの違いとして重要なのが選挙のサイクルである。衆議院は4年の任期、参議院は6年の任期と定められている。しかし衆議院では内閣総理大臣による解散総選挙というイベントが任期を満了する前にほぼ必発するため、実際の衆議院の任期は平均2年9ヶ月に過ぎない(Gemini調べ)。つまり衆議院は参議院の2倍以上のスピード感で選挙が行われる。3年おきに選挙カーが駆け回り名前を連呼し、選挙区の狭さも相まって駅前で見かける機会も少なくないため、有権者に顔が刷り込まれやすい。よって衆議院小選挙区で勝つのに必要なのはドブ板選挙である。汗を流して街頭に立ち、地道に顔の見える距離でアピールする。また地元の名士や二世議員、三世議員も強い。都市部では住民の入れ替わりもそれなりに激しいものの、親の世代から名前を知っているとなんとなく親近感も湧くというものである。
一方で参議院選挙区で勝つのは一筋縄ではいかない。東京都全体を2週間でドブ板選挙できるわけがない。よく言われるのはスポーツ選手やタレント、文化人などからの転身が有利ということだ。実際に今回の東京都参院選でもちらほらみかける。衆議院小選挙区と違ってトップ当選する必要はないため、ある程度の集票が見込めればよい。あとは強い政治的メッセージである。直接顔を合わせなくとも強いイデオロギー、政党のカラーによって投票する組織票を固めれば当選の目が見えてくる。従来は医師会、労働組合、農協、宗教団体といった組織票が強いと言われていた。最近は既存の組織力が弱まる一方でSNSを用いたイデオロギーによる集票が目立ちはじめているように思う。
以前より衆議院は民意をより反映し、参議院は良識の府と言われ長期的な視点から国政に意見するとされてきた。たしかに衆議院の小選挙区は最も住民に密着している。ただそれが国政への民意を反映しているかというと微妙なところである。顔が見える相手というのはえてして好感度で決まりやすい。インターネットで批判が集まるような思想をもった政治家が地元では強いという話もよく聞く。とはいえ参議院はどうか。従来参議院選挙で強いといわれる組織票とは、つまり既存の組織を反映した候補を国会に送り込む。これは既得権益維持・現状維持の方向にバイアスがかかりやすいだろう。イデオロギーよりも実利を優先すること、拙速な改革を好まないという点では良識の府と言われる所以も理解できるところである。ただ現実の選挙としてそれが機能しているかについては、自分は政治の現実を知らないのでコメントができない。
| 選挙 | 選挙区数 | 議席数 | 備考 |
|---|---|---|---|
| 衆議院 小選挙区 | 東京30(全国289) | 東京30(全国289) | ドブ板選挙 |
| 衆議院 比例代表 | 東京1(全国11) | 東京19(全国176) | 小選挙区救済・少数政党 |
| 参議院 選挙区 | 東京1(全国45) | 東京6(全国74)[改選毎] | 都道府県知名度 |
| 参議院 比例代表 | (全国1区のみ) | (全国50)[改選毎] | 全国知名度 |
残るのが衆議院比例代表と参議院比例代表である。まずは衆議院比例代表についてみてみる。実はこの衆議院比例代表は党によって戦略が大きく分かれるところである。具体的にいうと以下の2種類である。
| ①小選挙区を主要戦場と位置づけて、比例代表は小選挙区の救済(比例復活)と割り切る |
| ②比例単独候補を手厚くしつつ、小選挙区も取れるところを狙いにいく |
この2つの戦略の意味を理解するには比例単独というオプションを理解する必要がある。衆議院比例代表は、比例にだけ立候補する比例単独と、小選挙区との重複立候補の2種類の立候補が認められている。比例単独だと厳しいバトルロワイヤルである小選挙区を避けた上で、政策・イデオロギーを広く有権者に訴えることで当選を狙う。また衆議院比例は政党名でしか投票できないため、極端な話を言うと候補者が無名でも問題ない。
①の戦略は自民党の基本戦術であり、横綱相撲と言えるだろう。比例単独は2,3人しか設定していない。また旧民主党もこの戦略をとっていた。
②の戦略は基本的に野党がとる。立憲民主党や共産党、あとは公明党などは30−40程度を比例単独に擁立している。また新興政党は代表の知名度を活かして比例票を集め、比例単独を少しでも多く当選させることを目指す。面白いのは日本維新の会で、2017年の選挙までは40人程度を比例単独に擁立していたが、2021年以後は一定の力がついたと考えたのか、比例単独は5人、2人と減らしている。
また旧民主党から立憲民主党への比例単独の推移をみると興味深い。2009年、歴史的な政権交代劇となった衆議院選挙では比例単独を0人にして、小選挙区で300人以上の候補者を擁立した。つまり全エネルギーを小選挙区に注ぎ込み、自民党を打倒するという極めて攻撃的な戦略であり、歴史的な圧勝につながった。しかしその次の2012年も同様の戦略で望んだが、結果として歴史的な大敗につながった。これは政権与党としての引くわけにはいかないというアピールでもあったが、現在の立憲民主党のように比例単独をもっと多く擁立していれば、もう少し議席を残せたかもしれない。
ところで比例復活とは何か。小選挙区はバトルロワイヤルであるため思わぬ事故も起こりやすい。長年貢献してきた重鎮が事故的に落選することが起こり得る。またエリアごとの勝者総取りになるためマイノリティ政党の当選が難しくなることも懸念された(2位以降が全て死票となる)。そこで導入されたのが衆議院比例代表である。詳しい方法を知りたい人はドント式の項目を読んでもらいたいが、要するに2位以下の人でも1位当選者との票差がわずかであれば、比例で復活できる可能性を残すシステムである。
では参議院比例代表制はどのような選挙か。この比例代表は日本全体を一つの選挙区とする、かなり特殊な選挙である。国民投票みたいなものである。ここでものを言うのは圧倒的な知名度か、組織票である。参議院選挙区制が都大会なら、参議院比例代表は全国大会である。参議院比例代表のもう一つの特徴と複雑さを生むのが、「政党名と個人名のどちらを書いて投票してもよい」というルールだ。選挙公報では「比例はA党とお書きください」と書いている政党もあれば、「比例はB党もしくは候補者名をお書きください」と書いている政党もある。もし同じ政党内で比例にも自分の名前を書くようにアピールする候補者と、あくまで比例には政党名を書くようにとアピールする候補者が混在すると、自分の名前を書くよう求める候補者が順位の点では有利になる。一方党としての戦略としては、党名をアピールしたほうが今後につながるし、党全体の知名度アップも見込める。政党名を書いて投票することで、今後別の候補が比例代表で出馬したとしても、その票が維持されることが期待できるわけだ。
今回の選挙公報をみてみると、自民党や立憲民主党などは「政党名もしくは候補者名」としている。一方共産党や参政党は「政党名」と書いている。イデオロギーによる投票を期待する弱小寄りの政党は、政党名での投票を求める傾向があるのかもしれない。ところで日本保守党は「候補者名もしくは保守党」と順序が逆転している。これは日本保守党の比例代表候補が知名度が高い有名人がラインナップしているためだろう。
| 選挙 | 書ける名前 | 名簿 | 小選挙区との重複 | 特定枠 |
|---|---|---|---|---|
| 衆議院比例代表 | 政党名のみ | 拘束名簿 | 重複可(比例復活あり) | なし(ただし名簿1位に比例単独候補を載せることが実質的に特定枠として機能) |
| 参議院比例代表 | 政党名もしくは候補者名 | 非拘束名簿 | 重複不可(比例復活なし) | あり |
詳細はドント式の項目で解説するが、拘束名簿とは選挙前に名簿(当選する順序)を提出しておくことで、非拘束名簿は順序を決めておかないということ。当然ながら非拘束名簿でも比例代表に出馬するかどうかは事前に決まっており(当たり前である)、事前に設定しないのは順序である。重複可か不可かは、(小)選挙区と重複して立候補することができるかということ。バトルロワイヤルである衆議院小選挙区とちがい、参議院選挙区は複数名が当選するので、比例復活もない。
比例代表は(小)選挙区のように、わかりやすく候補者が獲得した票数で政党関係なしに当選が決まるわけではない。それを決めるのがドント式である。参議院と衆議院は両方とも比例代表はドント式である。
具体的には以下のような流れに従って各政党に議席が配分され、政党内で議席に候補者を割り当てる。
| ①各政党に議席配分 |
|---|
| ①-1 各党の総得票数を計算 /参議院比例:政党名+候補者名を合算 |
| ①-2 総得票数を1、2、3と整数で割る |
| ①-3 得られた計算結果を、政党関係なしに比べ、高い順から各政党に議席を配分する |
| ②政党内で議席に当選者を当てはめる |
| ②-1 いれば1位や2位の比例単独が当選する(いなければ②-2へ) /参議院比例:特定枠が当選する |
| ②-2 小選挙区の重複候補が惜敗率に応じて当選 /参議院比例:候補者名が書かれた獲得票数に応じて順番に候補者が当選 |
さて、実際にどのように名簿が書かれ当選するかみてみよう。まずは衆議院比例代表。
| 衆議院比例 | |
| 1位 | Aさん(比例代表単独) |
| 2位 | Bさん(小選挙区重複) |
| 〃 | Cさん(小選挙区重複) |
| 〃 | Dさん(小選挙区重複) |
| 〃 | (以下ずらっと小選挙区候補者名が並ぶ) |
| 28位 | Xさん(比例代表単独) |
ここでAさんはよほどの弱小政党でなければ必ず当選する。小選挙区には出てないので、政党が用意した特別枠である。そして2位にずらっと比例復活を狙った小選挙区候補者が並ぶ。ただし比例復活が批判される場合もあり(裏金問題などで批判され「 Permalink | 記事への反応(0) | 17:48
python import random import numpy as np import matplotlib.pyplot as plt from collections import defaultdict # 飴の配布システムのシミュレーション class CandyDistributionSystem: def __init__(self): """ 設計意図: このシステムは経済における資源分配の不平等性をモデル化しています。 特に少数の特権層(Aグループ)が富を集中させ、再分配システムからも不均衡に利益を得る 構造的問題を表現しています。 """ # 各グループの人数設定 self.group_a_count = 8 self.group_b_count = 2498 self.group_c_count = 7494 self.total_participants = self.group_a_count + self.group_b_count + self.group_c_count # 飴の提出数設定 self.contribution_per_a = 624 self.contribution_per_b = 2 self.contribution_per_c = 1 # 各グループの総貢献計算 self.total_a_contribution = self.group_a_count * self.contribution_per_a self.total_b_contribution = self.group_b_count * self.contribution_per_b self.total_c_contribution = self.group_c_count * self.contribution_per_c self.total_contribution = self.total_a_contribution + self.total_b_contribution + self.total_c_contribution # 配布用と貯金用の飴の区分 self.distribution_limit = 10000 self.savings = max(0, self.total_contribution - self.distribution_limit) # 結果追跡用の辞書 self.results = { 'A': defaultdict(int), 'B': defaultdict(int), 'C': defaultdict(int) } def distribute_candies(self, method='original'): """ 設計意図: 配布方法の選択によって、特権の固定化や格差拡大がどのように進むかを 示します。'original'メソッドは意図的にAグループを優遇するよう設計されています。 Parameters: ----------- method: str 配布方法 ('original', 'lottery', 'first_come', 'new_condition', 'fair') """ # Aグループへの確定配布 a_distribution = 625 * self.group_a_count remaining = self.distribution_limit - a_distribution # 残りの参加者数 remaining_participants = self.total_participants - self.group_a_count # Aグループの結果記録 for _ in range(self.group_a_count): self.results['A'][625] += 1 # 各配布方法によって処理が異なる if method == 'original': # オリジナルの問題設定通りの配布(5000人に1個ずつ、残りは0個) lucky_count = remaining # 5000人が当選 # B+Cグループの混合リスト作成 bc_participants = [(1, 'B')] * self.group_b_count + [(2, 'C')] * self.group_c_count random.shuffle(bc_participants) # 当選者に配布 for i in range(len(bc_participants)): participant_id, group = bc_participants[i] if i < lucky_count: self.results[group][1] += 1 else: self.results[group][0] += 1 elif method == 'lottery': # 抽選方式(BとCグループから無作為に5000人選出) bc_participants = [(1, 'B')] * self.group_b_count + [(2, 'C')] * self.group_c_count winners = random.sample(bc_participants, remaining) # 当選・落選のカウント for _, group in winners: self.results[group][1] += 1 # 落選者のカウント self.results['B'][0] = self.group_b_count - self.results['B'][1] self.results['C'][0] = self.group_c_count - self.results['C'][1] elif method == 'first_come': # 先着順方式(アクセス速度による先着順を乱数でシミュレート) # 設計意図: 先着順は単なる運の要素を超えて、情報格差や技術格差も含む制度設計 bc_participants = [(1, 'B')] * self.group_b_count + [(2, 'C')] * self.group_c_count # 現実では、情報を早く得られる人や高速インターネット接続を持つ人が有利 # これをシミュレートするため、Bグループにわずかなアドバンテージを与える bc_speeds = [] for id, group in bc_participants: if group == 'B': speed = random.random() + 0.1 # Bグループに小さなアドバンテージ else: speed = random.random() bc_speeds.append((id, group, speed)) # 速度順にソート bc_speeds.sort(key=lambda x: x[2], reverse=True) # 当選者決定 for i in range(len(bc_speeds)): _, group, _ = bc_speeds[i] if i < remaining: self.results[group][1] += 1 else: self.results[group][0] += 1 elif method == 'new_condition': # 追加条件方式(恣意的な条件を設定) # 設計意図: 新たな条件の設定は往々にして既存の特権を温存するように設計される bc_participants = [(i, 'B', random.random()) for i in range(self.group_b_count)] + \ [(i, 'C', random.random()) for i in range(self.group_c_count)] # Bグループに有利な条件を設定(例: 特定の知識やスキルを持つ人のみ) # この「条件」は表面上は中立的だが、実際には特定グループに有利になるよう設計 def meets_condition(participant): _, group, rand_val = participant if group == 'B': return rand_val > 0.3 # Bグループには70%の確率で合格 else: return rand_val > 0.7 # Cグループには30%の確率で合格 # 条件に合致する人を抽出 eligible = [p for p in bc_participants if meets_condition(p)] # 条件に合致する人が多すぎる場合は抽選 if len(eligible) > remaining: winners = random.sample(eligible, remaining) else: # 条件に合致する人が足りない場合、全員に配布 winners = eligible # 当選者をカウント for _, group, _ in winners: self.results[group][1] += 1 # 落選者のカウント self.results['B'][0] = self.group_b_count - self.results['B'][1] self.results['C'][0] = self.group_c_count - self.results['C'][1] elif method == 'fair': # 公平な再分配方式(貢献度に応じた配布) # 設計意図: この方法は「貯金分」も含めた全ての飴を、各グループの貢献度に応じて分配 # これにより構造的不平等を軽減、結果としてより多くの人が少なくとも損をしない状態になる # 全飴(貯金分も含む)を使った配布 total_to_distribute = self.total_contribution # 各グループの貢献比率計算 a_ratio = self.total_a_contribution / self.total_contribution b_ratio = self.total_b_contribution / self.total_contribution c_ratio = self.total_c_contribution / self.total_contribution # 各グループへの配布数決定 a_share = int(total_to_distribute * a_ratio) b_share = int(total_to_distribute * b_ratio) c_share = int(total_to_distribute * c_ratio) # 端数調整 remainder = total_to_distribute - (a_share + b_share + c_share) if remainder > 0: # 端数は最も人数の多いCグループに c_share += remainder # Aグループの配布(均等配分) per_a = a_share // self.group_a_count self.results['A'][per_a] = self.group_a_count # Bグループの配布(均等配分) per_b = b_share // self.group_b_count b_remainder = b_share % self.group_b_count self.results['B'][per_b] = self.group_b_count - b_remainder if per_b + 1 > 0 and b_remainder > 0: self.results['B'][per_b + 1] = b_remainder # Cグループの配布(均等配分) per_c = c_share // self.group_c_count c_remainder = c_share % self.group_c_count self.results['C'][per_c] = self.group_c_count - c_remainder if per_c + 1 > 0 and c_remainder > 0: self.results['C'][per_c + 1] = c_remainder def calculate_net_gain(self): """ 設計意図: この関数は各グループの純利益/損失を計算し、資源分配の公平性を 定量的に評価できるようにします。純利益/損失は個人の観点から見た経済的公正性の 重要な指標です。 """ net_gains = {} # Aグループの純利益計算 a_contribution = self.contribution_per_a a_distribution = list(self.results['A'].keys())[0] # 全員が同じ数を受け取る前提 net_gains['A'] = a_distribution - a_contribution # BとCグループの純利益計算(加重平均) for group, contribution_per_person in [('B', self.contribution_per_b), ('C', self.contribution_per_c)]: total_gain = 0 for received, count in self.results[group].items(): total_gain += (received - contribution_per_person) * count net_gains[group] = total_gain / (self.group_b_count if group == 'B' else self.group_c_count) return net_gains def analyze_results(self): """ 設計意図: この分析関数は、各グループの分配結果を詳細に調査し、 制度設計の公平性、貢献度と報酬の関係、およびシステムの持続可能性を 評価します。政策分析においては、こうした多角的な検証が重要です。 """ # 各グループの純利益/損失 net_gains = self.calculate_net_gain() # 貢献度分析 contribution_percentage = { 'A': (self.total_a_contribution / self.total_contribution) * 100, 'B': (self.total_b_contribution / self.total_contribution) * 100, 'C': (self.total_c_contribution / self.total_contribution) * 100 } # 飴を受け取った人の割合 received_percentage = { 'A': sum(count for received, count in self.results['A'].items() if received > 0) / self.group_a_count * 100, 'B': sum(count for received, count in self.results['B'].items() if received > 0) / self.group_b_count * 100, 'C': sum(count for received, count in self.results['C'].items() if received > 0) / self.group_c_count * 100 } # 分析結果の表示 print("\n===== 飴の配布システム分析 =====") print(f"総飴数: {self.total_contribution}個 (分配用: {self.distribution_limit}個, 貯金: {self.savings}個)") print("\n--- グループごとの貢献と結果 ---") for group in ['A', 'B', 'C']: group_size = getattr(self, f"group_{group.lower()}_count") contribution_per_person = getattr(self, f"contribution_per_{group.lower()}") total_contribution = getattr(self, f"total_{group.lower()}_contribution") print(f"\n{group}グループ ({group_size}人):") print(f" 貢献: 1人あたり{contribution_per_person}個 (総計: {total_contribution}個, 全体の{contribution_percentage[group]:.1f}%)") print(f" 受け取り状況:") for received, count in sorted(self.results[group].items()): print(f" {received}個: {count}人 ({count/group_size*100:.1f}%)") print(f" 飴を受け取った割合: {received_percentage[group]:.1f}%") print(f" 純利益/損失: 1人あたり平均 {net_gains[group]:.2f}個") print("\n--- 全体的な公平性分析 ---") print(f"最も得したグループ: {max(net_gains, key=net_gains.get)}グループ (+{max(net_gains.values()):.2f}個/人)") print(f"最も損したグループ: {min(net_gains, key=net_gains.get)}グループ ({min(net_gains.values()):.2f}個/人)") # 全員に飴が配布されたかどうか all_received = all(sum(count for received, count in self.results[group].items() if received > 0) == getattr(self, f"group_{group.lower()}_count") for group in ['A', 'B', 'C']) print(f"\n前提条件「全員に配布」の充足: {'はい' if all_received else 'いいえ'}") if not all_received: total_without = sum(self.results['B'][0] + self.results['C'][0]) print(f" 飴を受け取れなかった人数: {total_without}人") return net_gains, contribution_percentage, received_percentage def visualize_results(self): """ 設計意図: データの可視化は政策の効果や不平等性を直感的に理解するために重要です。 このようなグラフィカル表現によって、各グループ間の格差や制度設計の問題点を 一目で理解できるようになります。 """ # グラフのセットアップ fig, axes = plt.subplots(2, 2, figsize=(14, 10)) # 1. 貢献度のグラフ contributions = [self.total_a_contribution, self.total_b_contribution, self.total_c_contribution] axes[0, 0].bar(['Aグループ', 'Bグループ', 'Cグループ'], contributions) axes[0, 0].set_title('グループごとの総貢献飴数') axes[0, 0].set_ylabel('飴の数') # 貢献度の割合をアノテーションとして追加 total = sum(contributions) for i, v in enumerate(contributions): percentage = v / total * 100 axes[0, 0].text(i, v + 100, f'{percentage:.1f}%', ha='center') # 2. 1人あたりの貢献度と受け取り数の比較 group_names = ['Aグループ', 'Bグループ', 'Cグループ'] contribution_per_person = [self.contribution_per_a, self.contribution_per_b, self.contribution_per_c] # 各グループの平均受け取り数を計算 received_per_person = [] for group, letter in zip(group_names, ['A', 'B', 'C']): total_received = sum(received * count for received, count in self.results[letter].items()) group_size = getattr(self, f"group_{letter.lower()}_count") received_per_person.append(total_received / group_size) x = np.arange(len(group_names)) width = 0.35 axes[0, 1].bar(x - width/2, contribution_per_person, width, label='提出') axes[0, 1].bar(x + width/2, received_per_person, width, label='受け取り') # 純利益/損失をアノテーションとして追加 for i in range(len(group_names)): net = received_per_person[i] - contribution_per_person[i] color = 'green' if net >= 0 else 'red' axes[0, 1].text(i, max(received_per_person[i], contribution_per_person[i]) + 5, f'{"+" if net >= 0 else ""}{net:.1f}', ha='center', color=color) axes[0, 1].set_title('1人あたりの提出・受け取り飴数比較') axes[0, 1].set_xticks(x) axes[0, 1].set_xticklabels(group_names) axes[0, 1].set_ylabel('飴の数') axes[0, 1].legend() # 3. 各グループの受け取り状況の分布 # 各グループの受け取り状況を積み上げ棒グラフで表現 group_sizes = [self.group_a_count, self.group_b_count, self.group_c_count] received_counts = [] not_received_counts = [] for letter, size in zip(['A', 'B', 'C'], group_sizes): received = sum(count for received, count in self.results[letter].items() if received > 0) received_counts.append(received) not_received_counts.append(size - received) axes[1, 0].bar(group_names, received_counts, label='飴を受け取った人数') axes[1, 0].bar(group_names, not_received_counts, bottom=received_counts, label='飴を受け取れなかった人数') # 割合をアノテーションとして追加 for i in range(len(group_names)): if group_sizes[i] > 0: percentage = received_counts[i] / group_sizes[i] * 100 axes[1, 0].text(i, received_counts[i] / 2, f'{percentage:.1f}%', ha='center') axes[1, 0].set_title('グループごとの飴受け取り状況') axes[1, 0].set_ylabel('人数') axes[1, 0].legend() # 4. 貢献度vs報酬の分配公平性 # 貢献度と最終的な飴の配分の比較を円グラフで表現 total_contribution = self.total_contribution contribution_shares = [self.total_a_contribution / total_contribution, self.total_b_contribution / total_contribution, self.total_c_contribution / total_contribution] # 実際の配分シェアを計算 distribution_shares = [] for letter in ['A', 'B', 'C']: total_received = sum(received * count for received, count in self.results[letter].items()) distribution_shares.append(total_received / self.distribution_limit) # 2つの円グラフを並べて表示 ax4_1 = axes[1, 1].inset_axes([0, 0, 0.45, 1]) ax4_2 = axes[1, 1].inset_axes([0.55, 0, 0.45, 1]) ax4_1.pie(contribution_shares, labels=group_names, autopct='%1.1f%%') ax4_1.set_title('飴の貢献度割合') ax4_2.pie(distribution_shares, labels=group_names, autopct='%1.1f%%') ax4_2.set_title('飴の配分割合') axes[1, 1].axis('off') plt.tight_layout() plt.show() # 飴の配布システムをシミュレート candy_system = CandyDistributionSystem() # オリジナルの配布方法を実行 print("\n===== オリジナルの配布方法 =====") candy_system.distribute_candies(method='original') original_results = candy_system.analyze_results() candy_system.visualize_results() # 公平な配布方法を実験 print("\n\n===== 公平な配布方法のシミュレーション =====") fair_system = CandyDistributionSystem() fair_system.distribute_candies(method='fair') fair_results = fair_system.analyze_results() fair_system.visualize_results() # 公平な配布と元の配布の比較 print("\n\n===== 配布方法の比較 =====") print("オリジナル方式と公平方式の純利益/損失差:") net_diff = {} for group in ['A', 'B', 'C']: original_net = original_results[0][group] fair_net = fair_results[0][group] diff = fair_net - original_net net_diff[group] = diff print(f"{group}グループ: {'+' if diff > 0 else ''}{diff:.2f}個/人") print("\n結論:") if net_diff['A'] < 0 and net_diff['B'] > 0 and net_diff['C'] > 0: print("公平な再分配により、Aグループの特権が減少し、BとCグループの状況が改善されます。") print("これは構造的不平等の緩和に効果的です。") elif net_diff['A'] > 0: print("興味深いことに、公平な再分配ではAグループさえも利益を得られます。") print("これは、現行システムが特定グループだけでなく全体の非効率性につながっていることを示唆しています。")
2025年、私たちはソフトウェア開発の歴史的な転換点に立っている。大規模言語モデル(LLM)の進化は、GitHub Copilotのようなコード補完ツールに始まり、今や「何を作りたいか」を自然言語で伝えるだけで、アプリケーションの雛形が数分で生成される時代を現実のものとしつつある。この光景を目の当たりにした多くのプログラマが、漠然とした、しかし確かな不安を抱いているだろう。「私たちの仕事は、いずれAIに奪われるのではないか」と。
この問いに対する私の答えは、半分はYesであり、もう半分はNoだ。より正確に言えば、プログラマの仕事の本質が、歴史上かつてないレベルで抽象化され、その役割が再定義されるのだ。私たちは、コードを「書く」作業から解放される一方で、これまで以上に高度な思考を要求されることになる。
本稿では、プログラミングの歴史を「How(いかに作るか)」から「What(何を作るか)」への移行として捉え直し、LLMがこの流れをいかに加速させるかを論じる。そして、その先にある、AIには決して代替できない、人間ならではの競争優位性、すなわち「Why(なぜ作るのか)」を定義し、記述する能力の重要性について深く考察していく。これは、単なる未来予測ではない。今を生きるすべてのソフトウェアエンジニアにとっての、生存戦略の提示である。
LLMの登場を特異点として捉える前に、我々が立っている場所を正確に知る必要がある。ソフトウェア開発の歴史は、常に「抽象化」との戦いであった。そしてその歴史は、プログラマの関心が「How」から「What」へと徐々に移り変わっていくプロセスとして描くことができる。
コンピュータの黎明期、プログラミングとは、計算機が理解できる命令(How)を、一行一行、丹念に記述する作業そのものであった。アセンブリ言語や初期のFORTRAN、COBOLといった言語は、ハードウェアの制約を強く受けており、プログラマはメモリ管理やプロセッサの動作といった、極めて物理層に近いレベルでの「How」を意識する必要があった。
この時代のテストもまた、「How」に強く束縛されていた。書かれた手続きが、意図した通りに順番に実行されるか、特定の入力に対して期待された計算結果を返すか。テストの関心事は、あくまで「手続きの正しさ」の検証にあった。ビジネスロジックと実装の詳細が密結合し、コードは特定の処理手順を記述した、硬直的な塊となっていた。
風向きが変わり始めたのは、ソフトウェアの規模が拡大し、その複雑性が人間の認知能力を超え始めた頃だ。1990年代後半から2000年代にかけて提唱されたエクストリーム・プログラミング(XP)の中で、テスト駆動開発(TDD)という考え方が登場する。
TDDの本質は、単なるテスト手法の改善ではない。それは、プログラミングのパラダイムを根底から覆す思想だった。TDDは、「まずテストを書く」ことを強制することで、プログラマの意識を「これから実装するコード(How)」から「そのコードが満たすべき振る舞い(What)」へと強制的に転換させたのだ。
テストはもはや、書かれたコードの後追いで正しさを検証する作業ではない。それは、これから作られるべきソフトウェアの「仕様書」であり、「振る舞いの宣言」となった。例えば、「ユーザーがログインボタンをクリックしたら、ダッシュボード画面に遷移する」というテストコードは、具体的な実装方法(`onClick`イベントハンドラの中で`window.location.href`を書き換える、など)には一切言及しない。それはただ、達成されるべき「What」を記述しているだけだ。
この思想は、ビヘイビア駆動開発(BDD)へと発展し、`Given-When-Then`といった、より自然言語に近い形式でソフトウェアの振る舞いを記述するスタイルを生み出した。プログラマだけでなく、プロダクトマネージャーやビジネスアナリストといった非技術者をも巻き込み、「What」を共通言語として定義する試みが本格化したのである。
TDD/BDDによってプログラマの意識が「What」に向かい始めると、コードそのものもまた、宣言的なスタイルへと進化していく。この変化を劇的に加速させたのが、モダンなフレームワークの存在だ。
Reactを例に考えてみよう。Reactが登場する前、フロントエンド開発はjQueryに代表されるように、DOMを直接操作する命令的なコード(How)の連続だった。「このボタンがクリックされたら、この要素のテキストを書き換え、あちらの要素を非表示にする」といった具合だ。
しかし、Reactは「UIとは、ある状態(state)に対する純粋な写像である」という宣言的なモデルを提示した。プログラマがやるべきことは、UIの状態(`state`)と、その状態がどのように見えるか(JSXによるコンポーネント)を宣言することだけだ。状態が変更された際に、DOMをどのように効率的に更新するかという面倒な「How」の部分は、Reactの仮想DOMと差分検出アルゴリズムがすべて隠蔽してくれる。プログラマは「What(UIのあるべき姿)」を記述するだけでよくなったのだ。
この「WhatからHowへの変換」は、様々な領域で見られる。
これらのフレームワークやツールは、いわば「特定の制約下における、WhatからHowへの高性能な変換器」として機能してきた。プログラマは、フレームワークが課す「お作法」や「制約」を受け入れることで、退屈で間違いの多い「How」の記述から解放され、より本質的な「What」の定義に集中できるようになった。我々が「生産性が高い」と感じる開発体験は、この優れた変換器の恩恵に他ならない。
現状は、この歴史的変遷の延長線上にある。プログラマの仕事は、手続きを記述する職人から、振る舞いを定義し、それを実現するための最適な「変換器(フレームワーク)」を選択・設定するアーキテクトへと、その重心を移してきたのだ。
フレームワークがもたらした「WhatからHowへ」の潮流は、LLMの登場によって、未曾有のスケールで加速されようとしている。フレームワークが「特定の領域に特化した変換器」であったのに対し、LLMは「あらゆる領域に対応可能な、究極の汎用変換器」としてのポテンシャルを秘めているからだ。
前章で述べたように、ReactやTerraformといったフレームワークは、その恩恵と引き換えに、私たちに特定の「制約」を課してきた。Reactを使うならコンポーネントベースで思考し、状態管理の作法に従う必要がある。Terraformを使うなら、そのエコシステムとHCLの流儀を受け入れなければならない。これらの制約は、WhatからHowへの変換を自動化するための「レール」であり、私たちはそのレールの上を走ることで効率を得てきた。
しかし、LLMはこの前提を覆す。LLMは、特定のフレームワークや言語の知識を事前に学習しているが、その利用において絶対的な制約を課すわけではない。私たちは、より自由な形式で「What」を伝えることができる。
例えば、こうだ。
ユーザー認証機能付きのシンプルなブログアプリを作ってほしい。フロントエンドはReactとTypeScript、UIコンポーネントはMUIを使う。バックエンドはNode.jsとExpressで、データベースはPostgreSQL。ユーザーはGoogleアカウントでログインでき、新しい記事を作成、編集、削除できる。記事にはマークダウン記法が使えて、画像もアップロードできるようにしてほしい。
この要求(What)は、特定のフレームワークの流儀に則ったものではない。複数の技術スタックを横断し、機能要求を自然言語で並べただけのものである。しかし、現在のLLM、特にGPT-4oやそれに類するモデルは、このレベルの要求から、ディレクトリ構造、設定ファイル、APIエンドポイント、フロントエンドコンポーネントに至るまで、驚くほど具体的なコード(How)を生成することができる。
これは、フレームワークが担ってきた「WhatからHowへの変換」が、特定のレールから解き放たれ、より広範で柔軟な領域へと拡張されたことを意味する。これまで自動化が難しかった、あるいは特定のフレームワークが存在しなかったニッチな領域や、複数の技術を組み合わせる複雑なシステム構築においても、AIによる宣言的プログラミングの恩恵を受けられる時代が始まろうとしているのだ。
LLMという汎用変換器の登場により、プログラマの生産性は、「いかに質の高いWhatをLLMに伝えられるか」に直結するようになる。これは、俗に「プロンプトエンジニアリング」と呼ばれるスキルだが、その本質は、ソフトウェア開発における「要求定義」そのものである。
質の高い「What」とは何か。それは、曖昧性がなく、網羅的で、矛盾のない要求である。
これらは、優秀なソフトウェアエンジニアが、プロダクトマネージャーやデザイナーとの対話を通じて、日常的に行ってきた思考プロセスそのものではないだろうか。LLMの登場は、この思考プロセスを、より明確に、よりテキストベースで「記述」する能力を求める。私たちの頭の中にあった暗黙的な仕様が、LLMへの入力(プロンプト)という形で、明示的に言語化されることを要求するのだ。
やがて、ほとんどのプログラミング作業は、この「Whatの記述」に収束していくだろう。TDDがテストコードという形式で「What」を記述したように、私たちは自然言語や、より構造化された要求記述言語を用いて、AIに対して「What」を宣言することになる。コード(How)は、その宣言から自動生成される中間生成物に過ぎなくなる。まさに、コードが蒸発していく未来である。
「What」を伝えれば「How」が手に入る。この魔法のような世界の到来を前に、私たちは一つの重大な問いに直面する。それは、「そのWhatからHowへの変換は、本当に一意に決まるのか?」という問いだ。
答えは、明確にNoである。
ある「What(要求)」を実現するための「How(実装)」は、無数に存在する。そして、どの「How」を選択すべきかを決定するためには、単純な機能要求(What)だけでは情報が全く足りない。そこには、必ず「Why(なぜそう作るのか)」という、背景、文脈、そしてトレードオフの考慮が必要不可欠となる。
簡単な例を考えてみよう。「1億件のユーザーデータを格納し、ユーザーIDで高速に検索できるシステム」という「What」をLLMに与えたとする。LLMは、どのような「How」を提案するだろうか。
これらの選択肢は、どれも「What」を満たしている。しかし、その特性は全く異なる。案Aは多くのエンジニアにとって馴染み深く開発が容易だが、10億、100億件へのスケールは難しいかもしれない。案Bはスケール性に優れるが、厳密なトランザクション管理は苦手だ。案Cは高速だが、運用コストとシステムの複雑性が増す。案Dは安価だが、検索速度は他に劣る。
LLMは、これらの選択肢をリストアップすることはできるだろう。しかし、このプロジェクトにとって最適な選択肢はどれかを、自信を持って決定することはできない。なぜなら、その決定には、LLMが与えられていない「Why」の情報が必要だからだ。
これらの「Why」こそが、無数に存在する「How」の中から、ただ一つの「正解」を選び出すための羅針盤なのである。そしてこの「Why」は、ビジネスの目標、組織の文化、ユーザーの期待、技術的な制約といった、極めて人間的で、文脈依存的な情報の中にしか存在しない。
ここで重要なのは、これまでもエンジニアは、この「Why」に基づく意思決定を、意識的あるいは無意識的に行ってきたという事実だ。
私たちが技術選定を行うとき、単に「流行っているから」という理由だけでReactを選ぶわけではない。「SPA(Single Page Application)にすることでユーザー体験を向上させたい(Why)」、「コンポーネント指向の開発によって長期的な保守性を確保したい(Why)」、「Reactエンジニアの採用市場が活発だから(Why)」といった、様々な「 Permalink | 記事への反応(0) | 17:09
2025年、私たちはソフトウェア開発の歴史的な転換点に立っている。大規模言語モデル(LLM)の進化は、GitHub Copilotのようなコード補完ツールに始まり、今や「何を作りたいか」を自然言語で伝えるだけで、アプリケーションの雛形が数分で生成される時代を現実のものとしつつある。この光景を目の当たりにした多くのプログラマが、漠然とした、しかし確かな不安を抱いているだろう。「私たちの仕事は、いずれAIに奪われるのではないか」と。
この問いに対する私の答えは、半分はYesであり、もう半分はNoだ。より正確に言えば、プログラマの仕事の本質が、歴史上かつてないレベルで抽象化され、その役割が再定義されるのだ。私たちは、コードを「書く」作業から解放される一方で、これまで以上に高度な思考を要求されることになる。
本稿では、プログラミングの歴史を「How(いかに作るか)」から「What(何を作るか)」への移行として捉え直し、LLMがこの流れをいかに加速させるかを論じる。そして、その先にある、AIには決して代替できない、人間ならではの競争優位性、すなわち「Why(なぜ作るのか)」を定義し、記述する能力の重要性について深く考察していく。これは、単なる未来予測ではない。今を生きるすべてのソフトウェアエンジニアにとっての、生存戦略の提示である。
LLMの登場を特異点として捉える前に、我々が立っている場所を正確に知る必要がある。ソフトウェア開発の歴史は、常に「抽象化」との戦いであった。そしてその歴史は、プログラマの関心が「How」から「What」へと徐々に移り変わっていくプロセスとして描くことができる。
コンピュータの黎明期、プログラミングとは、計算機が理解できる命令(How)を、一行一行、丹念に記述する作業そのものであった。アセンブリ言語や初期のFORTRAN、COBOLといった言語は、ハードウェアの制約を強く受けており、プログラマはメモリ管理やプロセッサの動作といった、極めて物理層に近いレベルでの「How」を意識する必要があった。
この時代のテストもまた、「How」に強く束縛されていた。書かれた手続きが、意図した通りに順番に実行されるか、特定の入力に対して期待された計算結果を返すか。テストの関心事は、あくまで「手続きの正しさ」の検証にあった。ビジネスロジックと実装の詳細が密結合し、コードは特定の処理手順を記述した、硬直的な塊となっていた。
風向きが変わり始めたのは、ソフトウェアの規模が拡大し、その複雑性が人間の認知能力を超え始めた頃だ。1990年代後半から2000年代にかけて提唱されたエクストリーム・プログラミング(XP)の中で、テスト駆動開発(TDD)という考え方が登場する。
TDDの本質は、単なるテスト手法の改善ではない。それは、プログラミングのパラダイムを根底から覆す思想だった。TDDは、「まずテストを書く」ことを強制することで、プログラマの意識を「これから実装するコード(How)」から「そのコードが満たすべき振る舞い(What)」へと強制的に転換させたのだ。
テストはもはや、書かれたコードの後追いで正しさを検証する作業ではない。それは、これから作られるべきソフトウェアの「仕様書」であり、「振る舞いの宣言」となった。例えば、「ユーザーがログインボタンをクリックしたら、ダッシュボード画面に遷移する」というテストコードは、具体的な実装方法(`onClick`イベントハンドラの中で`window.location.href`を書き換える、など)には一切言及しない。それはただ、達成されるべき「What」を記述しているだけだ。
この思想は、ビヘイビア駆動開発(BDD)へと発展し、`Given-When-Then`といった、より自然言語に近い形式でソフトウェアの振る舞いを記述するスタイルを生み出した。プログラマだけでなく、プロダクトマネージャーやビジネスアナリストといった非技術者をも巻き込み、「What」を共通言語として定義する試みが本格化したのである。
TDD/BDDによってプログラマの意識が「What」に向かい始めると、コードそのものもまた、宣言的なスタイルへと進化していく。この変化を劇的に加速させたのが、モダンなフレームワークの存在だ。
Reactを例に考えてみよう。Reactが登場する前、フロントエンド開発はjQueryに代表されるように、DOMを直接操作する命令的なコード(How)の連続だった。「このボタンがクリックされたら、この要素のテキストを書き換え、あちらの要素を非表示にする」といった具合だ。
しかし、Reactは「UIとは、ある状態(state)に対する純粋な写像である」という宣言的なモデルを提示した。プログラマがやるべきことは、UIの状態(`state`)と、その状態がどのように見えるか(JSXによるコンポーネント)を宣言することだけだ。状態が変更された際に、DOMをどのように効率的に更新するかという面倒な「How」の部分は、Reactの仮想DOMと差分検出アルゴリズムがすべて隠蔽してくれる。プログラマは「What(UIのあるべき姿)」を記述するだけでよくなったのだ。
この「WhatからHowへの変換」は、様々な領域で見られる。
これらのフレームワークやツールは、いわば「特定の制約下における、WhatからHowへの高性能な変換器」として機能してきた。プログラマは、フレームワークが課す「お作法」や「制約」を受け入れることで、退屈で間違いの多い「How」の記述から解放され、より本質的な「What」の定義に集中できるようになった。我々が「生産性が高い」と感じる開発体験は、この優れた変換器の恩恵に他ならない。
現状は、この歴史的変遷の延長線上にある。プログラマの仕事は、手続きを記述する職人から、振る舞いを定義し、それを実現するための最適な「変換器(フレームワーク)」を選択・設定するアーキテクトへと、その重心を移してきたのだ。
フレームワークがもたらした「WhatからHowへ」の潮流は、LLMの登場によって、未曾有のスケールで加速されようとしている。フレームワークが「特定の領域に特化した変換器」であったのに対し、LLMは「あらゆる領域に対応可能な、究極の汎用変換器」としてのポテンシャルを秘めているからだ。
前章で述べたように、ReactやTerraformといったフレームワークは、その恩恵と引き換えに、私たちに特定の「制約」を課してきた。Reactを使うならコンポーネントベースで思考し、状態管理の作法に従う必要がある。Terraformを使うなら、そのエコシステムとHCLの流儀を受け入れなければならない。これらの制約は、WhatからHowへの変換を自動化するための「レール」であり、私たちはそのレールの上を走ることで効率を得てきた。
しかし、LLMはこの前提を覆す。LLMは、特定のフレームワークや言語の知識を事前に学習しているが、その利用において絶対的な制約を課すわけではない。私たちは、より自由な形式で「What」を伝えることができる。
例えば、こうだ。
ユーザー認証機能付きのシンプルなブログアプリを作ってほしい。フロントエンドはReactとTypeScript、UIコンポーネントはMUIを使う。バックエンドはNode.jsとExpressで、データベースはPostgreSQL。ユーザーはGoogleアカウントでログインでき、新しい記事を作成、編集、削除できる。記事にはマークダウン記法が使えて、画像もアップロードできるようにしてほしい。
この要求(What)は、特定のフレームワークの流儀に則ったものではない。複数の技術スタックを横断し、機能要求を自然言語で並べただけのものである。しかし、現在のLLM、特にGPT-4oやそれに類するモデルは、このレベルの要求から、ディレクトリ構造、設定ファイル、APIエンドポイント、フロントエンドコンポーネントに至るまで、驚くほど具体的なコード(How)を生成することができる。
これは、フレームワークが担ってきた「WhatからHowへの変換」が、特定のレールから解き放たれ、より広範で柔軟な領域へと拡張されたことを意味する。これまで自動化が難しかった、あるいは特定のフレームワークが存在しなかったニッチな領域や、複数の技術を組み合わせる複雑なシステム構築においても、AIによる宣言的プログラミングの恩恵を受けられる時代が始まろうとしているのだ。
LLMという汎用変換器の登場により、プログラマの生産性は、「いかに質の高いWhatをLLMに伝えられるか」に直結するようになる。これは、俗に「プロンプトエンジニアリング」と呼ばれるスキルだが、その本質は、ソフトウェア開発における「要求定義」そのものである。
質の高い「What」とは何か。それは、曖昧性がなく、網羅的で、矛盾のない要求である。
これらは、優秀なソフトウェアエンジニアが、プロダクトマネージャーやデザイナーとの対話を通じて、日常的に行ってきた思考プロセスそのものではないだろうか。LLMの登場は、この思考プロセスを、より明確に、よりテキストベースで「記述」する能力を求める。私たちの頭の中にあった暗黙的な仕様が、LLMへの入力(プロンプト)という形で、明示的に言語化されることを要求するのだ。
やがて、ほとんどのプログラミング作業は、この「Whatの記述」に収束していくだろう。TDDがテストコードという形式で「What」を記述したように、私たちは自然言語や、より構造化された要求記述言語を用いて、AIに対して「What」を宣言することになる。コード(How)は、その宣言から自動生成される中間生成物に過ぎなくなる。まさに、コードが蒸発していく未来である。
「What」を伝えれば「How」が手に入る。この魔法のような世界の到来を前に、私たちは一つの重大な問いに直面する。それは、「そのWhatからHowへの変換は、本当に一意に決まるのか?」という問いだ。
答えは、明確にNoである。
ある「What(要求)」を実現するための「How(実装)」は、無数に存在する。そして、どの「How」を選択すべきかを決定するためには、単純な機能要求(What)だけでは情報が全く足りない。そこには、必ず「Why(なぜそう作るのか)」という、背景、文脈、そしてトレードオフの考慮が必要不可欠となる。
簡単な例を考えてみよう。「1億件のユーザーデータを格納し、ユーザーIDで高速に検索できるシステム」という「What」をLLMに与えたとする。LLMは、どのような「How」を提案するだろうか。
これらの選択肢は、どれも「What」を満たしている。しかし、その特性は全く異なる。案Aは多くのエンジニアにとって馴染み深く開発が容易だが、10億、100億件へのスケールは難しいかもしれない。案Bはスケール性に優れるが、厳密なトランザクション管理は苦手だ。案Cは高速だが、運用コストとシステムの複雑性が増す。案Dは安価だが、検索速度は他に劣る。
LLMは、これらの選択肢をリストアップすることはできるだろう。しかし、このプロジェクトにとって最適な選択肢はどれかを、自信を持って決定することはできない。なぜなら、その決定には、LLMが与えられていない「Why」の情報が必要だからだ。
これらの「Why」こそが、無数に存在する「How」の中から、ただ一つの「正解」を選び出すための羅針盤なのである。そしてこの「Why」は、ビジネスの目標、組織の文化、ユーザーの期待、技術的な制約といった、極めて人間的で、文脈依存的な情報の中にしか存在しない。
ここで重要なのは、これまでもエンジニアは、この「Why」に基づく意思決定を、意識的あるいは無意識的に行ってきたという事実だ。
私たちが技術選定を行うとき、単に「流行っているから」という理由だけでReactを選ぶわけではない。「SPA(Single Page Application)にすることでユーザー体験を向上させたい(Why)」、「コンポーネント指向の開発によって長期的な保守性を確保したい(Why)」、「Reactエンジニアの採用市場が活発だから(Why)」といった、様々な「 Permalink | 記事への反応(0) | 17:09
---
# **M-98C《ヴァニタス》🇳🇴 ノースソヴリン連邦空軍 出撃個人記録**
| 項目 | 内容 |
| 任務番号 | **NSF-JSOC 25-10-17-Σ** |
| 目的 | 東アジア経済圏に対する**限定的経済/通信遮断実証** |
| 発令主体 | ノースソヴリン連邦 極地戦略合同司令部(JSOC) |
| 離陸基地 | **スカディ飛行場**(72°45′N 178°32′E, ノヴァシベリア諸島) |
| 再突入高度 | 35 km |
| 武装 | **HGV-B73 “Dawn Shard”** ×3 |
> *ノースソヴリン連邦*…北極海資源を背景に台頭した極寒のシ―ステイツ。表向きはNATO傘下だが、実質は独立経済圏を構築。今回の作戦は「輸出レアメタル決済を量子網に限定させる交渉カード」として計画された。
---
> *HUD:* “L-1, INS ALIGN OK. SCRJ READY.”
寒霧の滑走路。機体は液体窒素噴霧で外板温度を均一化。ステルス皮膜のシリコン閃光が月光を弾く。
「政治じゃない。ただの“工学実証”さ」。整備士の捨て台詞が耳に残った。
---
> *VALIS:* “日本防空レーダ― ソーンソート γ 帯…ダウンリンク阻害完了。”
---
私は\*\*「品川シーサイド・クラスタ」\*\*を地図上でタップ。黄色のリング。
> *VALIS:* “推定非戦闘員被害 < 1 k.”
> 心の声: 「数字にしてくれると、罪悪感も概算できるのか」
コクピット温度 48 ℃。耐Gスーツの微振動が脈拍を代行する。
---
瞬間、**EMPフラッシュ**が湾岸の灯りを片側だけ喰った。
---
> *VALIS:* “目的達成率 62 % … 臨界閾値到達。”
---
## 04:25 JST ― サードリリース & 離脱
旋回 G が抜けた瞬間、**東京タワーのシルエット**が視界に入る — オレンジの灯がまだ点いていた。
私が破壊したのは都市ではなく、都市を繋ぐ**約束事の網**。だが網の上には人が立っている。
---
機体外板にプラズマ浸し。レーダー断面積 0.0009 m²。
日本列島の監視網が\*\*“ここに私がいた痕跡”\*\*を掴めないよう、電子的に自分を溶かす。
“Checkmate?”
いや、まだ。これは詰めろの一手だ。
---
## 05:18 JST ― カムチャツカ沖・補給艇 Rendez-vous
機体を折り畳むように格納庫へ滑り込む。
> **司令官:** 「通信インフラの復旧見積りは?」
> **司令官:** 「なら充分だ。マーケットは怖がる」
私は黙って頭を下げた。
---
* “ノースソヴリン”は自国領を一発も撃たずに、通貨スワップ再交渉の座を得る。
* 私たちが名乗る日は来ない。名乗った瞬間、交渉カードが紙切れになる。
3. **次回があるなら**
* それが「戦争をしないための爆撃」の完成形だと信じたい。
---
> “任務完遂。民間死傷 0.06 % — 許容範囲内。
> Good morning, Lazarus-1. The world will wake up different.”
そして自問した。
まず、もしテーブルが小さいならそれこそJOINなんてそもそも無駄。
usersが1万件くらいのサイズなら、最初に全件引いて辞書にしておけば、処理時は全部O(1)。
一方JOINはどうなる?SQL構文パース、オプティマイザ、プランの生成と実行、インデックス参照、場合によってはソート・一時テーブル、何重にもコストがかかる。JOIN使うのは、全力で自己放尿してるのと同じ。
「今後も巨大にはならない」
これ、現実逃避の典型な。開発初期で小さくても、プロダクトってのは使われてナンボ。使われればデータは自然に増える。
さらに「本当に巨大なら辞書は無理」って言ってるけど、じゃあJOINならいけると?
脳ミソの冷却ちゃんと回ってるか?
JOINってのは重いんだよ。リレーショナル演算のコスト、現場でまともに見積もったことあるか?
JOINするたびに何十万、何百万件ってレコード舐めて、インデックス使って、それでもI/Oボトルネック起きる。
そういうの避けるためにRedisとか列指向DBとかプリマテリアライズするんだろ。JOINは最適解じゃない、最後の逃げだよ。
結局、JOINを正当化する理由が「JOIN以外知らない」ってだけじゃねえの?
設計手段を学ばず、「それしか知らない」ことを自信に変えるな。
知識の不足を理屈で補うのは無理がある。JOINを使うなとは言わん。でも、JOINが最適って言うなら、それ相応の読み、キャッシュ設計、オプティマイザとの対話が必要だ。
農林水産大臣が更迭された。失言ひとつで更迭というのも極端な気もするがコメ不足というのはそれだけ切羽詰まった問題なのだろう。前にスーパーでコメを買ったのはいつだったか。田舎の方に住んでいるため、コメが店頭から消えるということはなかったように思う。
更迭を「更送」と書いている人がいた。確かに字の形はそっくりである。しんにょうの中身は失うか朕のつくりか。大抵の機器には漢字変換が搭載されるようになったが、読み方を知らなければ漢字変換を利用することもできない。読み方を調べるにも一工夫が必要だ。(iPhoneなら選択して調べるという簡単なコマンドがある)
私の学生時代はタッチパネルに漢字を書ける電子辞書というものがあった。いまでもスマホが利用できない学生は使っているかもしれない。それより前は漢字辞典を開いたのだろうか。部首でソートされた事典ははじめ非常に使いにくかったが、今になってありがたみを感じる。
我々は漢字辞典の使い方から初等教育を受けているのだから、読めない漢字があることは恥ずかしいことではないが、知ろうとしないことはやはり恥である。
ところで、果たして私もこの円マークに足がついた漢字の読み方を知らなかった。しかし手元に電子辞書も漢字辞典もない。部首も見当がつかない。「天」という部首は存在するだろうか。そう思ってGoogleで「天にちょんちょん」と検索した。これで見つかるのだから大変便利な時代になったものである。せっかくなので読み方は皆さんそれぞれで確認していただきたい。
最後に、「斜に構える」とはなんと読むだろうか。私はかつてよりずっと「はすにかまえる」と読み、発音していたが「しゃにかまえる」が正解らしい。私一人かとも思ったのだが小学校から大学まで一緒だった友人が「はすにかまえる」と発音したのを聞いた。一種の方言なのか、単なる誤用なのか。その友人とは久しぶりに顔を合わせたのだが奇妙な縁を感じ帰路についた。
bookliveとかの検索結果って理論的にはページ送りするたびに結果が再生成されるだろ。
漫画全部知りたいみたいな動機で漫画全部引っかかるような検索条件で結果出すと、全部探している間にどんどん検索順位が変わるわけで、ページ送りしているうちに、まだ見てないページにある漫画が、順位の更新で今まで見たページに移動することが十分な可能性あるわけだけし、しかもそれが何周しようと起こる。何周してもそのbookliveとかに登録されている漫画を全て確認しようとしても漏れが出る可能性はなくせないってことなんだよな。
これは人気順のようなアルゴリズム不明なソートならいうまでもなく最新順でも起こりうること(掘り下げてまた後日このへん書くかもしれない)。
なんとか、ページ送りのさせた方でもなんでもいいけど、こういう結果生成の方法をとっている物に対して漏れなくその検索条件でヒットする項目を抽出できるようなアルゴリズムって考えられないものなのかねえ?