OmniAICreator/Qiita-1.07M huggingface dataset:
AIは“わかり合う”ことができるか？大規模言語モデルによる説明対話の最新研究

# AIは“わかり合う”ことができるか？大規模言語モデルによる説明対話の最新研究
今回は、最新の研究成果である「Investigating Co-Constructive Behavior of Large Language Models in Explanation Dialogues」という論文をご紹介します。  
この研究は、**大規模言語モデル（LLMs）が、対話を通じてユーザーと理解を「共に築く」ことができるか**を体系的に検証したものです。AIが一方的に説明するだけでなく、ユーザーの理解度を観察し、柔軟に説明を変化させることの重要性に挑戦した意欲的な試みです。

---

### 論文情報
- **タイトル**: Investigating Co-Constructive Behavior of Large Language Models in Explanation Dialogues
- **リンク**: [arXiv:2504.18483v1](https://arxiv.org/abs/2504.18483)
- **発表日**: 2025年4月25日
- **著者**: Leandra Fichtel, Maximilian Spliethöver, Eyke Hüllermeier, Patricia Jimenez, Nils Klowait, Stefan Kopp, Axel-Cyrille Ngonga Ngomo, Amelie Robrecht, Ingrid Scharlau, Lutz Terfloth, Anna-Lisa Vollmer, Henning Wachsmuth
- **DOI**: なし（arXiv preprint）

---

## 背景と目的
近年、**Explainable AI (XAI)**への関心が急速に高まっています。しかし、多くの既存手法は、静的で一方通行な説明にとどまっています。  
現実のコミュニケーションでは、理解は**対話の中で動的に構築されていく**ものです。

本研究が注目したのは、
- **モニタリング**（ユーザーの理解状況を常に観察する）
- **スキャフォールディング**（必要に応じて支援のレベルを調整する）

という教育学的な技術を、LLMに適用できるかどうかという点です。

目的は、次の三つの問いに答えることでした。

1. LLMは共同構成型の説明者として設計できるか？
2. LLMは自然に共同構成的な行動を取れるか？
3. その結果、ユーザーの理解度は向上するか？

---

## 研究の焦点
研究では、**2種類のプロンプト**を設定して、Llama 3.1（70Bモデル）に対して対話実験を行いました。

| 設定 | 内容 |
|:----|:----|
| Base Setting | 「説明者として振る舞え」とだけ指示 |
| Enhanced Setting | 「モニタリングとスキャフォールディングを使いながら説明せよ」と詳細指示 |

---

## 実験の概要と結果

### 実験デザイン
- **参加者**: 300名（データクリーニング後277名）
- **プラットフォーム**: Prolific
- **トピック**:  
  - クアルト（ボードゲーム）
  - ブラックホールの形成
  - 人間の睡眠サイクル
- **手順**:
  - 15分間、LLMとチャット
  - 対話前後で「自己評価型理解度・モチベーション」の測定
  - 対話後に「客観的理解度・応用力」のテスト

### 定量的結果まとめ

| 指標 | Base Setting | Enhanced Setting |
|:-----|:-------------|:-----------------|
| 平均対話時間 | 12分26秒 | 13分25秒 |
| 平均ユーザー発話数 | 8.2回 | 9.3回 |
| LLM応答の平均文数 | 17.8文 | 12.0文 |

- **Enhanced Settingでは対話がよりインタラクティブ**になり、参加者の発言機会が増えました。
- **モニタリング行動**（例: 理解確認質問、知識チェック）が有意に増加しました。
- **主観的な理解度**（自己申告）は、両設定ともに大きく向上。
- **客観的な理解度**（クイズ正答率）は、平均では大差なし。ただし**個別に見ると伸びた参加者も増加**しました。

---

### モニタリングとスキャフォールディングの検証
#### モニタリング
- Enhanced Settingでは、LLMが「理解できていますか？」「これについてどう思いますか？」といった**確認質問を多用**。
- 参加者も自発的な発言が増え、**対話への能動的参加**が促されました。

#### スキャフォールディング
- 「分からない」というシグナルがあった場合、**説明の難易度を下げる**動きが一部で観察されました。
- ただし、**適応の一貫性には課題**があり、すべての参加者に適切な調整ができたわけではありません。

---

### 理解度への影響
| 指標 | Base Setting | Enhanced Setting |
|:-----|:-------------|:-----------------|
| 客観的理解度（平均正答率） | 75.1% | 74.6% |
| 応用力（enabledness） | 67.0% | 69.5% |
| 共同構成評価（自己申告） | 3.7/5.0 | **4.1/5.0** |

- 主観的には、Enhanced Settingの方が**「一緒に理解を作った」感覚が高かった**。
- 客観的な理解度向上にはつながる場合とそうでない場合があり、**効果は個人差が大きい**ことが示唆されました。

---

## 賛否両論
### 賛成意見
- **プロンプト設計だけで、LLMの共同構成型対話が部分的に引き出せた**のは画期的。
- 確認質問を適切に挟むことで、**ユーザーの能動性を高める効果**が確認できた。
- 教育分野や対話型サポート（ヘルプデスク、コーチング）への応用可能性を感じさせる。

### 反対意見
- **スキャフォールディングが安定しない**：特に複雑な話題では、かえって説明が難しくなることもあった。
- **モニタリングが表層的にとどまる場合がある**：単に質問するだけでは、真の理解度把握にはならない。
- **本格的な共同構成（適応的対話）にはまだ達していない**：現時点では「擬似的な協調」にとどまる。

---

## 総合評価と今後の展望
本研究は、**「対話的に理解を築くAI」への第一歩**を確実に踏み出しました。  
特に、**適切なプロンプト設計がLLMの対話能力を大きく引き出す**可能性を示した点が非常に重要です。

しかし、本当に人間に近い「共に考え、共に成長する」AIを目指すならば、
- より高度なモニタリング技術
- ダイナミックでリアルタイムなスキャフォールディング
- マルチモーダルな感情理解（表情、声色など）

といったさらなる技術革新が必要不可欠でしょう。

---

この記事が、皆さんの研究や実務に少しでも貢献できれば幸いです。  
ご質問やディスカッションは、ぜひコメント欄へお寄せください！
アンサンプル学習

**アンサンブル学習（Ensemble Learning）**とは、**複数のモデルを組み合わせて、単一のモデルよりも精度の高い予測を行う手法**のことです。個々のモデル（「弱学習器」）の予測結果を統合することによって、全体としての予測性能を向上させます。

---

### 特徴
- **複数のモデル**を使用
- 各モデルの予測を統合して、最終的な予測を出す
- 単一のモデルに比べて**精度の向上**や**過学習の抑制**に役立つ

---

### 主なアンサンブル学習の手法

1. **バギング（Bagging）**
   - 複数の学習器を独立に学習させ、その結果を平均や多数決でまとめる。
   - 代表例：**ランダムフォレスト（Random Forest）**
   - 主に**過学習**の抑制に効果的。

2. **ブースティング（Boosting）**
   - 各学習器を順番に学習させ、前のモデルの誤分類を重視して次のモデルを改善する。
   - 代表例：**AdaBoost、XGBoost、LightGBM**
   - **精度向上**に効果的だが、過学習に注意が必要。

3. **スタッキング（Stacking）**
   - 異なるモデルを複数使用し、その予測結果を新たなモデル（メタ学習器）で学習させる。
   - 複数のモデルの**予測結果を統合**し、さらに良い予測を行う。

---

### メリット
- **予測精度の向上**：複数のモデルを組み合わせることで、単一のモデルよりも高い精度を得やすい。
- **過学習の抑制**：異なるモデルが協力することで、特定の誤差に対して強く依存することがなくなる。

### デメリット
- **計算コストが高くなる**：複数のモデルを学習させるため、処理時間やリソースが増加する。
- **解釈が難しい**：複数のモデルの予測結果を統合するため、モデルの解釈が難しくなることがある。

---

### まとめ
- **アンサンブル学習** = **複数のモデルを組み合わせて予測精度を向上させる手法**
- 代表的な手法は**バギング、ブースティング、スタッキング**
- 精度向上や過学習の抑制に有効だが、計算リソースが多く必要

---
Teensy4.0でUSBホストとSDカードを使う





## 使用部品 ##

### Teensy4.0 ###
Teensy4.1よりコンパクトな分、USBホストのピンが出てなくて、SDカードスロットがなくて、FLASHメモリのサイズが1/4の2MBに。価格はTeensy4.1より少し安い。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107818/024a3067-1130-4b8e-8123-dc3fb522b1ab.png)

### SDカード ###
ILI9341のLCD。タッチ付きと無しがあるみたい。画面の大きさは1.8インチから3.2インチくらいまである様子。SDカードのスロットがついてる（microSDではなくてデカい）。とりあえず初めて使うので秋月で購入した。
https://akizukidenshi.com/catalog/g/g116265/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107818/88bca6b0-bb5d-4f7c-8f0a-d686637c65eb.png)

### USBホストケーブル ###
赤白緑黒黒5ピンのUSBホスト用のケーブルを使う。
![IMG_9134.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107818/a5532e1e-b946-4b32-878c-cc53ed88a98f.jpeg)


コネクタ部分を切って、ケーブルをむき出しにしてはんだ付け。白をD-、緑をD+、赤をVUSB（5Vと3.3Vの間のGNDの隣に穴が空いてる）、黒x2をGNDに。
![IMG_9132.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107818/7275e159-0b5d-42f6-b6b7-c33a14342d3e.jpeg)

## 接続 ##
### LCD + SD Card ###
| ILI9341 LCD | Teensy4.0 |
|:------------|:-------|
| VCC | 3.3V |
| GND | GND |
| CS | 10 |
| RESET | 6 |
| DC | 9 |
| SDI(MOSI) | 11 |
| SCK | 13 |
| LED | 50Ω -> 3.3V |
| SDO (MIDO) | 12 |
| SD_CS | 1 |
| SD_MOSI | 11 |
| SD_MISO | 12 |
| SD_SCK | 13 |

### USB Host Cable ###
| USB Hostケーブル | Teensy4.0 |
|:------------|:------------|
| 赤         | VUSB        |
| 白     | D-      |
| 緑     | D+        |
| 黒     | GND          |
| 黒    | GND     |


## スケッチ ##
LCDに関してはILI9341_t3、ILI9341_t3n、ILI9341_t4といったライブラリがArduino IDEのライブラリマネージャにも登録されていて、サンプルがそれぞれ動いた。ピンのアサインは適宜変更のこと。


SD CardはTeensy4.x用のライブラリ？がそのまま使えた。SD.begin()でSD-CSのピン番号を指定するだけ。
```cpp
#define SD_CS    1

// SDカード初期化
if (!SD.begin(SD_CS)) {
    Serial.println("Failed to init SD Card");
} 
else {
    Serial.println("Succeeded to init SD Card");
}

// ファイルを書き込み(追記)モードで開く（なければ作成）
File file = SD.open("/test.txt", FILE_WRITE);

if (file) {
    file.println("Hellow SD Card");
    file.println("Written from Teensy 4.0");
    file.close();
    Serial.println("Wrote to SD card");
}
else {
    Serial.println("Failed to open file");
}
```

USB HostはTeensy4.1と同様。

## 実際に動かした感じ ##
タッチも繋げば使える。
![IMG_9130.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107818/7ed63900-da8b-469a-aa55-6bc4aebb05c6.jpeg)

ESP32(M5Stamp Pico)とPCM5102Aボードを組み合わせた例。
![IMG_9131.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107818/08314c2d-c9a9-4a6f-878f-a38dc8a9eb0e.jpeg)
同じ設置先でのデータレプリケーション

データの複製方法の一つとして、レプリケーションというものがある。同じシステムに複数の環境を置いて、一方は本番用、もう一方は待機用という区分で使い、通常のバックアップと比較して、本番で障害が発生した際の切り替えが早い。システム復旧までの時間を最小限に食い止めることができる。

レプリケーションは災害対策という側面もあるため、基本的には距離の離れた2つのサイトにそれぞれの環境を置くことが推奨されているが、稀に同じサイトの同じネットワーク内にレプリケーションを行なう手法も存在する。

今回の投稿では、その方法について見ていきたい。

## 複数台のストレージからレプリカとしての利用

同じネットワーク内に2台のストレージを置いたレプリケーションの例を見ていく。

・プライマリ(マスタ)：メインのストレージとして活用。データの読み書きが行なわれる。
・セカンダリ(レプリカ)：マスタのデータを複数するためのストレージ。複製方法としてリアルタイム書き込みである同期型、一定の時間に反映処理を行なう非同期型が存在する。

同じネットワーク内にレプリカを置くメリットとして、まず障害発生時の迅速な切り替えが挙げられる。これは長距離型の2サイトレプリケーションにも通ずるが2サイトよりも物理的な距離が近い分、復旧がよりスピーディーな形で期待できる。同サイトという性質上、両機を設置しているデータセンター全体に災害による被害が発生した際はデータ復旧ができなくなるリスクも併せ持つ。

また万が一、マスタ側のストレージ筐体そのものが故障してデータの取り出しができなくなったとしてもレプリカ側での復元でどうにかなる。
そして同サイトに置く最大のメリットは負荷分散だろう。レプリカを読み取り専用として使うリードレプリカとして活用してマスタとネットワーク、それぞれの負荷を軽減させる効果がある。

マスタ側は新規データの格納、またはデータ更新処理に専念して、レプリカ側では大量のアクセスを捌く。ものすごい数のユーザが利用するようなアプリケーションではネットワーク内にリードレプリカを設置するメリットは大きい。

## 同じ筐体内でのレプリケーション

次の例では同じネットワーク内の1台のストレージ内で行なわれるレプリケーションについても触れる。
デメリット面から先に言えば、設置先のデータセンターへの災害はもちろん、ストレージ機器そのものに障害が起きてしまったらデータ復旧ができないリスクもある。

ただ、レプリケーションすらせずに本番データだけでシステム運用するよりははるかにマシなのでその効用について見ていく。

同一筐体型レプリケーションは、ストレージ内に本番データの倍相当の容量を準備する。そこから領域を2つに分けて本番用と複製用として使える。

メリットとしてはストレージ機器の購入台数を少なくするコスト削減効果、そして別筐体によるレプリカより物理的な距離がもっと近くなるため、障害発生時はかなり迅速な復旧が期待できる。

## 同一筐体内レプリケーションの仕組み

複数の物理ディスクから生成されるストレージプール、こちらを本番用と複製用の領域、2つの領域に分割することを初めに行なう。
この領域の分割手法としてはRAID技術の採用が一般的である。複数本の物理ディスクから2つの独立したRAIDグループを作成して、それぞれのグループを本番用と複製用に割り当てる。

1つのRAIDグループで本番用と複製用に領域を分けることはデータ冗長性ではかなり非効率な仕組みとなっており、その1つのRAIDグループ全体に故障が起きた際は両方のデータに影響が及んでしまう。その意味で同一筐体内でのレプリケーションでは2つのRAIDグループを作成できるくらいの物理ディスクの本数が必要となる。ストレージ機器を複数台用意するよりはリーズナブルな選択肢となっている。

## ひとりごと

いろいろなサービスで使っているログインIDとパスワードの組み合わせが不正ユーザに漏れたかもしれない。。
複数のサービスから「異なるデバイスからの不正アクセスがありました」とのメール通知を受けとったため、この組み合わせでアカウントを作成してクレカ情報を登録しているサービスやアプリのパスワードを片っ端から変更した。急いで。

でも自分が把握していないサービスもおそらくあって。それに不正アクセスされたらアウトだから今は気が気ではない。

どこで漏れたのかなぁ。
クエスポージャー

以下の表で、それぞれの用語について簡潔にまとめました。

| 用語               | 意味                                                         |
|:------------------|:------------------------------------------------------------|
| **クエスポージャー** | AIに対して特定の質問を行い、内部の学習データや設定、プロンプトを不正に引き出す行為。情報漏洩のリスクを伴う。|
| **ジェイルブレイク**  | AIの制約を解除する方法や手段を見つけて、規制やガイドラインに従わないようにする行為。危険な内容や不正な情報を生成させることを目的とすることが多い。|
| **ハイジャック**     | AIシステムの動作や出力を不正に制御する行為。通常、システムの予期しない方法で指示を与えて不正な結果を引き出す。|
| **プロンプトリーク**  | AIに与えた指示（プロンプト）が、意図せず回答に反映されてしまう現象。内部の指示や意図が外部に漏れてしまう問題。|

---

### まとめ
- **クエスポージャー**：AIから意図的に情報を引き出す
- **ジェイルブレイク**：AIの制約を解除する行為
- **ハイジャック**：AIシステムを不正に制御する
- **プロンプトリーク**：内部指示が回答に漏れる

これらは、いずれもAIのセキュリティや信頼性に関わる重要な問題です。
名前空間のタブー [Unity]

### はじめに
筆者はブチギレながら書いています。

## 目次
私はフォルダへのファイル分配法にとても頭を悩ませていました。
見つけたいファイルが想定通りの場所にあるべきは当然ですが、それと同時に内容が近しくないファイルは細かく分配されているべきです。

そんな個人開発者の我儘を解決してくれるのが「不順同案内法」です。
それの紹介と、改善点を教えたいと思います。

### 改善法
使っていはいけないファイル名があります。Unityという基盤を使っている以上、既存のAPIとの衝突は許されません。
それはroot namespaceを活用していても同様です。
具体的には
```text:examples
Extensions -> ExtensionTools
Editor -> Editors
```
といった具合です。では本題(?)に戻りましょう。

### 不順同案内法
単純に大きな括りでくくるのをやめましょうといった話です。
ScriptableObjectsが全て同じ親ディレクトリScriptableObjectsに属する必要はありません。
いろんなディレクトリの下にScriptableObjectsがあってもいいのです。
ただGamePlayとかCoreだとか、シーンを基準に案内するといった形でいい感じに分配できるといった具合です。
これはベクトルの四則演算を思い浮かべてくれれば、直感的だと思います。
勿論開発中の効率も良くなります。参照するべき名前空間が狭まるので、おそらくですが、効率の良いコンパイルを可能とします。

## まとめ
筆者がブチギレていたのは、元々クラス内に書き込んでいた名前空間を諸々書き直す必要があったからです。
そして冗長な命名法をとても嫌っているので、こういった下らない理由で長くしないといけないのはとても心労的負荷になります。
ただ、ツールに縛られし個人開発者としては、まあ多少妥協すべきことなのかとも思います。社会人としての洗礼を受けている気分です。

ここまでお疲れ様でした！
Stable diffusionの出力時、ノイズのようなものがかかってしまう

# 概要

Stable diffusionで画像を出力していて、　90%までの出力している最中では問題なくても、以下のように100%で出力でノイズがかかったようなものになってしまう。

![スクリーンショット 2025-04-28 8.18.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/c7b60776-757a-4a5e-b443-d36e9be05c8c.png)

# 結論

選択しているモデルと、VAEが対応していない場合に起こる。今回の場合は、
- モデル：SDXLに対応した別のモデル
    - 4nimaPencilXL
- VAE：stable diffusionに対応した別のモデル
    - counterfeit-V2.5

であったため、今回のような出力になってしまった。ちなみにVAE、モデルは、画像出力の部分のプロンプトの下に書いてある。

# 解決策
設定のところで、vaeが設定されているところを外せるようにする。
まず、
```
Setting / Stable diffusion
```
の部分から、SD VAEが何になっているかを確認する。

![スクリーンショット 2025-04-28 8.20.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/edf3607c-0bf5-42ac-9c4a-fd5919ddc21d.png)

今回だと、チェックポイントのモデルとVAEが異なる内容になってしまっていた。

そこで、VARの部分を`None` にして、`Apply settings`を押す。

![スクリーンショット 2025-04-28 8.22.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/ef4671f6-d6dd-4954-bd63-a075728f4d3a.png)

そこから、text_to_imgの部分を見てみると、VAEの部分に選択されていそうなので、
これを❌を押して解除する。

![スクリーンショット 2025-04-28 8.26.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/0c3c4734-19d2-4bbc-aa90-32c5456606a5.png)

そして、この状態で再度画像を生成させると、以下のように無事成功する。

![スクリーンショット 2025-04-28 8.30.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/bbade318-51bc-459a-9eb9-d68d09593ff1.png)

ちなみに、選択の部分でAutoにする、かつ`sellected vae overrides`のところにチェックを入れて`Apply`を入れた状態で画像を生成しようとしてみたが、特に変化点は見当たらなかった。（override settingsの部分に変化があるかと思ったが、特に何も起こらない。）

![スクリーンショット 2025-04-28 8.36.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/30ccbfd0-bbd1-4c5f-9ddb-9808da27060e.png)
文献レビューが激変する！DimIndによる次世代LLM支援型リサーチ整理法とは？

# 文献レビューが激変する！DimIndによる次世代LLM支援型リサーチ整理法とは？
今回は、最新の研究成果である「Facets, Taxonomies, and Syntheses: Navigating Structured Representations in LLM-Assisted Literature Review」という論文をご紹介します。この研究は、文献爆発時代におけるレビュー負荷を抜本的に軽減し、ユーザー主導の深い文献理解と構造化統合を可能にする、次世代型LLM支援システム「DimInd」を提案しています。

---

### 論文情報
- **タイトル**: Facets, Taxonomies, and Syntheses: Navigating Structured Representations in LLM-Assisted Literature Review
- **リンク**: [arXiv:2504.18496](https://arxiv.org/abs/2504.18496)
- **発表日**: 2025年4月
- **著者**: Raymond Fok, Joseph Chee Chang, Marissa Radensky, Pao Siangliulue, Jonathan Bragg, Amy X. Zhang, Daniel S. Weld
- **DOI**: なし（arXivプレプリント）

---

## 背景と目的

現代科学では、年間数百万件の論文が発表されており（Jinha, 2010）、  
システマティックレビューの更新サイクルすら追いつかない時代に突入しています（Shojania et al., 2007）。  
この環境下、**単なる検索や表層可視化ではなく、「深い意味構造」を抽出・組織化する新たな支援技術**が求められています。

従来のLLM活用型支援（例：Elicit, SciDaSynth, CHIME）はそれぞれ、
- 単純な属性抽出（表ベース）
- 高度なクラスタリング（階層分類）  
に強みを持ちますが、「多層情報変換＋ユーザー主導制御」を統合的に支援するものは存在しませんでした。

このギャップを埋めるべく、本研究は  
**段階的構造化（papers → facets → taxonomies → narrative）**を核とするDimIndを提案しています。

---

## 研究の焦点と設計思想

本研究は次の理論的枠組みに基づいています。

- **Sensemaking Model**（Pirolli & Card, 2005）  
　→ 情報収集・構造化・統合の段階的プロセスモデル。

- **Information Foraging Theory**（Pirolli & Card, 1999）  
　→ ナビゲーションコスト最小化、情報利得最大化を指向する探索理論。

これらを踏まえ、DimIndは以下を設計ゴールとしました。

| ゴール | 解説 |
|:--|:--|
| DG1 | 大量論文を、探索可能なファセット別情報リポジトリへ変換 |
| DG2 | 低レベル詳細と高レベルテーマの自由往還を支援 |
| DG3 | LLM出力の検証容易性とユーザー修正の柔軟性を確保 |

---

## DimIndシステム内部構造の詳細

### ファセット発見（Facet Discovery）
- **方法**: ランダムに小サンプルを生成し、それぞれから独立にファセット候補群を生成。最後に統合・整理。
- **理由**:  
　- 大規模文献コレクションでは、直列処理では過学習リスクが高まるため。  
　- 局所的な多様性を確保しつつ、全体テーマの一貫性も保つため。

### 値抽出（Value Extraction）
- 論文本文からリトリーバル強化型LLM（RA-LLM）で情報抽出。  
- 次に、内容密度を20ワード以下に自動圧縮（ミニサマリー生成）。

### タクソノミー生成（Taxonomy Creation）
- 抽出スニペットをLLMクラスタリングし、最大5階層の階層分類を自動構築。
- 1階層目で最大20クラスまでに制約し、可読性と探索効率を担保。

### ナラティブ統合（Synthesis）
- タクソノミーの選択枝に沿ったナラティブまとめを生成。
- すべての主張に出典リンク（[[PaperID]]形式）を明示付与。

---

## ユーザスタディ設計と深堀り分析

### 実験設定

| 項目 | 内容 |
|:--|:--|
| 被験者 | 23名（HCI PhD中心、経験平均3-5年） |
| タスク | 50論文セット、30分で特定セクションのアウトライン構築 |
| 条件 | DimInd vs ChatGPT（Web検索禁止） |
| 評価指標 | 知的負荷、分類効率、探索効率、出力検証性、満足度、自己信頼度 |

### 主な数値結果

| 指標 | DimInd | Baseline | 統計検定結果 |
|:--|:--|:--|:--|
| 精神的負荷低減 | 中央値6（IQR5–7） | 中央値5（IQR2–5） | **p<.01** |
| 論文分類支援 | 中央値6（IQR5–7） | 中央値5（IQR2–5） | **p<.01** |
| 出力検証容易性 | 中央値6（IQR5.5–7） | 中央値5（IQR3–6） | **p<.05** |

---

## 成功例と失敗例

### 成功例
> 「複数ファセットを自由に増やせるので、自然に構造が深まっていった」（P2）

> 「分類ツリーから興味ある領域にすぐ飛べた。これがなければ収拾がつかなかった」（P5）

### 失敗例
> 「抽出スニペットがやや冗長で、分類しきれないファセットもあった」（P13）

> 「ナラティブ生成結果が表層的で、自分で組み直した」（P19）

---

## 賛否両論

| 賛成 | 反対 |
|:--|:--|
| スニペットから即座に原文へ遷移でき、検証性が極めて高い | 抽出された内容の"粒度"にブレがある場合があった |
| タクソノミーが探索の起点として非常に機能した | すべてのファセットがきれいに分類できるわけではない |

---

## 今後の展望（具体案）

- **マルチファセット相関探索モジュール**  
　→ ピボットテーブルのように2軸ファセットを動的に交差解析。
  
- **アクティブ・クエリ駆動型タクソノミー再構成**  
　→ ユーザーのクリック・選択に応じて、タクソノミー自体をリアルタイム更新。

- **学際領域特化モード**  
　→ 医療レビュー用、政策文書レビュー用など、専門用語セット最適化。

- **自己説明型ナラティブ生成（XAI対応）**  
　→ 各まとめ文に「なぜこのまとめになったか」を自己説明できる新世代プロンプト設計。

---

この記事が、次世代の文献レビュー支援の在り方を考える一助となることを願っています。  
ご意見・ご質問があれば、ぜひコメントでお寄せください！
グリッドサーチ


---

**グリッドサーチ（Grid Search）**とは、  
**機械学習モデルのパラメータ（ハイパーパラメータ）を、いろいろな組み合わせで総当たり的に試して、一番良い組み合わせを探す方法**のことです。

簡単に言うと：

> **「パラメータを網の目（グリッド）のように並べて、全部試してみる！」**  
というイメージです。

---

### もう少し具体的に

例えば、あなたが機械学習モデルを作るとき、  
次のようなハイパーパラメータを調整したいとします：

| パラメータ | 試す値 |
|:---|:---|
| 学習率（learning rate） | 0.01, 0.1, 1 |
| 木の深さ（max depth） | 3, 5, 7 |

これをグリッドサーチすると：

- 学習率0.01 × 深さ3
- 学習率0.01 × 深さ5
- 学習率0.01 × 深さ7
- 学習率0.1 × 深さ3
- ...  
など**すべての組み合わせ**を試して、  
一番精度がよかったものを選びます。

これが「グリッドサーチ」です。

---

### 特徴

| 項目 | 内容 |
|:---|:---|
| メリット | 確実に「設定した範囲内で最良の組み合わせ」を探せる |
| デメリット | 計算コストが高い（組み合わせが多いと非常に時間がかかる） |

---

### まとめ
- グリッドサーチ＝**総当たりでパラメータを探索する方法**
- 正確だけど**計算量が多くて大変**なこともある
- たくさん試せるとき（リソースがあるとき）にはとても有効！

---
競プロ日記#25/04/27

# アルゴ式-[九九の表 (1)](https://algo-method.com/tasks/406)
- iを固定して条件を満たすjの範囲をiごとに探索していく。
```
// 表の列を全探索
long long count = 0;
for (int i=0; i<N; i++) {
count += min(N, K/(i+1));
}
```

# アルゴ式-[九九の表 (2)](https://algo-method.com/tasks/407)
- ↑の応用

# AtCoder

## ABC158_[B-Count Balls](https://atcoder.jp/contests/abc158/tasks/abc158_b)
- long long型で取らないとREになる可能性があり
【便利Chrome拡張】選択したテキストとそのページのリンクを任意の形式でクリップボードに保存する拡張機能

表題の通り、今回は選択したテキストとそのページのリンクを任意の形式でクリップボードに保存する拡張機能のコードを紹介します。

自分で使っていてとても便利なので、ぜひ使ってみてください。

### 公開済みの拡張機能

https://chromewebstore.google.com/detail/link-clipper/ihjeamhemokphinobmaennmmjbiggboe

こちらは私が公開している拡張機能です。
ただし、形式が`[選択テキスト](ページURL)`に限定されてしまっています。
これ以外の形式でコピーしてみたい方は、以下に紹介するコードを元にローカルで拡張機能を読み込んで使ってみてください！


### コード本文

```javascript
// コンテキストメニューを作成
chrome.runtime.onInstalled.addListener(() => {
  chrome.contextMenus.create({
    id: "copyAsLinkText",
    title: "リンクテキストとしてコピー",
    contexts: ["selection"], // テキスト選択時のみ表示
  });
});

// 右クリックメニューのクリックイベント
chrome.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId === "copyAsLinkText" && info.selectionText) {
    // 選択されたテキストとURLをコンテンツスクリプトに渡す
    chrome.scripting.executeScript({
      target: { tabId: tab.id },
      function: copyToClipboard,
      args: [info.selectionText, info.pageUrl],
    });
  }
});

// コンテンツスクリプトとして実行する関数
function copyToClipboard(selectedText, pageUrl) {
  const formattedText = `[${selectedText}](${pageUrl})`;

  // クリップボードに書き込む
  navigator.clipboard.writeText(formattedText).catch((err) => {
    console.error(
      "link-clipper: 何らかのエラーによりクリップボードにコピーできませんでした",
      err
    );
  });
}
```
```javascript
const formattedText = `[${selectedText}](${pageUrl})`;
```
この部分でコピーした時の形式を指定しているので、コピーしたい形式に変更してください。

ローカルに作成した拡張機能の読み込み方は以下を参考にしてみてください。
https://note.com/cute_echium873/n/n997dcf40b3a1
【Git】簡単にスタックブランチを管理する方法

git-spiceを使用すると簡単にブランチをスタックできます。

https://abhinav.github.io/git-spice/

## インストール

Homebrewを使用する場合には以下のコマンドでインストールできます。

```terminal
brew install git-spice
```

https://abhinav.github.io/git-spice/start/install/

## 使い方

以下の順で実行するとブランチを作成し、変更をコミットします。

```terminal
# ファイルに変更を加える
$ git add file.txt
$ gs branch create feat1
```

上記を繰り返してブランチをスタックします。

```terminal
# ファイルに変更を加える
$ git add file2.txt
$ gs branch create feat2

```

https://abhinav.github.io/git-spice/guide/branch/

リモートにブランチを反映するには以下のコマンドで認証します。

```terminal
gs auth login
```

https://abhinav.github.io/git-spice/setup/auth/

以下のコマンドでブランチをリモートに反映し、プルリクエストを開きます。

```terminal
gs branch submit
```

https://abhinav.github.io/git-spice/guide/cr/

プルリクエストに以下のようなコメントがつきます。

![stack-comment.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/54f0abb2-6b2c-4bf5-afea-45ad76009f7f.png)

リモートの変更を反映するには以下のコマンドを実行します。

```terminal
gs repo sync
```
Ubuntu24.04にcontainerdとnerdctlをインストールする

# この記事について
WSL2上のUbuntu24.04にnerdctlをインストールする手順。
すでに多数の記事が出回っているが、微妙に動いたり動かなかったりしたので、最短でインストールできた方法をメモする。試行錯誤の過程は全て端折って結論だけ書いている。
OSを初期化した状態から試しているので、おそらく過不足ない、はず。

# 環境
- WSL2 + Ubuntu 24.04 LTS

# 手順

## 必要なパッケージのインストール

```bash
$ sudo apt update
$ sudo apt install containerd runc rootlesskit iptables
```

:::note info
CNIプラグインを動作させるためにiptablesコマンドが必要。
Ubuntu 24.04にはデフォルトでnftablesがインストールされている。
iptablesコマンドは、実際にはiptables-nftを呼んでいるので、両方インストールしても問題ないらしい。
:::


## nerdctlのインストール

- nerdctlのサイトに書かれている手順をそのまま実行する

```bash
$ curl -OL https://github.com/containerd/nerdctl/releases/download/v2.1.1/nerdctl-2.1.1-linux-amd64.tar.gz
$ sudo tar Cxzvvf /usr/local/bin nerdctl-2.1.1-linux-amd64.tar.gz
$ containerd-rootless-setuptool.sh install
```

https://github.com/containerd/nerdctl


## CNIプラグインのインストール

- `/opt/cni/bin/` に置いとくと読んでくれるらしい…

```bash
$ curl -OL https://github.com/containernetworking/plugins/releases/download/v1.7.1/cni-plugins-linux-amd64-v1.7.1.tgz
$ sudo mkdir -p /opt/cni/bin/
$ sudo tar Cxzvvf /opt/cni/bin cni-plugins-linux-amd64-v1.7.1.tgz
```

https://github.com/containernetworking/plugins


## buildkitのインストール

- `nerdctl build` を実行するために必要

```bash
$ curl -OL https://github.com/moby/buildkit/releases/download/v0.21.1/buildkit-v0.21.1.linux-amd64.tar.gz
$ sudo tar Cxzvvf /usr/local/ buildkit-v0.21.1.linux-amd64.tar.gz
$ CONTAINERD_NAMESPACE=default containerd-rootless-setuptool.sh install-buildkit-containerd
```

https://github.com/moby/buildkit

# 動作確認

- nerdctl のバージョン確認
```bash
$ nerdctl -v
nerdctl version 2.1.1
```

- nerdctl buildの確認
``` bash
$ mkdir tmp && cat <<EOF > tmp/Dockerfile
FROM alpine:3.21.3
RUN echo hello
EOF
$ cd tmp
$ nerdctl build -t mytest .
```

- コンテナの実行

```bash
$ nerdctl run mytest echo "hello"
hello
```
EndeavourOS(KDE) 日本語入力設定で嵌らないために

# EndeavourOS(KDE) 日本語入力設定で嵌まった話
皆さんEndeavourOSって使っていますか？
筆者は最近インストールして弄っている最中なのですが、初手の日本語設定で足を取られたので、一応記事にしておきます。

嵌った問題は別に大した事では無く、ユーザーフレンドリー過ぎての見落としです。要するに素のArchLinuxとの差があり過ぎて、OSが何でもやってくれていると勝手に思い込んだんですね。

具体的には、locale設定とキーボード設定を日本語(ja),Mozcにさえすれば、標準で入っている bash や firefox で日本語入力が出来てしまうので、この時点で既に設定されていると勘違いした事が原因でした。

その後、VSCodeをインストールした際、日本語入力が出来ず、さらにWEBにも「VSCodeのOSS版では日本語入力出来なかったけどwin正規版をダウンロードしたら直った」という情報があったので筆者自身もVSCodeのせいにしてしまい、長時間嵌りました(泣)。

EndeavourOSが快適すぎて疑いもしねーよ、ほんと。

##
（2025.5.19追記） 書き忘れていましたが下記環境での話です。

EndeavourOS： デスクトップ環境 KDE、 ディスプレイサーバー Wayland、 入力 Fcitx5 + mozc

EndeavourOS公式：https://endeavouros.com
KDE（ArchWiki）https://wiki.archlinux.jp/index.php/KDE
Wayland（ArchWiki）https://wiki.archlinux.jp/index.php/Wayland
Fcitx5（ArchWiki）https://wiki.archlinux.jp/index.php/Fcitx5
Mozc（ArchWiki）https://wiki.archlinux.jp/index.php/Mozc

##

以下の手順で上手く行ったので、嵌った方、どうぞ。


## 上手くいった手順

locale.gen を vim で開きます。(もちろんnanoとかでも問題無いです）
```ruby:bash
$ sudo vim /etc/locale.gen
```

下記のコメントアウトを外します。
```diff_ruby:/etc/locale.gen
- #ja_JP.UTF-8 UTF-8
+ ja_JP.UTF-8 UTF-8
```

以下を入力。
```ruby:bash
$ sudo locale-gen
```

environment を開きます。
```ruby:bash
$ sudo vim /etc/environment
```

以下を入力し保存します。
```ruby:/etc/environment
XMODIFIERS=@im=fcitx
```

次にKDEシステム設定を開き、仮想キーボードを設定します。
下図のFctix5 Wayland ランチャーを選択
![仮想キーボード設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3957376/e955975e-f81c-4410-9c0d-0fff95fef4f9.png)

キーボードを適合するものに変更（筆者はjapanese 106-key　日本語レイアウトです。Shift + 2 で " を入力するのが手に馴染み過ぎてJIS以外無理です。絶対に@はPの右です。）
![キーボード設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3957376/991986f2-7da5-4127-89be-9a042682a157.png)

続いて入力メソッドですが、下図の様にします。

![fcitx設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3957376/400e53d9-e105-4666-a0c2-a1ebc05e7ad0.png)

<code>Mozc</code>の初期モードを"全角かな"にします。
![mozc設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3957376/fe3a60cd-8679-4c6e-b644-d13f64560c91.png)

:::note info
投稿当初は<code>Mozc</code> のみにしたと記述したのですが、それだとキーボード設定を再度弄らなければならない場面が出て来るので、入力メソッドオフ（先に追加）<code>キーボード-日本語</code>入力メソッドオン（後から追加）<code>Mozc</code>（初期モードを"全角かな")に設定の方が良かったなと思い記事を訂正させていただきました。
:::

では VSCode を開いて かな切り替えをしてみます。
```ruby:bash
$ code
```

![かな切替.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3957376/6d87d12b-049b-4254-8f22-03a6ae85b41a.png)
出来ました。良かった。

###
上記でダメな時は、インストール時に何かしら上手くいっていない箇所があるかも知れません。

例えば、次の様なkeymap設定とかです。（参考のため貼っておきます）

```ruby:bash
sudo localectl set-keymap jp106
```

##

## 余談
筆者は以前、素のArchLinuxをインストールして快適なデスクトップ環境にするべく環境構築を進めていたのですが、何か１つ追加する⇛問題が起きる⇛解決するために情報を漁る⇛解決する　を繰り返して他の事が出来なくなるという本末転倒な事が起きてしまい、断念しました。。

が、折角ArchLinuxについて学んだのに勿体ないので人気のあるEndeavourOS（KDE）に乗り換えました。（最初から...）物凄く快適です。

素のArchでは xrandr,autorandで行っていたディスプレイ設定が、、bashの文字の詰まりが、、とまぁ、それを体験したことで改めて作り込まれたディストリビューションの有難みを感じました。

余談が過ぎますが、此処だけの話 Ubuntu と迷っている方、筆者は両方使った事がありますが、圧倒的に EndeavourOS を推します。
「OSI参照モデルってなに？ネットワーク通信のルールをざっくり解説！」

# OSI参照モデルとは？


# まとめ：OSI参照モデルとは？

- OSI参照モデルは、ネットワーク通信の役割を7つの階層に分けて整理した共通ルール。  
- 異なる機器やメーカー間でも通信できるようにするために作られた。  
- 送信側はデータに情報を付け足しながら下層へ、受信側は付け足された情報を順に取り除きながら上層へ渡す。  
- 7層はそれぞれ役割が違い、物理層からアプリケーション層まで階層的に機能を分担している。  
- ネットワークの基礎理解や資格試験の必須知識。  

**一言で言うと：**  
> 「ネットワークの通信をわかりやすく7段階に分けてルール化したモデル」



## ざっくり説明

OSI参照モデル（読み：オーエスアイサンショウモデル、英：OSI model）とは、

> 「通信機能って、どんな仕組みにするの？」を整理した共通ルール（のひとつ）です。

もう少し難しく言うと、国際標準化機構（ISO）が作った、データ通信の機能を階層に分けて整理したモデル（イメージ図）です。

---

## なぜ必要なの？

通信をするには、送り手と受け手が同じルールを共有しなければなりません。

たとえば、片方が日本語で話しても、もう片方が英語で返していたら会話が成立しませんよね？  
ネットワークも同じです。

---

### 昔の問題点

昔はメーカーごとに勝手なルールを作って通信していました。  
- あるメーカーの機器は日本語  
- 別のメーカーの機器は英語

こんな感じで、異なるメーカー同士の通信はできませんでした。

---

### 解決策

そこでISOという団体が、「共通ルールを作ろう！」と提案しました。

この共通ルールを満たしていれば、どのメーカーの機器でも通信が可能になります。  
まるでみんな同じ言語を話すように。

---

## OSI参照モデルの構成

OSI参照モデルはネットワークの役割を**7つの階層**に分けて考えます。

---

### 7つの階層（上から順）

| 層番号 | 層名             | 役割のざっくり説明                     |
|--------|------------------|--------------------------------------|
| 第7層  | アプリケーション層 | ユーザーが使うソフトとの窓口          |
| 第6層  | プレゼンテーション層 | データの形式変換や暗号化・圧縮         |
| 第5層  | セッション層       | 通信の「会話」を管理                   |
| 第4層  | トランスポート層    | 信頼できる通信のためにデータを管理      |
| 第3層  | ネットワーク層      | データの送り先を決める（ルーティング） |
| 第2層  | データリンク層      | 物理的な通信路の確保とエラー検出       |
| 第1層  | 物理層            | ケーブルなどの物理的な通信手段           |

---

## 各層の役割をもっと詳しく！

### 第1層：物理層

- ケーブルや電気信号など、データを物理的に送る部分を担当  
- 例：LANケーブル、光ファイバー、無線の電波など

### 第2層：データリンク層

- 直近の通信相手とデータをやりとりする  
- MACアドレスで識別し、エラーを検出・訂正  
- 例：スイッチングハブの動きなど

### 第3層：ネットワーク層

- ネットワーク全体でデータの行き先を決定（ルーティング）  
- IPアドレスを使う  
- 例：ルーターが担当

### 第4層：トランスポート層

- データを正確に届けるための仕組み  
- データの順番や欠損を管理（TCPなど）  
- 再送制御もここで行う

### 第5層：セッション層

- 通信の開始・維持・終了を管理  
- 電話の「もしもし」みたいな会話の管理者

### 第6層：プレゼンテーション層

- データの表現形式を変換  
- 暗号化・圧縮・文字コード変換など  
- 「英語を日本語に通訳する」イメージ

### 第7層：アプリケーション層

- ユーザーの使うソフトとネットワークの接点  
- Webブラウザやメールなどのアプリが動く場所

---

## 通信の流れイメージ

### 送り手側

1. 元のデータが第7層（アプリケーション層）に届く  
2. 第7層でデータに「宛先」などの情報を追加  
3. そのまま第6層、第5層・・・第1層へ順に渡しながら情報をどんどん付け足す  
4. 第1層で物理的にデータを送信

---

### 受け手側

1. 第1層でデータを受け取る  
2. 第1層が自分に関係ない情報を取り除き、第2層に渡す  
3. 第2層でも同様にチェックし情報を外して第3層に渡す  
4. これを第7層まで繰り返す  
5. 最後に元のデータだけが残ってユーザーに届く

---

# まとめ：OSI参照モデルとは？

- OSI参照モデルは、ネットワーク通信の役割を7つの階層に分けて整理した共通ルール。  
- 異なる機器やメーカー間でも通信できるようにするために作られた。  
- 送信側はデータに情報を付け足しながら下層へ、受信側は付け足された情報を順に取り除きながら上層へ渡す。  
- 7層はそれぞれ役割が違い、物理層からアプリケーション層まで階層的に機能を分担している。  
- ネットワークの基礎理解や資格試験の必須知識。  

**一言で言うと：**  
> 「ネットワークの通信をわかりやすく7段階に分けてルール化したモデル」
EBAZ4205 FPGA Xilinx編 第2版　演習2-4-2

# 2-3の応用例　HDMIでWRGB4色のグラデーション

- ほとんど下記のプロジェクトを流用して作られています。

https://qiita.com/ShigeoYakuno/items/05ebae0932d7b5c7917d

## 2－3のパターン表示回路との差分

- 前回は、8段階のストライプパターン（80ピクセル区切り＋上下反転の単純なカウント値）を表示
- 今回の回路は水平方向の輝度を滑らかに変化させ、垂直方向で複数の色相（グレースケール、赤、緑、青）のグラデーションを表示する設計になっている
- 水平ブロックの幅が 80 ピクセル（前回）から 64 ピクセル（今回）に変更されている


## gradation_hdmi（最上位モジュール）
### 機能・構成
- HDMI出力用の最上位モジュールで、HDMIディスプレイにグラデーションパターンを表示する
- 入出力は以前の pattern_hdmi と同様
- 内部で「gradation」モジュールをインスタンス化し、その出力をHDMI変換IP（rgb2dvi）へ接続している
- 注意点として、リセット信号は反転（!RST）で渡している。EBAZ4205回路依存のところ。

# gradation（グラデーション生成モジュール）
### 基本構造 
- VGAタイミング（640×480）のパラメータは外部ヘッダファイル vga_param.vh から読み込む
- 内部では syncgen モジュールにより水平・垂直のカウンタおよびピクセルクロック PCK を生成

# グラデーション生成ロジック
### 水平部分：
- ローカルパラメータ HSIZE が 10'd64 と定義され、水平有効領域内のピクセルカウント（hcounter = HCNT - HBLANK + 1）の上位4ビット（hcounter[5:2]）を用いて「pattern」として算出。
- この4ビットの値がグラデーションの変化量を決定

### 垂直部分：
- 垂直方向は VSIZE（10'd120）ごとに区切られ、(VCNT - VBLANK) / VSIZE で計算した vcounter によって、表示する色の種類を切り替え
- case 文による色選択は以下の通り

1. vcounter == 0：グレースケール（R = pattern, G = pattern, B = pattern）
2. vcounter == 1：赤系のみ（R = pattern, G = 0, B = 0）
3. vcounter == 2：緑系のみ（R = 0, G = pattern, B = 0）
4. vcounter == 3：青系のみ（R = 0, G = 0, B = pattern）
5. その他の場合は黒（全チャネル0）

### 出力

- それぞれの4ビットの値を8ビットに拡大するため、同じ4ビットを上下に配置して（{R, R, G, G, B, B}）RGB出力としている

- disp_enable 信号は、水平・垂直の表示可能領域内であるかを判定しており、これをタイミング合わせて VGA_DE として出力

### その他は2－3と同じ


# 実行例

![IMG_1857.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3657924/dd39f2ea-0576-4c26-a7f4-ce475f18c5a2.jpeg)


### 今日はここまで
AWS EC2でFlaskを動かす

おはようございます。座禅いぬです。

いつも表題の仕組みを使っているのですが、**見事に構築の仕方を忘れました**。頻繁にサーバーなんて立てないよね...特に非エンジニアは。そこで忘備録というかせっかくなので、自分のような非エンジニアがチャレンジするときの助けになったらうれしいので、手順をなるべく詳細にまとめなおすことにしました。

### とりあえずEC2立ち上げよう
1. 左上のここで「EC2」を検索
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/850275/e048809b-557a-47de-a4c2-3541e76f9d92.png)

2. 「インスタンス」を選ぼう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/850275/9e0147f7-2feb-4a21-962b-71619df1757c.png)

3. 「インスタンスを起動」する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/850275/a790c9e6-cc37-4247-8046-401e1dcbfd58.png)

4. 「名前とタグ」で適当な名前をアルファベットでつける（例：zazeninu-is-not-a-dog）
5. 「アプリケーションおよびOSイメージ」で「クイックスタート」「Amazon Linux2023AMI」を選ぶ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/850275/28ae8a2b-8097-4c4d-a8c1-4d3e1d7a89af.png)

6. 「インスタンスタイプ」を選ぶ。とりあえずt2.microにしとこう
7. 「キーペア（ログイン）」
新しいキーペアの作成→キーペア名（アルファベット）、RSA、.pemでキーペアを作成する
→pemファイルがダウンロードされる。これ絶対無くしちゃダメ。
7. 「ネットワーク設定」では、「セキュリティグループを作成」、「からのSSHトラフィックを許可」を「自分のIP」、「インターネットからのHTTPトラフィックを許可」にチェックを入れる。
7. とりあえずこれで右側の「インスタンスを起動」を押そう
起動したらインスタンスに接続、で「SSHクライアント」を選ぶ。下の方にssh-i なんちゃらというのがあるので、これをコピペしておく


## SSH接続する
### gitbashが便利
僕はssh接続にはGitBash使っています。Windows上でLinuxコマンドがちょっと使えるという感じのツール。
参考資料→[WindowsでGitBashを使って快適なBashライフを送る](https://qiita.com/alpaca-honke/items/c7516823671e499eac7d)

### 接続してみよう
```
ssh -i (さっき落としたpemファイルを絶対パスで指定） ec2-user@（IPv4アドレス）
```
これをかますと、初回だけ
The authenticity of host'（IPV4アドレス） can't be established.なんちゃらとでるので、yesと打ってエンターを押す。すると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/850275/a3fa379e-a008-4f4d-a86d-8aa6e9cc4e8f.png)
なんか鳥が出てきます。これでOK.

### timezone設定しよう
最初は時間帯が世界標準的な何かで、日本時間じゃないので色々後でイラつくことになります。そこで、日本時間にサーバーをセッティングする必要があります。
[ここを参考にするとすぐできます](https://www.t3a.jp/blog/infrastructure/set-timezone/)。

### インストール
まずはyumをアップデートしてpipをインストールします。
```
sudo yum update
sudo yum install python3-pip 
sudo pip install flask
```
これでOK.

あとはflaskでアプリを書いて、scpでアップロードします。フォルダごと送る時は-rを指定します。
```
scp -i (さっき落としたpemファイルを絶対パスで指定）-r (flaskアプリを書いたフォルダを絶対パスで指定) ec2-user@（IPv4アドレス）:/home/ec2-user/
```
これで送りこまれるので、
```
sudo flask --app (実行ファイル名）.py run --port 80 --host 0.0.0.0
```
これでサーバーが起動します。
ただし、あくまでこれはテストサーバーなので、実際にはwaitressとか入れるといい感じです。

## まとめ
思い出してみたら簡単でした。
テストサーバーまでは簡単なのですが、他にもいろいろやろうと思ったらnginx入れたりDBサーバ立てたりセキュリティ色々いじったりは必要です。でもこの作業たまにやると色々思い出していいなと思いました。

そのうちCloudFormationでどうやるかとか、AMIイメージつかってみるとか、TerraForm使うとかもまとめてみたい。

## 参考資料
### YouTube
- [AWS EC2入門 #01： AWSを始めよう](https://www.youtube.com/watch?v=K7FElmt6Jq4)
昔はじめてAWS触ったときはこの動画を参考にやりました。とてもわかりやすくて本当にありがたかった...
- [【AWS EC2入門】クラウド上にサーバを構築！SSH接続してFlaskのWebアプリをデプロイしてみよう！〜初心者向け〜](https://www.youtube.com/watch?v=AGmkG0wJ8LA)
本も買いましたサプーさん。説明がとても丁寧。

### オンライン学習
- [paiza](https://paiza.jp/works/courses)
python、flask、SQLあたりはだいたいここで覚えました。ブラウザ上でほぼ完結するからわかりやすかったです。
JavaScript: 頻出部分文字列問題

文字列 S 中に閾値以上出現する部分文字列を全て列挙する方法を紹介します(列挙するだけでなく位置、長さ、出現回数もろもろを高速に求める)。率直に言うと接尾辞配列構築する過程で処理を打ち切ると、いい感じに求まります。

## 手法
* multi key quicksort(3分割法)
* 頻出でない部分文字列とそれを含む上位部分文字列は処理を打ち切る

接尾辞木を利用するより遥かにmemory消費は少なく、下ごしらえも遥かに軽微。計算量は`O(n log n)`っぽいですが,長大な繰り返しや連長があると`O(n^2)`程度になりそうです。
## 実装編
```js
let minsup=2; // 閾値. これ以上出現する文字を抽出
/*
ssmine(T,SA,a,z,lv)
@T: 文字列を数値配列化したもの
@SA: Tの接尾辞配列もどき
@a: 始点(最初の呼び出しは0)
@z: 終点(最初の呼び出しはS.length)
@lv:部分文字列長(最初の呼び出しは0)
*/
function ssmine(T,SA,a,z,lv){
	if(z-a<minsup)return;
	var p=a+Math.random()*(z-a)|0,r=SA[a];
	SA[a]=SA[p],SA[p]=r;
	for(var t=T[SA[a]+lv],i=a+1,c=z-1,b=i,d=c;;SA[c--]=r){
		for(;b<=c&&(r=T[SA[b]+lv]-t)<1;b++)
			r||(r=SA[i],SA[i++]=SA[b],SA[b]=r);
		for(;b<=c&&(r=T[SA[c]+lv]-t)>=0;c--)
			r||(r=SA[c],SA[c]=SA[d],SA[d--]=r);
		if(b>c)break;
		r=SA[b],SA[b++]=SA[c]
	}
	p=i-a,r=b-i;if(p<r)r=p;
	for(p=a,b-=r;r--;SA[b++]=t)t=SA[p],SA[p++]=SA[b];
	p=d-c,r=z+~d;if(p<r)r=p;
	for(p=b,z-=r;r--;SA[z++]=t)t=SA[p],SA[p++]=SA[z];
	ssmine(T,SA,a,a+=b-i,lv),b+=z+~d;
	if(b-a>=minsup&&~t)lv&&T.get(T,SA,a,b-a,lv),ssmine(T,SA,a,b,lv+1);
	ssmine(T,SA,z-d+c,z,lv)
}
```
```js:実行例
minsup=2;	// 2未満不可
var S=[97,98,114,97,99,97,100,97,98,114,97],
	a=S.length,n=a,
	I=Array(a);
S[a]=-1; //終端に番兵追加
for(;I[--a]=a;);

//列挙関数の例
//S:文字(数値)配列. I:位置配列. i:I上の出現位置. c:出現数. d:文字列長
function print(S,I,i,c,d){
	console.log("位置",I[i],"数",c," 長さ",d+1,S.slice(c=I[i],c+d+1))
}
S.get=print; //callbackもどき
ssmine(S,I,0,n,0);
```
codepen的実験装置。画面横幅が狭いと表示しきれない親切設計
<p class="codepen" data-height="400" data-default-tab="html,result" data-slug-hash="ZYYZoxV" data-pen-title="Mining Frequent Substrings" data-user="xezz" style="height:300px;box-sizing:border-box;display:flex;align-items:center;justify-content:center;border:2px solid;margin:1em 0;padding:1em"> <span>See the Pen <a href="https://codepen.io/xezz/pen/ZYYZoxV">Mining Frequent Substrings</a> by xezz (<a href="https://codepen.io/xezz">@xezz</a>) on <a href="https://codepen.io">CodePen</a>.</span></p><script async src="https://public.codepenassets.com/embed/index.js"></script>

## 参考文献
[頻出部分文字列のマイニング](http://2boy.org/~yuta/publications/nl158-slides.pdf)
localstackにterraformでs3 bucketを作成する

## はじめに

本記事では、localstackにterraformでs3 bucketを作成する方法を紹介します。

## 環境情報

| コンポーネント | バージョン | 備考 |
|-|-|-|
| PC | Intel® NUC Kit NUC11PAHi7 | SSD: 2TB<br>Memory: 64GB|
| OS | Ubuntu 24.04  | |
| Docker | 26.1.3 | |
| LocalStack | 4.4.0 | |
| awscli | 2.27.17 | |
| awscli-local | 0.22.0 | |
| terraform | v1.12.0 | |

## 手順

以下の記事で紹介した環境がセットアップされていることを前提とします。
https://qiita.com/ykmchd/items/a6f8834a5787cc7fbe85

### provider.tfを作成する

以下のprovider.tfを作成します。
```terraform:provider.tf
terraform {
  required_version = "~> 1.12.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5"
    }
  }
}

provider "aws" {
  access_key        = "test"
  secret_key        = "test"
  region            = "us-east-1"
  s3_use_path_style = true

  endpoints {
    s3 = "http://localhost:4566"
  }
}
```

ポイントはs3_use_path_style = trueを設定することです。
デフォルトのバーチャルホスト形式でs3 bucektを表現します。
しかし、localstackではバーチャルホスト形式が使えないため、このオプションを指定する必要があります。
https://registry.terraform.io/providers/hashicorp/aws/5.39.1/docs#s3_use_path_style-1

### s3.tfを作成する

以下のs3.tfを作成します。
```terraform:s3.tf
resource "aws_s3_bucket" "s3-localstack" {
  bucket = "s3-localstack"
}
```
s3-localstackという名前のs3 bucketを作成します。

### terraformを実行する

以下のコマンドを実行します。
```bash
terraform init
terraform plan
terraform apply
```

### s3 bucketを作成できたか確認する

awslocalコマンドでs3 bucketが作成できたか確認します。
```bash
❯ awslocal s3 ls           
2025-05-18 23:12:40 s3-localstack
```

## 最後に

localstackにterraformでs3を作成する方法を紹介しました。
terraformの練習をするときに、実際のAWSを使うと料金が心配です。
しかし、localstackだと使い勝手は違いますが、料金を気にせず試せるのが良いですね。
【Rust】ジェネリクスの型パラメータってどこに書いたらいいの

Rustを学び始めると、ジェネリクス（総称型）の概念とその記法に戸惑うことがあります。特に、ジェネリック型パラメータをどこに配置するべきか—`impl`ブロックの後ろに置くべきか、それとも個々のメソッド定義に置くべきか—という点は混乱しやすいです。

## ジェネリクスの基本的な配置ルール

Rustでジェネリクスを使用する場合、その配置には下記のルールがあることを認識するとよいです：

1. 構造体全体で使用するジェネリック型パラメータは、構造体名の後に配置します
2. 実装全体（`impl`ブロック内の複数のメソッド）で共有されるジェネリック型パラメータは、`impl`キーワードの後に配置します
3. 特定のメソッドでのみ使用される型パラメータは、そのメソッド名の後に配置します

## コード例

### 例1: 構造体とimplブロックのジェネリクス

2つの値を持つ`Point`構造体の例です：

```rust
struct Point<X1, Y1> {
    x: X1,
    y: Y1,
}

impl<X1, Y1> Point<X1, Y1> {
    fn new(x: X1, y: Y1) -> Self {
        Point { x, y }
    }
    
    fn get_x(&self) -> &X1 {
        &self.x
    }
    
    fn get_y(&self) -> &Y1 {
        &self.y
    }
}
```

ここでは、`X1`と`Y1`というジェネリック型パラメータを構造体定義で使用し、同じパラメータを`impl`ブロックでも使用しています。これらのパラメータは`impl`ブロック内のすべてのメソッドで共有され、メソッド間で一貫性のある型として扱われます。

### 例2: メソッド固有のジェネリクス

次に、特定のメソッドだけで使用するジェネリック型パラメータの例を見てみましょう：

```rust
struct Point<X1, Y1> {
    x: X1,
    y: Y1,
}

impl<X1, Y1> Point<X1, Y1> {
    fn mixup<X2, Y2>(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
        Point {
            x: self.x,
            y: other.y,
        }
    }
}
```

この例では、`mixup`メソッドが別の`Point`インスタンスを引数に取り、新しい`Point`を返します。ここで重要なのは：

- `X1`と`Y1`は構造体とimplブロック全体で共有される型パラメータ
- `X2`と`Y2`は`mixup`メソッド固有の型パラメータで、このメソッドの中でのみ有効

このメソッドは2つの異なる`Point`インスタンスの値を組み合わせて、新しい`Point`インスタンスを作成します。

## なぜimplブロックにすべての型パラメータを置けないのか？

どうせならimplブロックで全ての型パラメータを指定しておけばいいと思いませんか？しかし次のようなコードを書くとエラーに遭遇します：

```rust
// このコードはコンパイルエラーになります
struct Point<X1, Y1> {
    x: X1,
    y: Y1,
}

impl<X1, Y1, X2, Y2> Point<X1, Y1> {
    fn mixup(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
        Point {
            x: self.x,
            y: other.y,
        }
    }
}
```

このコードはコンパイルできません。エラーメッセージは以下のようになります：

```
error[E0207]: the type parameter `X2` is not constrained by the impl trait, self type, or predicates
 --> src/main.rs:6:12
  |
6 | impl<X1, Y1, X2, Y2> Point<X1, Y1> {
  |            ^^ unconstrained type parameter
```

### なぜエラーになるのか？

このエラーが発生する理由は、Rustの型システムの設計に関係しています：

1. `impl`ブロックで宣言されたジェネリックパラメータは、「制約される（constrained）」必要があります
2. 「制約される」とは、**そのパラメータが実装対象の型（この場合は`Point<X1, Y1>`）、トレイト境界、またはwhere節のいずれかで使用される**ことを意味します
3. `X2`と`Y2`はこれらのいずれにも登場しないので「制約されていない型パラメータ（unconstrained type parameter）」としてエラーになっています

エラーメッセージが指摘している通り、`X2`と`Y2`は`impl`ブロックのどこにも「束縛」されていません。**これらのパラメータは各メソッド呼び出しごとに異なる型を取る可能性があり、`impl`ブロックレベルで固定することができない**と考えると、理解しやすいかもしれません。

## まとめ

Rustにおけるジェネリクスの配置ルールをまとめると：

1. 構造体全体で共有される型パラメータは構造体名の後に配置
2. `impl`キーワードの後に配置される型パラメータは`impl`ブロック内のすべてのメソッドで共有される
3. 特定のメソッドでのみ使用される型パラメータは、そのメソッド名の後に配置

この区別を理解することで、Rustのジェネリクスをより効果的に活用できるようになりますし、コードの読解力が向上すると思います！

## 参考資料

- [Rustの公式ドキュメント - ジェネリクス](https://doc.rust-lang.org/book/ch10-00-generics.html)
- [Rust By Example - ジェネリクス](https://doc.rust-lang.org/rust-by-example/generics.html)
EBAZ4205 FPGA Xilinx編 第2版　演習2-4-1

# SWでLEDパターンを切り替える課題

- どうせなら、自分で仕様を決めて作ってみます。
- SWを押すとLED点灯パターンをバイナリカウンタからグレイコードカウンタに変化させます。
- githubにアップしています　https://github.com/ShigeoYakuno/EBAZ4205

## 拡張ボードの入手
まず演習するにあたり、SWなどを拡張したい
HDMIの課題に向け購入していた拡張ボードを使用した。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3657924/081853b1-4620-4885-9b4a-f3b9eb38d2bf.png)


- アダプタボードの回路図は、わかりにくいが以下の通り
- SWは、KEY1～５を使う
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3657924/0c1863c2-d0e1-45e0-b60d-6338638bdc30.png)

<br>
- 使いそうなポートは以下の通り。HDMI,LED,SW。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3657924/9332d23d-1a53-4988-aafd-23225ed8dd33.png)


## スイッチはSW３とSW１を使用。

- 拡張ボードのSWは５つあり、うち１つはリセット(真ん中のSW3)を使う。
- 他に4つ使えるが、今回はSW１(上)を使う。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3657924/a929fd04-3958-4bb6-afba-9a5ee36247f3.png)
													

### グレイコードカウンタの特徴
- 点灯パターンとして使用するグレイコードカウンタについて
- 各遷移で1ビットしか変わらない
- 周回（ループ）させても常に1ビット差
- 誤動作やラッチアップが起きづらい



### バイナリ➡グレイコードの変換
- G = B >> 1 ^ B (G:グレイコード　B:バイナリ）
- 例 B= 3'b111 なら B>>1=3'b011 で　3'b111^3'b011で3'b100に変換できる
~~~verilog
assign gray = bin ^ (bin >> 1);
~~~

- 今回は以下の表をべた書きした

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3657924/de246391-8ad1-4c59-bfcf-e3a76986e460.png)

### EBAZ4205での変更点

- RST(SW3)が、例と反転しているので下記のように冒頭に1行付け加えた
- あとは例題のRSTの箇所を「rstn」に変更する。debounce.vも同様に変更すること

~~~verilog
module blinkpat (
    input               CLK,
    input               RST,
    input       [0:0]   BTN,
    output  reg [2:0]   LED_123
);

// @yaku EBAZ4205 負論理 RST を正論理に変換
wire rstn = ~RST;

/* チャタリング除去回路を接続 */
wire btnon;
debounce d0 (.CLK(CLK), .RST(RST), .BTNIN(BTN), .BTNOUT(btnon));
~~~

### 今日はここまで
arduino1.8を使ってPCからESP32のSPIFFS領域にファイルを送信する

今回は、ESP32のSPIFFS領域にファイルを保存する必要があったので記録として残します。

## 1、開発環境
arduino1.8.19

## ２、方法
１、arduino1.8.19をインストール。以下のURLから
https://www.arduino.cc/en/software/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1339052/0b1a0423-1ae9-4f43-b6fb-59e95270723e.png)

2、今回のボードの設定は以下です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1339052/51fcaf4c-2342-452b-8399-b59317157ddf.png)

３、以下のサイトからESP32FS-1.1.zipをダウンロードして、解凍後
C:\Users\*****\OneDrive\ドキュメント\Arduino\toolsフォルダに保存（toolsフォルダは作成する必要あり）
https://github.com/me-no-dev/arduino-esp32fs-plugin/releases/tag/1.1
（ESP32FSごと保存）

４、arduinoIDEを起動してESP32 Sketch Data Uploadが表示されればOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1339052/20200804-4b83-4411-9a1a-532c5ca0b85b.png)

５、プログラムと同じフォルダにdataフォルダを作成して、そのフォルダに書き込みたいファイルを保存。
　　その後、ESP32 Sketch Data Uploadを実行したら確認できます。
  　今回は、config.txtファイルを作成して、中身は「hello」と書き込みをしています。

６、以下のプログラムを作成して動作確認して下さい。（）
```c
#include "FS.h"
#include <SPIFFS.h>

void setup() {
    SPIFFS.format(); // 初回フォーマット時のみ有効にする（次回書き込みからは無効にする）
    Serial.begin(115200);
    if (!SPIFFS.begin(true)) {
        Serial.println("SPIFFSのマウントに失敗しました。");
        return;
    }

}

void loop() {
    delay(2000);
    File file = SPIFFS.open("/config.txt", "r");
    if (!file) {
        Serial.println("ファイルを開けませんでした。");
        return;
    }

    Serial.print("config.txtの内容:");
    while(file.available()){  // ファイルのデータ分繰り返す
      char c = file.read(); // ファイルから1バイトずつデータを読み取り
      Serial.print(c);      // 液晶へ表示
    }
    Serial.println();
    file.close();
}

```

７、２秒間隔でhelloが表示されればOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1339052/81b14478-2eb1-451d-bb4b-beeb8b3e4de3.png)
はじめてのOJT担当の心得

# はじめてのOJT担当の心：メンティーが最速で成長するために大切なこと

## はじめに

現場で活躍するエンジニアを育てるには、OJT（On-the-Job Training）が非常に重要です。特に若手エンジニアにとって、現場での実践は座学以上に価値ある学びになります。

私は自社開発企業で働く、2年目のWEBエンジニアです。新しく新卒として入社してきてくださるエンジニアのOJTメンターとして活動することになりました。

今まで自分がメンティーの立場で感じたことやマネジメントの書籍で学んだことを整理してみました。

本記事では、私が意識しているOJTの目的、具体的な行動指針、そして方向性の設計についてまとめます。

## OJTの目的：3つの柱

### 1. 最高速度で成長する

エンジニアとして「最高速度で成長してもらう」ことが最も重要な目的です。そのためには以下の点を大切にしています：

- モチベーションを維持すること
- フィードバックはポジティブ：改善点 = 4：1を意識
  - 自信を失わせず、前向きに受け取れるよう心がけます。
- 好きな技術分野に関わってもらうこと
  - 「好きこそ物の上手なれ」という言葉の通り、興味がある分野こそ、自然と成長が加速します。
- 常にヒアリングを行い、好奇心の方向性を掴みつつも、ブレない軸を育てていきます。

### 2. 社内コミュニケーション力の強化

エンジニアとしてだけでなく、将来的に他職種に進む可能性も見据えたうえで、社内での円滑なコミュニケーション能力は必須です。

- 方法を一方的に教えるのではなく、「機会」を与える
- 自分から周囲に話しかけるハードルを下げる環境作りを意識
- 質問されたときにはしっかり答えるが、「指示」はしない
- 自発性が何よりの成長の源です。

### 3. エンジニアとしてのキャリアの方向性を示す

業界の背景や動向を踏まえた上で、その人自身のキャリアの軸を一緒に考えることが大切です。

- 志向性を丁寧にヒアリング
- 「こうすべき」と押し付けるのではなく、考えるヒントや情報を提供し続ける
- 自分の意思で方向性を定めるからこそ、長期的なモチベーションにつながります。

## 具体的なアクション

### ポジティブな関わり方

- フィードバックは「良かった点」を中心に伝える（4：1ルール）
- やりたいことをヒアリングし、好奇心を最大限に活かす場を提供

### コミュニケーションのサポート

- 他職種との接点を自然に持てるように、場の創出に努める
  - 例：雑談の機会、ご飯に誘う、ミーティング後のちょっとした会話など
- 自分が話しかけやすい雰囲気を率先して作る

### キャリア形成の伴走

- 最新トレンドや経験談を積極的に共有
- ヒントを与えつつ、最終的には本人の意思で決めてもらう

## OJTの運用スタイル

- 1on1を定期的に実施し、内省と対話の場を作る
- 日常的な声がけで距離感を近づける
- ときにはご飯に行くなど、リラックスした環境で話すことも効果的です

## おわりに

OJTは単なる「仕事を教える場」ではなく、その人の成長とキャリアの土台を作る大切な機会です。

だからこそ、本人主体で考え、行動し、成長できるように伴走する姿勢が求められます。

若手エンジニアの可能性を最大限に引き出すために、今後もよりよい関わり方を模索し続けていきたいと思います。
【Unity × AdMob】リワード広告後にGoogle Drive連携処理を実行する際の注意点

UnityアプリでAdMobのリワード広告を活用し、ユーザーにGoogle Driveへのインポート・エクスポート機能を提供する構成はよくあります。

しかし、この組み合わせには**処理タイミングに関する注意点**が存在します。  
本記事では、広告報酬獲得後にDrive連携処理を行う場合に**陥りやすいエラー**とその**回避方法**を解説します。

---

## 想定ユースケース

- リワード広告視聴後にアプリ設定をGoogle Driveにバックアップ
- 広告を見たユーザーに、Driveからのデータ復元を提供

---

## よくあるNGパターン

以下のような流れで処理を実行すると、高確率でエラーになります。
```csharp
AdMobリワード広告表示
↓
OnUserEarnedRewardイベント内で即座にGoogle Drive処理を実行
```

この時点では以下のような不安定要素が残っています：

- `Activity`が完全に復帰していない
- UIスレッドの切り替え中
- Google認証が非同期で未完了

結果、以下のような不具合が発生します。

---

## 代表的なエラー例

| エラー | 内容 |
|--------|------|
| `ActivityNotFoundException` | ファイルピッカーが起動できない |
| `NullPointerException` | Intent対象がnull |
| 無反応／クラッシュ | 非同期競合による処理停止 |

---

## 解決策①：ユーザー操作をトリガーにする

```csharp
void OnUserEarnedReward(...) {
    canExecuteDriveOperation = true;
}

void OnClickDriveImport() {
    if (!canExecuteDriveOperation) return;
    StartDriveImport();
    canExecuteDriveOperation = false;
}
```
OnUserEarnedReward() では**「Drive処理してOKな状態」であることを記録**するだけ

実際のDrive処理はUIボタン等でユーザーが明示的に行う

この設計により、処理タイミングの安定性が大幅に向上します。

## 解決策②：広告終了イベントをトリガーにする
AdMobやUnity Adsの OnAdClosed() / OnUnityAdsDidFinish() を活用して、広告終了後にDrive処理を開始する方法です。

```csharp
void HandleAdClosed() {
    StartDriveExport();
}
```
この方法でも処理は可能ですが、広告終了直後は一部UIが不安定なこともあるため、ワンクッション（数百ms遅延）を入れるとさらに安定します。

### 補足：ワンクッションを入れる方法（例）
```csharp
IEnumerator DelayedDriveExport() {
    yield return new WaitForSeconds(0.3f);
    StartDriveExport();
}
```

## まとめ
| やること                | 理由                   |
| ------------------- | -------------------- |
| 報酬イベント内で即Drive処理しない | Activity未安定、認証未完了の恐れ |
| UI経由 or 広告終了後に処理開始  | タイミングが安定しやすい         |
| 処理前に数百ms待機させる       | UIスレッドの安定を待つため       |


## ブログでもUnityや個人開発ネタを発信中です！
開発ノウハウやアプリ制作過程、Unity連携系のハマりポイントなど
より深掘りした内容をブログにまとめています。
▶ https://syunpp.com

## 公開中のアプリ一覧はこちら！
実際にUnityで開発してリリース済みのアプリ一覧をまとめています。
▶ https://syunpp.com/公開中のアプリ一覧/
保守開発のリアル 〜安定稼働中のシステムにどう向き合うか〜

# はじめに
システム開発の現場では、大きく分けて「新規開発」と「保守開発」という2つのタイプがあります。

新規開発では、要件定義から設計、構築、テストまで一連の開発フローを通してゼロからシステムを作っていきます。一方で保守開発は、すでに稼働しているシステムに対して機能追加や不具合修正などを行う開発です。

現場の実感としても、多くのエンジニアが関わる開発の大半は「保守開発」であることが多いのではないでしょうか。

本記事では、これから保守開発に携わるエンジニアや、既に関わっている方に向けて、保守開発におけるポイントや心構えについて共有できればと思います。

※主に業務アプリケーションを想定しています。

# 保守開発とは？
#### 1. 「動いているもの」に手を加えるという緊張感
保守開発の本質は「現在進行形のシステム」に対して改修を加えることにあります。
特にクリティカルなシステムや大規模な業務システムの場合、「今も使われている」という緊張感があります。

本番リリースのたびに、「障害は出ないか」「問い合わせが来ないか」と緊張するのは、保守開発に携わる人なら誰もが経験していることでしょう。

#### 2. 既存処理を読み解く力が求められる
保守開発では、過去のソースコードや設計資料を頼りに開発を進めることになります。ただし、ドキュメントが完璧に整備されているとは限らず、「コードが唯一の真実」になっているケースもしばしばです。

そのためには、既存コードをリバースエンジニアリングする力が必要です。

- <strong>似たような機能がどこかに存在していないか</strong>

- <strong>過去にどういう実装判断がされたのか</strong>

- <strong>この処理の裏側にある業務的背景は何か</strong>

こうした観点を持ちながら、必要に応じて既存処理を参考にしたり、新たな処理を設計・実装したりする判断が求められます。

#### 3. 品質とメンテナンス性のバランス
「新しいやり方で書いた方がキレイになる」と思うことはよくあります。
しかし保守開発では、あえて既存処理に寄せるケースも少なくありません。

たとえば、

- <strong>同じようなロジックが既に存在している</strong>

- <strong>実際にその処理が長年動いている実績がある</strong>

- <strong>処理を置き換えることで新たな障害を生む可能性がある</strong>

といった理由から、あえて古いロジックに合わせて実装することも「品質」を保つ上での戦略です。
こうすることで将来的な改修コストを抑えることができ、チーム全体としての生産性を担保することにも繋がります。

# 保守開発における注意点

#### 「いま動いている」ことの重み
保守開発で最も注意すべき点は、「今まさに動いているシステムに手を加える」ということです。

これはつまり、
　<strong>変更によって新たな障害を生み出すリスクが常にある</strong>
ということを意味します。

そのため、以下のような慎重な進め方が必要です。

- <strong>変更対象の処理がどこに影響を及ぼすかの影響調査</strong>

- <strong>検証環境での入念な動作確認</strong>

- <strong>万が一に備えたロールバック</strong>

場合によっては「石橋を叩いて、叩いて、それでも渡らない」という判断も必要になります。
利便性向上や新機能追加よりも、安定性を優先するのが保守開発の基本スタンスだったりします。

# 最後に：保守開発のポイントまとめ
保守開発は、現場では欠かせない仕事です。
システム全体を理解し、安定稼働を守りながら改修を進めていくためには、着実さと判断力が求められます。

改めて、保守開発のポイントを整理してみます。

✅ <strong>動いているシステムに手を加えるという緊張感と責任感</strong>

✅ <strong>バグ（デグレ）を出さないことが最優先</strong>

✅ <strong>既存コードを活用しながら、将来コストも考慮に入れる</strong>

✅ <strong>システム全体の整合性や保守性にも気を配る</strong>


保守開発に取り組む皆さんにとって、本記事が少しでも参考になれば幸いです。
SQLで中央値を求める方法

# SQLで中央値を求める方法

SQLでは `AVG()` や `SUM()` は簡単に使えますが、**中央値（メディアン）**を求めるのは少し工夫が必要です。

この記事では、PostgreSQLの**ウィンドウ関数**を使って、効率的に中央値を求める方法を紹介します。

毎度のことながら、ミックさん著の「SQL実践入門 高速でわかりやすいクエリの書き方」から、理解できなかった箇所を噛み砕いて記事にしています。


---

## 💡 発想のポイント

- 中央値とは「ソートしたときに真ん中に来る値」
- データ件数が奇数：真ん中の1件
- データ件数が偶数：真ん中の2件の平均

---

## 🛠 アプローチの仕組み

以下の工夫を使います：

1. `ROW_NUMBER()` を使って、ソート後の順位を取得  
2. `COUNT(*) OVER ()` で全体の件数を取得  
3. `diff = 2 × ROW_NUMBER - COUNT(*)` という式で中央値候補を判定  
4. `diff BETWEEN 0 AND 2` の行だけを抽出し、`AVG(salary)` で中央値にする  

---

## 📊 例：salary テーブル

```sql
SELECT AVG(salary)
FROM (
  SELECT salary,
         2 * ROW_NUMBER() OVER (ORDER BY salary)
         - COUNT(*) OVER () AS diff
  FROM salary
) TMP
WHERE diff BETWEEN 0 AND 2;
```

## 🤓 なぜこれで中央値になるのか？

まず、この式を見てください：


```sql
2 * ROW_NUMBER() - COUNT(*) OVER ()
```

この `diff` の値を使って、**中央値に該当する行だけを抽出**しています。

たとえば、3行のデータが `salary` テーブルにあった場合：

| ROW_NUMBER | salary | COUNT(*) | diff = 2×ROW - COUNT |
|------------|--------|----------|------------------------|
| 1          | 300    | 3        | -1                     |
| 2          | 400    | 3        | 1 ✅ 中央値！           |
| 3          | 500    | 3        | 3                      |

この中で `diff BETWEEN 0 AND 2` に該当するのは2行目だけです。つまり 奇数件の場合は真ん中の1件を正確に取得できます。

---

### 偶数件の場合は？

例えばデータが4件のとき：

| ROW_NUMBER | salary | COUNT(*) | diff |
|------------|--------|----------|------|
| 1          | 300    | 4        | -2   |
| 2          | 400    | 4        | 0 ✅ |
| 3          | 500    | 4        | 2 ✅ |
| 4          | 600    | 4        | 4    |

このときは `diff BETWEEN 0 AND 2` に該当するのは 2行目と3行目。これら2つの平均を取ることで、偶数件でも正しい中央値になります。

---

このように、`diff` 式を使うことで、奇数・偶数の両方に対応した中央値抽出が実現できます。


## 🚀 パフォーマンスも良好

この方法はパフォーマンス面でも優れています。

### ✅ ソートは1回だけで済む

`ROW_NUMBER()` と `COUNT(*) OVER ()` は、どちらも同じ順序（`ORDER BY salary`）を使うため、1回のソートで両方の処理が済みます。これにより無駄な処理が発生しません。

### ✅ 複雑なJOINやサブクエリが不要

他の中央値の求め方では、自己結合やネストの深いサブクエリが必要になることがありますが、今回の方法は非常にシンプルな1段のクエリで完結します。

### ✅ インデックスが使える可能性あり

`ORDER BY salary` にインデックスが貼られていれば、`ROW_NUMBER()` のパフォーマンスも向上します。

---

## ✅ まとめ

- 中央値は SQL で直接求めるのが難しいが、ウィンドウ関数と `diff` 式を使えば効率的に計算可能
- データ件数が奇数・偶数どちらでも対応できる
- ソート回数が1回で済むので高速
- シンプルな構文で読みやすく、保守性も高い

---