OmniAICreator/Qiita-1.07M huggingface dataset:
GitHubで `~/.ssh` 配下を安全に管理する（git-crypt活用法）

複数のサーバーやPCを使っていると、`~/.ssh/config` や秘密鍵の管理がどんどん面倒になりますよね。とはいえ、SSHの秘密鍵を手作業でコピーするのは避けたいし、かといってそのままGitHubで管理するのはリスキー…。

そこで今回は、**`git-crypt` を使ってセキュアに `.ssh` 配下をGitHubで管理する方法**を紹介します。  
もちろん、GitHubリポジトリは **private** に設定してください！

## :wrench: 1. git-crypt のインストール

まず、`git-crypt` を各自の環境にインストールしましょう。

- **macOS:**

  ```bash
  brew install git-crypt
  ```


- **Ubuntu / Debian:**

  ```bash
  sudo apt-get update
  sudo apt-get install git-crypt
  ```

- **その他のOS:**

  [公式リポジトリ](https://github.com/AGWA/git-crypt) からソースビルドしてください。

## :file_folder: 2. リポジトリの準備と git-crypt の初期化

1. **リポジトリを作成・初期化**

   ```bash
   cd ~/.ssh
   git init
   ```

2. **git-crypt を初期化**

   ```bash
   git-crypt init
   ```

## :closed_lock_with_key: 3. 暗号化対象ファイルの指定（`.gitattributes`）

`git-crypt` は、特定のファイル/パスだけを暗号化対象にできます。  
例えば、`config` や `id_rsa` などを含めたい場合は `.gitattributes` に以下を記述します。

```gitattributes
# すべてのファイルを暗号化
* filter=git-crypt diff=git-crypt

# ただし、以下のファイルは暗号化対象から除外
config -filter -diff
README.md -filter -diff
```

## 🧪 4. 暗号化チェック用ファイルの作成

暗号化が効いているかの確認用に、以下のような `checker.txt` を用意しておきます。

```txt
If you can read this, encryption is not working.
```

このファイルがGitHub上で**平文で見えていたらアウト**です。

ついでに `.gitignore` も作っておきましょう（例: macOS用）:

```bash
echo .DS_Store > .gitignore
```

## 📤 5. Gitリモート登録とコミット

```bash
git remote add origin git@github.com:yourname/.ssh.git
git add --all
git commit -m "Add git-crypt attributes for sensitive files"
git push -u origin main
```

## 🧑‍💻 6. GPGキーの登録（復号できるユーザーの追加）

1. **GPGキーがない場合は生成**

   ```bash
   gpg --full-generate-key
   ```

2. **自分のGPGキーIDを確認**

   ```bash
   gpg --list-secret-keys --keyid-format LONG
   ```

   出力例:

   ```
   sec   rsa4096/ABCD1234EFGH5678 2024-01-01 [SC]
   ```

3. **復号可能なユーザーとしてGPGキーを登録**

   ```bash
   git-crypt add-gpg-user ABCD1234EFGH5678
   ```

## 🔄 7. 別のPCで復号する手順（秘密鍵の移動）

### ✅ 現在のPCでの操作

1. **秘密鍵をエクスポート**

   ```bash
   gpg --export-secret-keys -a ABCD1234EFGH5678 > mykey-private.asc
   ```

2. **USB/SCPなどで安全に別PCへコピー**

   > ⚠ このファイルは絶対に流出させないように！

### ✅ 別のPCでの操作

1. **秘密鍵をインポート**

   ```bash
   gpg --import mykey-private.asc
   ```

2. **キーがインポートされたか確認**

   ```bash
   gpg --list-secret-keys
   ```

3. **リポジトリをクローン**

   ```bash
   git clone git@github.com:yourname/.ssh.git
   cd .ssh
   ```

4. **`git-crypt` で復号**

   ```bash
   git-crypt unlock
   ```

   これで、暗号化されていたファイルがローカルで復号されます。

## 📎 補足：公開鍵も一緒に渡す場合

必要に応じて、公開鍵もエクスポート可能です。

```bash
gpg --export -a ABCD1234EFGH5678 > mykey-public.asc
```

ただし、秘密鍵をインポートすると公開鍵も含まれているので、通常は不要です。

## 🔒 最後に：セキュリティ上の注意

- `mykey-private.asc` は作業後に必ず削除（`shred`推奨）
- GPGキーにはパスフレーズをかけておく
- 信頼できるPC以外では秘密鍵を扱わない

## ✅ まとめ

- `git-crypt` + GPG を使えば、SSH設定や秘密鍵を**セキュアにGit管理**できる
- `.gitattributes` でファイルを選択的に暗号化
- GPGキーを使って復号権限をコントロール
- リポジトリは必ず **private** に！

安全に運用しながら、SSH設定の煩雑さを一気に解消していきましょう 💪
JetBrains IDE、エディターから直接コミットしたり、差分を確認したり、ロールバックできる

JetBrains IDEはGit・GitHub関連の豊富な機能を備えています。実は、エディターから、素早く差分を直接コミットしたり、差分を確認したり、ロールバックすることも可能です。

※ 本投稿は、Rider 2024.3.7のスクリーンショットを利用しています。

次のようなコード編集を行いました。

|編集前|編集後|
|:---:|:---:|
|![スクリーンショット 2025-04-06 15.03.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4125/9b5affc1-5a51-4203-a1e0-187b5dc857ea.png)|![スクリーンショット 2025-04-06 15.02.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4125/cdb42312-4bf6-4a6b-a8d7-1c73121e3506.png)|


エディターの左側「ガター」領域に注目してください。

* 追加がある行に緑色のマーカー
* 変更があった行に青色のマーカー

が、それぞれ表示されています。

このガターのマーカーをクリックすると、次の様なツールバーが表示されます。

![スクリーンショット 2025-04-06 15.04.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4125/f2d0019b-d839-4f73-94a9-9214efcf540f.png)



ツールバー中の下図赤枠で囲んだボタンをクリックすると、差分をロールバック（打ち消す）できます。

![スクリーンショット 2025-04-06 15.06.01のコピーあ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4125/6d768fb1-dc87-488f-802b-808fb9173c70.png)


ツールバー中の下図赤枠で囲んだボタンをクリックすると、

![スクリーンショット 2025-04-06 15.06.01のコピーあ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4125/9d399d28-d1c5-4191-aaaa-b924f030a100.png)

次の様な差分を確認できる画面が開きます。

![スクリーンショット 2025-04-06 15.10.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4125/8d548631-3e60-4c7b-8beb-523ec22c03b3.png)

ツールバー中の下図赤枠で囲んだボタンをクリックすると、変更前のコードをクリップボードにコピーします。

![スクリーンショット 2025-04-06 15.06.01のコピーあ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4125/4eb7fd2d-b03f-4c9d-92d7-5eb02c779552.png)

ツールバー中の下図赤枠で囲んだボタンをクリックすると、Change Listに追加できます。

![スクリーンショット 2025-04-06 15.06.01のコピーあ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4125/bd7e821f-d1e2-4659-bf9f-0a3e79e87e7c.png)

ツールバー中のテキストエディットボックスをクリックすると、領域が広がります。コミットメッセージを入力し、右側のボタンを押すと、直接コミットすることができます。

![スクリーンショット 2025-04-06 15.12.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4125/9b9f5ff8-e50a-4a8f-b7c8-17a3187d5795.png)

このようにJetBrains IDEでは、エディターから素早く差分を直接コミットしたり、差分を確認したり、ロールバックすることも可能です。
Day.js(dayjs)の使い方まとめ

JavaScriptの日付の処理・操作のライブラリ[Day.js](https://day.js.org/en/)(dayjs)の使い方をまとめてみました。

## format
日付をフォーマットして取得することができます。

```
const day = dayjs('2025-01-01 17:10:30')
day.format('YYYY-MM')  // 2025-01
day.format('YYYY-MM-DD') // 2025-01-01
day.format('YYYY-MM-DD HH:mm:ss')  // 2025-01-01 17:10:30
day.format('YYYY月M日D日') // 2025月1日1日
```

## subtract
「◯日前」「◯週間前」「◯ヶ月前」「◯年前」などの日付を取得できます。

```
const day = dayjs('2025-01-01 17:10:30')
day.subtract(1, 'day').format('YYYY-MM-DD')  // 2024-12-31
day.subtract(1, 'week').format('YYYY-MM-DD') // 2024-12-25
day.subtract(1, 'month').format('YYYY-MM-DD')  // 2024-12-01
day.subtract(1, 'year').format('YYYY-MM-DD') // 2024-01-01
```

## add
「◯日後」「◯週間後」「◯ヶ月後」「◯年後」などの日付を取得できます。

```
const day = dayjs('2025-01-01 17:10:30')
day.add(1, 'day').format('YYYY-MM-DD')  // 2025-01-02
day.add(1, 'week').format('YYYY-MM-DD') // 2025-01-08
day.add(1, 'month').format('YYYY-MM-DD')  // 2025-02-01
day.add(1, 'year').format('YYYY-MM-DD') // 2026-01-01
```

## year、month、date
それぞれ年、月、日を取得できます。

```
const day = dayjs('2025-01-01 17:10:30')
day.year();  // 2025
day.month();  // 0　※0〜11で取得されます
day.date();  // 1
```

## startOf、endOf
月初や月末の日付を取得できます。

```
const day = dayjs('2025-06-15 17:10:30')
day.startOf('month').format('YYYY-MM-DD') // 2025-06-01
day.endOf('month').format('YYYY-MM-DD')  // 2025-06-
day.startOf('year').format('YYYY-MM-DD') // 2025-01-01
day.endOf('year').format('YYYY-MM-DD')  // 2025-12-31
```

## isBefore、isAfter、isSame
日付の比較ができます。

```
const day1 = dayjs('2025-01-01 00:00:00')
const day2 = dayjs('2025-01-02 00:00:00')
const day3 = dayjs('2025-01-03 00:00:00')

day1.isBefore(day2) // true
day2.isAfter(day3) // false
day3.isSame(day1) // false
```

第二引数で日付のどの単位までで比較するかを指定できます。
```
const day1 = dayjs('2025-01-01 00:00:00')
const day2 = dayjs('2025-01-01 00:01:00')

console.log(day1.isSame(day2, 'year'))  // true
console.log(day1.isSame(day2, 'month'))  // true
console.log(day1.isSame(day2, 'date'))  // true
console.log(day1.isSame(day2, 'hour'))  // true
console.log(day1.isSame(day2, 'minute'))  // false
console.log(day1.isSame(day2, 'second'))  // false
```
【CSS、Chakra-ui】floatを使うと親要素から高さを認識されなくなるの気を付けたい

## ❓ 問題

以下のようなコードで `Button` が `Box` の外にはみ出している、本来であれば【戻るボタン】をボーダー内に設置したい。（コードは省略していますが）

```tsx
<Box borderWidth="1px">
  ...
  <Button float="left">戻る</Button>
</Box>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3909033/85a97b50-2231-4ea1-b792-7636d1955c61.png)

## 🔍 原因

`float="left"` を指定すると、**親の `Box` がボタンの高さを認識できず**、結果としてレイアウトが崩れたり、ボタンがボックスの外に飛び出します。

---

## ✅ 解決方法

### ✅ 方法1: `Flex` を使う（おすすめ）

```tsx
<Flex justify="flex-start" mt={4}>
  <Button _hover={{ bg: "blue.400" }}>戻る</Button>
</Flex>
```

- `float` を使わず `Flex` で左寄せ。
- 親コンテナとの整合性も取れる。

---

### ✅ 方法2: `overflow="hidden"` で高さ崩れを防ぐ

```tsx
<Box borderWidth="1px" overflow="hidden">
  <Button float="left">戻る</Button>
</Box>
```

:::danger 非推奨
Chakra UIでは `float` よりも `Flex` / `Stack` を使うのが推奨されます。
:::

---

### ✅ 方法3: 幅を調整して崩れを防ぐ

```tsx
<Button w="fit-content">戻る</Button>
```

- レイアウトによっては、ボタン幅の調整でもはみ出しを防げます。

### 修正結果（自分はFlexを利用しました）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3909033/2a1d5d26-5423-4228-90c3-def9b0af20eb.png)
---

## ✅ まとめ

| 方法         | 安定性 | Chakraらしさ | 備考 |
|--------------|--------|---------------|------|
| Flexを使う    | ◎      | ◎            | 一番おすすめ |
| overflow指定 | △      | △            | 応急処置的 |
| ボタン幅調整 | ○      | ○            | シンプルなケース向き |

---
VSCode Remote-SSHで「UNPROTECTED PRIVATE KEY FILE!」にハマった話【Windows版】

# VSCode Remote-SSHで「UNPROTECTED PRIVATE KEY FILE!」にハマった話【Windows版】

AWS EC2 に VSCode の Remote-SSH で接続しようとした際に発生した、
`UNPROTECTED PRIVATE KEY FILE!` エラーの原因と、その具体的な解決方法を詳細に記録します。

---

## 発生したエラー内容

```text
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions for 'C:\\Users\\<user>\\.ssh\\<key-file>.pem' are too open.
This private key will be ignored.
Load key "...": bad permissions
ec2-user@<EC2グローバルIP>: Permission denied (publickey,gssapi-keyex,gssapi-with-mic)
```

---

## 原因（なぜこのエラーが出るのか？）

SSH（Secure Shell）はセキュリティを非常に重視しており、秘密鍵ファイル（.pem）に第三者がアクセスできる状態だと接続を拒否します。

### SSHがファイルのアクセス権を厳密に管理する理由

- SSHの秘密鍵（private key）は認証情報の一部であり、第三者に見られてしまうと、サーバーへの不正アクセスにつながるリスクがあります。
- そのため、秘密鍵のファイルアクセス権は本人以外には絶対に読み取らせないという厳密なポリシーが設定されています。

### なぜWindowsで起きやすい？

- LinuxやmacOSでは、chmod 400などで容易にアクセス権を制限できますが、Windowsにはそのようなシンプルな方法がなく、通常のプロパティ設定だけでは不十分です。
- 特に日本語やスペースを含むユーザー名やファイルパスの場合、Windowsのアクセス権設定（icacls）がうまく機能しないことがよくあります。
- そのため、明示的にPowerShellやGUIからアクセス制限を設定する必要があります。

---

## 解決手順 (PowerShell)

### 1. `.pem` ファイルを `.ssh` フォルダに移動（推奨）

```powershell
mkdir "$env:USERPROFILE\.ssh" -Force
Move-Item "C:\\Users\\<user>\\Desktop\\<your-folder>\\<key-file>.pem" "$env:USERPROFILE\.ssh\<key-file>.pem"
```

### 2. `icacls` でパーミッションを設定

```powershell
$PemPath = "$env:USERPROFILE\.ssh\<key-file>.pem"

icacls "$PemPath" /inheritance:r
icacls "$PemPath" /remove "Users"
icacls "$PemPath" /remove "Authenticated Users"
icacls "$PemPath" /remove "Everyone"
icacls "$PemPath" /grant "<PC名>\\<ユーザー名>:R"
```

※ `whoami` コマンドで確認できる「PC名\\ユーザー名」を正確に指定してください。

---

## GUIでのアクセス権確認方法

1. `.pem` ファイルを右クリックし、「プロパティ」を開きます。
2. 「セキュリティ」タブを開き、「詳細設定」をクリックします。
3. 「アクセス許可のエントリ」一覧で、ユーザーが自分のみ、権限が「読み取り」または「フルコントロール」になっていることを確認します。
4. 他のユーザーやグループが存在する場合は、それらを削除します。
5. 設定を保存します。

---

### 3. SSH接続テスト

```powershell
ssh -i "$env:USERPROFILE\.ssh\<key-file>.pem" ec2-user@<EC2のIP>
```

`UNPROTECTED PRIVATE KEY FILE!` エラーが出なければ成功です。

---

## VSCode Remote-SSH用 `.ssh/config` 設定（任意だがおすすめ）

```ssh
Host my-ec2
  HostName <EC2のIP>
  User ec2-user
  IdentityFile ~/.ssh/<key-file>.pem
```

### VSCode の接続手順

1. `Ctrl + Shift + P`
2. `Remote-SSH: Connect to Host`
3. `自分のホスト` を選択

---

## おわりに

Windows環境でSSH秘密鍵を使用する場合、アクセス権を明示的に自分だけに設定する必要があります。

- 日本語のユーザー名、スペースを含むファイルパスには特に注意。
- GUI設定だけでは不十分で、`icacls`を使った明示的な設定が必要。

この記事が同様のトラブルに遭遇した方の参考になれば幸いです。
GitHub Pages 画面が真っ白になる問題の解決策の一つ

# はじめに
「GitHub Pages 画面 真っ白」で調べると様々な解決策がでてきますが、そのどれを試してもうまくいきませんでした。
しかし、色々調べて以下の設定にしたところ、無事動いたので、その方法を共有します。

# 環境
- React（typescript）
- Vite
- gh-pages

# 解決策
以下を、package.json の deploy の値に追加すると動きました。
```
--repo https://github.com/<ユーザ名>/<リポジトリ名>.git
```
実際の形は以下。
```package.json
  // 省略
  "homepage": "https://<ユーザ名>.github.io/<リポジトリ名>/",
  "scripts": {
    // 省略
    "deploy": "npm run build && gh-pages -d dist --repo https://github.com/<ユーザ名>/<リポジトリ名>.git"
  },
```

他のファイルに設定していた内容は以下になります。
```vite.config.ts
export default defineConfig({
  plugins: [react()],
  base: '/<リポジトリ名>/',
})
```
```index.tsx
// ルーター部分
function AppRoutes() {
  return (
    <>
      <BrowserRouter basename='/<リポジトリ名>'>
        <Routes>
          <Route path='/' element={<Main />} />
          // 省略
        </Routes>
      </BrowserRouter>
    </>
  );
}
```
後は、gh-pagesブランチを作り、GitHub上でPagesの設定を行います。
この設定で`npm run deploy`を行なうと、画面が適切に表示されるようになりました。

以上です。
Flow Collections and Loops Superbadge Unit



https://trailhead.salesforce.com/content/learn/superbadges/superbadge_collections_loops_sbu

# ユーザーインタラクション用のレコードを収集して表示するフローを作成する


データ テーブルを構成して、ユーザー操作用に商談レコードをアルファベット順に表示します。データ テーブルに表示されるレコードをユーザーがフィルター処理できるようにします。

何もしないで検証ボタンをクリックする

:::note alert
We can't find the 'Opportunity Review' flow.
:::

WEBで訳してもまともや意味不明。
実際の設定と関係ないことを書いてあって、訳すと何をしたらいいかがはっきりしない。
仕方ないのエラーメッセージを手掛かりにするしか無い。

はっ？

:::note alert
The 'Opportunity Review' flow doesn't provide a way to sort opportunities.

「商談レビュー」フローでは、商談を並べ替える方法は提供されません。
:::

:::note alert
We can't find the expected Choice resource that allows the users to view all
:::

:::note alert
The 'Opportunity Review' flow should display filtered records in a Data Table component.

「商談レビュー」フローでは、データ テーブル コンポーネントにフィルターされたレコードが表示されます。
:::

:::note alert
We can't find the Decision element that evaluates if the users selected a stage to filter on.


ユーザーがフィルタリングするステージを選択したかどうかを評価する決定要素が見つかりません。
:::

いつも自動フォーマットでやるんでループがうまくいかないですね。フリーに変えるか


# ユーザーの変更を収集して保存するための画面フローを構成する


ユーザーが画面で行った変更に基づいて機会を更新するための追加構成を完了します。最高のパフォーマンスと効率を得るためにフローを最適化します。 


何もしないで検証ボタンをクリックする

:::note alert
The 'Opportunity Updater' flow isn't properly setting the updated values for changed fields in the loop.


「Opportunity Updater」フローは、ループ内の変更されたフィールドの更新された値を適切に設定していません。
:::
Atcoder 備忘録_一次元の累積和_問題B07

# 概要
- 競技プログラミングを始めてみた。
  せっかくなので１テーマごとに解法と学びをアウトプットしてみる。

## 取り組む内容
- 鉄則本[https://atcoder.jp/contests/tessoku-book] に沿って実施する。
- ![鉄則本.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636949/7525f26c-5077-422b-b6b6-cbfab34ef327.jpeg)
- 言語は Python
- 今回テーマは「一次元の累積和」

# 問題文
あるコンビニは時刻0に開店し、時刻Tに閉店します。このコンビニにはN人の従業員が働いており、i番目(1≤i≤N)の従業員は時刻Liに出勤し時刻Riに退勤します。

t=0,1,2,…,T−1それぞれについて、時刻t+0.5にコンビニにいる従業員の数を出力するプログラムを作成してください。

## 制約
- 1≤T≤500000
- 1≤N≤500000
- 0≤Li<Ri≤T(1≤i≤N)入力はすべて整数

## 出力
全体でT行出力してください。
t+1 行目(0≤t≤T−1)には、時刻t+0.5 にコンビニにいる従業員の数を出力してください。

# 解法
```
T = int(input())
N = int(input())

x = [0] * (T+1)

for n in range(N):
  Li, Ri = map(int, input().split())

  x[Li] += 1
  x[Ri] -= 1
  
for i in range(1, T+1):
  x[i] += x[i-1]

for answer in x[:-1]:
  print(answer)
```

# 結果
- AC
- Runtime： 912 ms
- メモリ：32568  KB

# 振り返り
## 思想
- 出勤時間に+1, 退勤時間に-1をそれぞれ立てる
- その後、一次元累積和を計算すると時間ごとの在籍人数となる

## 学び
- リストを累積和するのは以下が便利

```
for i in range(1, T+1):　# +1するかは問題による
  x[i] += x[i-1]
```

## 感想
- ↑の累積和リスト作成は汎用性高いので覚えておきたい
ABC400回答メモ

# 0.はじめに
　記念すべき400回目のコンテスト。
　ABは普通に解けましたが、C以降の難易度が高く
　大ブレーキ。
　久しぶりにCすら解けないという惨憺たる成績でした・・・。
　さすがにレートも急降下し、-26の776と住み慣れた茶色に
　戻ってきました。


# 1. 	A - ABC400 Party
　Nで400を割り切れるならば、400÷Nを表示し
　割り切れない時は-1を表示して終了。

　https://atcoder.jp/contests/abc400/submissions/64503173


# 2.B - Sum of Geometric Series
　変数ansを0で初期化して用意。
　iを0からMまで回し、ansにNのi乗を足していく。
　その後、ansが10の9乗以下ならansを出力し
　ansが10の9乗より大きいなら”inf”を出力して終了

　https://atcoder.jp/contests/abc400/submissions/64511444


# 3.C - 2^a b^2
　Ａ（２のa乗と定義）の最小は２、Ｂ（ｂの2乗と定義）は最小１なので、
　ＡとＢがＮ以下での取りうる値を求め
　それぞれの全組み合わせをsetにぶち込んで
　重複をなくした個数を数えて表示して終了。
　としたらまぁ10の18乗では普通にTLE。
　いろいろ工夫しても解決策が思いつかずコンテスト終了。

　コンテスト後、解説を見てもよくわかりませんでしたが
　翌朝見た時に追加されていた4つ目の解説が理解できました。

　【考え方】
　　１）2のA乗をA、bの2乗をBとした時、N以下の良い整数を満たす条件は
　　　-１）Aの最大値は、B＝１（b＝1）の時
　　　　　→aの最大値は、Nの上限10の18乗の場合でも60以下
　　　-２）Bの最大値は、Ａ＝２（ａ＝1）の時
　　　　　→bの最大値は、Nの上限10の18乗の場合でも10の9乗以下
　　２）条件を満たすＡとＢがあり
　　　　ｂが偶数（奇数掛ける２のｐ乗）の場合、Ａ側に２の（ｐ＋１）乗を掛ければ
　　　　求まる良い整数は同じとなる
　　３）以上から、奇数ｙの2乗に２のｘ乗を掛けた場合にＮ以下となる最大のｘの値が
　　　　奇数ｙの場合の良い整数の個数となる

　【実装】
　　１）Ｎを入力
　　２）x（奇数ｙの時の良い整数の数＝Ｎ以下となる２の乗数の最大）を60で初期化
　　３）ans（Ｎ以下の良い整数の個数）を0で初期化
　　４）ｙ（2乗して２のｘ乗にかけ合わせる奇数）を１で初期化
　　５）以下ｘが1以上の間繰り返し
　　　-1）以下ｘが1以上かつ、ｙの2乗掛ける２のｘ乗がＮより大きい場合から1マイナスする
　　　　　（ｙの2乗にかけることのできる2の最大の乗数を求める為）
　　　-2)ａｎｓにｘを加算
　　　-3)ｙに2を加算（次の帰趨を調べる）
　　６）ａｎｓを出力して終了

　３５０点と高目ではありましたが、今後Ｃの問題がこのレベルになってくると
　なかなか厳しいなと思いました。

　https://atcoder.jp/contests/abc400/submissions/64579830


以上
ウインドウを留める方法…"モニタの端"でワープする現象について

## この記事では
　サイズの異なるマルチモニタ環境でウインドウをドラッグする際に、「モニタ間の"段差"でウインドウが止まらずに隣へワープしてしまう現象」を解決する方法を記します。
　OSはWindows11を想定しています。

 ## 状況
　普段、ノートPC+ディスプレイというマルチモニタ環境で作業しています。ウインドウを左右分割で配置する際に、「ウインドウをドラッグして画面の端にぶつけて離す」といった操作をすることがあります。
 　ノートPCとモニタは左右隣合わせで置いていますが、位置を多少ずらしているため、ウインドウをその段差に当てることで左右分割できていました。
 
　先日Windows11にリプレースしたところ、段差に当てたウインドウがそのまま隣のモニタへワープするようになりました。そこで以前の動作に戻そうとしてウインドウスナップの設定画面を開いたのですが、該当する項目が見当たりません。
　おのれWindows11め…と思わず頭を抱えました。

## 原因
　「ディスプレイの設定」を変更する必要がありました。「ディスプレイ間でカーソルを簡単に移動させる」という項目が有効な場合、カーソルが画面端の段差に当たると（ウインドウドラッグの有無にかかわらず）隣のモニタへワープするようです。

## 解決方法
1. 「設定」>「システム」>「ディスプレイ」を開く
1. [マルチディスプレイ]を展開する
1. 「ディスプレイ間でカーソルを簡単に移動させる」のチェックを外す

## 雑記
 "改悪"を感じる部分が多いWindows11。"アップデート"をもとに戻すため、原因の切り分けと対処する能力が鍛えられる日々です。
　しかし「手のかかる子ほど可愛い」「自分でカスタマイズした環境には愛着が湧く」とも言います。またClipCampや各種QR表示など便利機能も多く実装されています。
　数年後には好きなOSになってるのかもしれないですね。
Gemini 2.5 proはすごいのか

これは「Geminiはすごい。でも、それだけでは足りない。」という考察をAIと壁打ちしてつくったドキュメントです。意図は僕のものですが、ドキュメント自体はAIが語っています。


# 「Gemini 2.5 proはすごい」の違和感
ここ数日、「Gemini 2.5 Proのベンチマークが圧倒的！」という声が溢れている。AIインフルエンサーたちは、「OpenAIは終わった」「Googleの勝利だ」と騒ぎ立て、ある種の祭りのような熱気を生んでいる。

だが、私はその盛り上がりにまったく乗れない。
なぜなら、LLMの「機能的優劣」そのものには、本質的な意味があるように思えないからだ。

# LLMは楽器。演奏されなければ音楽にならない。
私の中にはひとつの前提がある。
技術の価値とは、その体験設計によって初めて立ち上がるものだということ。

LLMは、楽器のようなものだ。
どれだけ鍵盤の数が多かろうと、弦の素材が良かろうと、それだけでは音楽にはならない。
大切なのは、それをどう演奏し、どんな物語を紡ぐかということだ。

それなのに今、多くのAI言説が「ベンチマーク」や「処理速度」といったわかりやすい数値の安心ばかりを根拠に、「最強」「世界一」といった言葉を並べている。
それはまるで、人の心を動かさない情報設計のように映る。

# Googleは、すでに最強のパーツを持っている。
私はGoogleの技術群に、覇権クラスのポテンシャルを確かに感じている。

- Gemini（マルチモーダルLLM）
- NotebookLM（知識ベースのAI支援）
- DeepResearch（探索型AI検索）
- ImageFX / Veo（生成系AI）
- GmailやDocsとのシームレス統合

これらを一つの体験として再設計すれば、OpenAIを本気で脅かすこともできるはずだ。

だが、Googleは“演奏”が下手だ。

Google+、Inbox、Wave、Stadia、Pixel──彼らは優れた素材を作れるが、それを魅せることができない。構造を作る力はあっても、物語をつくる力が弱い。

なぜか？
それは、Googleという企業が「技術で勝つ」ことに最適化されており、「体験で魅せる」文化が育っていないからだ。彼らにとって、UXは往々にして「広告ビジネスの邪魔」であり、調和より分断のほうが都合がいいようにも見える。

# Apple vs Microsoftの再演に近い構造
この構図、どこかで見たことがないだろうか？
- 黎明期：Microsoftが機能と構造で支配した時代
- 成熟期：Appleが体験と意味で市場を広げた時代

そう。かつてのPC業界にあったこの対立構造が、今、AIの世界でも再び浮かび上がろうとしている。まさに、技術中心の時代から、体験中心の時代へと移り変わる転換点に、私たちは立っているのかもしれない。

# AI市場って“成熟”してるの？
ここで一つ、冷静な視点を入れておきたい。
「LLM市場はもう成熟期に入った」とする言説にエビデンスは乏しい。

マーケティング理論に照らせば、今のAI市場はまだ「アーリーアダプター」段階であり、アーリーマジョリティには届いていない。むしろ、今は“キャズム（溝）”の手前だ。

今、求められているのは、「機能の高さ」ではなく、「橋の設計」ではないだろうか。
アーリーアダプターからマジョリティへと橋渡しできるUXやナラティブこそが、これからの差を決定づける軸になると見る。

# OpenAIは“橋を設計しはじめている”という点で有利
Googleは“技術はあるが橋を描けていない”という点で分が悪い。

OpenAIが前に出しているのは、必ずしも技術力ではない。
優れているのは「語りの上手さ」「体験の魅せ方」「ユーザーの不安を先回りした設計力」だと感じる。

- ChatGPTのUIやメモリ設計
- GPTsやプロンプト共有の導線
- ChatGPT TeamやEnterpriseでの“入りやすさ”

つまり、体験を“橋”として設計できている。
果たしてGoogleにその準備はあるのだろうか。


# キャズムに橋を架けた者が次の世界をつくる
どんなに性能が高くても、人間の行動を変えられなければ、それはただの無音の楽器だ。

OpenAIは、そのための橋を架けはじめている。
Googleは、橋の材料は持っていても、構造図を持ってない。

私にはそのように見える。
さて、橋を架けるのはどちらか。

---
たしかにGemini 2.5 proはすごい。
でも、それだけでは足りない。
ExcelVBA　メモ


# セル値・配列の一括入力・出力

出力
https://daitaideit.com/vba-array-resize/


入力
```
Dim aryTmp As Variant
Dim ary1 As Variant
Dim lastRow As Long

lastRow = ws01Input.Cells(Rows.Count, 1).End(xlUp).Row
aryTmp = ws01Input.Range(ws01Input.Cells(2, 1), ws01Input.Cells(lastRow, 1)).Value  
    
'2次元配列→1次元配列へ変換  
'...aryTmp(1,1) , aryTmp(2,1) のように2次元配列として入ってしまうので
'   transposeで1次元配列 aryInType(1), aryInType(2)に置き換えする
'
 ary1 = WorksheetFunction.Transpose(aryTmp)
    
```



# シートの存在確認

関数名　func_sheetExist
引数  
  in_wb  workbook型
  in_wsname string型
返り値　integer型
　指定された in_wsname のシートが存在するか
　存在する場合　返り値=1　　存在しない場合 0

```
方法A
Function func_sheetExist(in_wb As Workbook, in_wsname As String) As Integer
    Dim res As Integer
    res = 0
    
    Dim ws As Worksheet

    On Error Resume Next
    Set ws = Worksheets(in_wsname)
    On Error GoTo 0
        
    If Not ws Is Nothing Then
        res = 1
    End If
    
    func_sheetExist = res
End Function
```

```
方法B
Function func_sheetExist(in_wb As Workbook, in_wsname As String) As Integer
    Dim ws As Worksheet
    Dim exists As Integer
    exists = 0
    
    For Each ws In in_wb.Worksheets
        If ws.Name = in_wsname Then
            exists = 1
            Exit For
        End If
    Next ws

    func_sheetExist = exists
End Function
```

# オートフィルターで3項目以上のフィルターを掛ける (～を含む）

```
Sub sub_filterTest()


Dim ccArray(2) As String

ccArray(0) = "A"
ccArray(1) = "B"
ccArray(2) = "C"

Sheet1.Range("A1:C12").AutoFilter field:=2, Criteria1:=ccArray, Operator:=xlFilterValues

```

# オートフィルターで3項目以上のフィルターを掛ける (～から始まる）

フィルターで3項目以上のあいまい検索できないため、事前にループで検索し完全セル値でフィルタする

```




Sub sub_filterTest2()

Dim aryTmp As Variant
Dim ary1 As Variant

Dim aryS1 As Variant
ReDim aryS1(1)

Dim aryS2 As Variant
Dim lastRow As Long
Dim tmpVal As Variant

Dim ws As Worksheet
Set ws = ActiveSheet

Dim cnt As Long
Dim i As Long


If ActiveSheet.AutoFilterMode Then ActiveSheet.AutoFilterMode = False

ReDim aryS1(1 To 3)
aryS1(1) = "A*"
aryS1(2) = "B*"
aryS1(3) = "C*"

'フィルターで3項目以上のあいまい検索できないため、ループで検索し完全セル値でフィルタする
ReDim aryS2(0 To 1)
cnt = 0
For i = 1 To lastRow
    tmpVal = Cells(i, 2)
    If tmpVal Like aryS1(1) Or tmpVal Like aryS1(2) Or tmpVal Like aryS1(3) Then
        ReDim Preserve aryS2(cnt)
        aryS2(cnt) = tmpVal
        cnt = cnt + 1
    End If
    
Next

Sheet1.Range("A1:C15").AutoFilter field:=2, Criteria1:=aryS2, Operator:=xlFilterValues

MsgBox "完了しました"


End Sub

```
OpenWrt + Prometheus + Grafana でルーター監視 (Lua版 Node Exporter)


確実に忘れるのでメモ

## 0. 動機

- ネットワークが遅い
- Grafanaを使用したかった

## 1. 構成

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3389433/893d9c10-2387-4b6e-8a25-880894914a6c.png" width=70% />

|サーバ|OS|アドレス|役割|
|-----|--|--------|---|
|ルータ|OpenWrt|192.168.8.1|監視対象|
|監視サーバ|Ubuntu 22.04.5 LTS|192.168.8.20|メトリクス配置|
|クライアント|Windows 11| 192.168.8.10 | ブラウザ | 

## 2. 流れ

- OpenWrt に `prometheus-node-exporter-lua` をインストール
- Prometheus と Grafana を用いてルーターの監視ダッシュボードを作成

## 3. `prometheus-node-exporter-lua` のインストール

ルータで実施する。

### OpenWrt に Lua版 Node Exporter をインストール

`prometheus-node-exporter-lua` は、OpenWrt のような組み込みシステム向けの軽量な Prometheus エクスポーター。

```sh
sudo opkg update
sudo opkg install prometheus-node-exporter-lua
```

### ローカルホストのバインド解除

`prometheus-node-exporter-lua` はデフォルトで`127.0.0.1`にバインドして動作するため、外部(`192.168.8.20`)からアクセスできない。

そのため、バインドアドレスを`uci`を使用して変更する（lanインターフェース: `192.168.8.1`）。

```sh
sudo uci set prometheus-node-exporter-lua.main.liten_interface='lan'

# 設定を保存
sudo uci commit prometheus-node-exporter-lua

# 再起動
sudo /etc/init.d/prometheus-node-exporter-lua restart
```

監視サーバからアクセスして確認。

```sh
curl http://192.168.8.1:9100/metrics
```

## 4. Prometheus のインストール

監視サーバで実施する。

### Ubuntu serevr に Prometheus をインストールする

```bash
sudo apt update
sudo apt install prometheus
```

### 設定ファイル作成

`/etc/prometheus/prometheus.yml`を編集する。

`job_name` は Grafana ダッシュボードでjobを指定するときの名前になる。
`targets` に監視対象の`prometheus-node-exporter-lua`のアドレスとポートを記載。

```yaml
scrape_configs:
  - job_name: 'router1'
    static_configs:
      - targets: ['192.168.8.1:9100']
```

サービス等再起動

```bash
sudo systemctl daemon-reload

sudo systemctl restart prometheus

# 確認
sudo systemctl status prometheus
```

## 5. Grafana のインストール

監視サーバで実施する。

### Grafana のインストール

```bash
# gpgキー取得
wget -q -O /usr/share/keyrings/grafana.gpg https://packages.grafana.com/gpg.key

# リポジトリ登録
echo "deb [signed-by=/usr/share/keyrings/grafana.gpg] https://packages.grafana.com/oss/deb stable main" | sudo tee /etc/apt/sources.list.d/grafana.list

# パッケージを更新してインストール
sudo apt update
sudo apt install grafana

# サービス起動
sudo systemctl daemon-reload
sudo enable grafana-server
sudo systemctl start grafana-server
```

## 6. Grafana ダッシュボード作成

クライアントで実施する。

### ダッシュボード作成

`192.168.8.20:3000`にアクセスし、以下の情報でログインすると任意のパスワードでパスワード変更を求められるので実施。

- user: admin
- password: admin


### データソース設定

サイドバーの`Connections/Data sources`から Prometheus を選択し、`localhost:9090`を入力。

### Grafana ダッシュボードのインポート

以下のダッシュボードをインポートする。

- Node Exporter Full（Grafana ダッシュボードID: 1860）

Jobをprometheusで設定した名前に変更して、以下のような画面が出れば成功

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3389433/51076a4a-b1a3-47b8-895d-9c6c2ab9d122.png" />



## 補足

繋がらない場合はファイヤーウォール等でアクセスできていないこと考える。
【備忘録】macでpythonを書く

# この記事について
macの初期設定を終えた直後の状態から、vscodeでpythonを書いて実行できるようになるところまでを、超おおざっぱに説明する。
個々の項目について詳しく知りたいなら、ググる。
# 1. ターミナルを開く
:::note
ターミナルとは... コマンドでmacを操作できるツール
:::
`command` + `スペース`でspotlightを開き、『ターミナル』で検索する

```
ここのコマンドをターミナルで実行する。
```
# 2. homebrewをインストールする
:::note
homebrewとは... macのパッケージマネージャ
:::
[homebrewの公式サイト](https://brew.sh/ja/)から下記のようなコマンドをコピぺして実行
```terminal
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
色々と英語で指示されるので、従う。
```terminal
brew --version
```
を実行して、エラーが出なければインストール成功
# 3. VSCodeをインストールする
:::note
vscodeとは... microsoftが提供する無料のテキストエディタ。
:::
早速homebrewを使う。
```terminal
brew install visual-studio-code
```
次に、
```terminal
code
```
を実行。これだけでvscodeがインストールされ、立ち上がる。
日本語化などの諸々を済ませたら、[拡張機能の`Python`](https://marketplace.visualstudio.com/items/?itemName=ms-python.python)をインストールしておく。
# 4. pythonをインストールする
:::note
python... プログラミング言語。あなたがインストールしたいもの。
:::
```terminal
brew install python3
```
ここで、`python`ではなく、`python3`であることに注意する。
```terminal
python3
```
で、対話モードが立ち上がったらインストール成功。
対話モードで色々と遊んでみる。`print(1+1)` `print("hello,python")`
```terminal
exit()
```
で、対話モードは終了する。
# 5. 仮想環境を構築する
:::note
仮想環境とは... とりあえずみんなが使ってるから使う。詳しくはググる
:::
好きな場所に好きな名前でpython用のフォルダを作る。
作り終えたら、
```terminal
cd (python用フォルダのパス)
```
を実行する。
フォルダのパスは、finderでフォルダを選択した状態で`command` + `option` + `C`でコピーできる。

次に、
```terminal
python3 -m venv myenv
```
を実行する。`myenv`の部分は自由に決めていいが、今回は`myenv`という名前とする。
python用フォルダに`myenv`というフォルダが追加されたことを確認したら、
```terminal
. myenv/bin/activate
```
で仮想環境を起動する。
```terminal
deactivate
```
で終了できる。
# 6. vscodeに仮想環境を設定する
仮想環境を起動した状態で、
```
which python3
```
を実行。返ってきた文字列をコピーしておく。

次に、
```
code .
```
で、vscodeが起動するので、
vscodeで、`command` + `shift` + `P`と入力。
コマンドパレットが開くので、`python`と入力し、`Python:Select Interpreter`という項目を探し、クリック。
`インタープリター パスを入力...`をクリック。
先ほどコピーした文字をペーストし、`returnキー`
これでvscodeに仮想環境が設定され、
**全ての準備が完了した。**
# 7. 書いてみる
vscodeにて、拡張子が`py`のファイルにpythonのコードを書き、
`control` + `~`(1キーの左)でvscodeのターミナルを開き、
```terminal
python3 (作ったファイルの名前)
```
を実行。これでpythonのコードの実行できる。
JavaScript ポインターイベントで、マウスとタッチに楽に両対応

# ポインターイベントとは？
JavaScriptで、ドラッグ操作などをPCとモバイルに両対応したいときがあります。私は今まで、`MouseEvent`と`TouchEvent`をどちらも登録していましたが、かなりめんどくさい。
そこで役に立つのが、ポインターイベントです。

# 使い方
ポインターイベントは、マウスイベントと対応するようなイベントが存在します。

(`mousedown`に対して`pointerdown`
`mousemove`に対して`pointermove`
`mouseup`に対して`pointerup`)
例えば、ドラッグの移動量を検出したい場合は、
```javascript
element.addEventListener('pointermove', e => {
  e.movementX // 直前からの移動量X
  e.movementY // 直前からの移動量Y
})
```
このイベント1つで、マウスとタッチに両対応することができます。
リスナーで受け取るイベント`e`は、`MouseEvent`を継承しているので、マウスイベントからの移行は簡単です。
さらに、`movementX`と`movementY`を一発で取れるのはとても便利です。

# 追記
Mozillaのドキュメントを見ると
>:::note alert
>警告: ブラウザーは`movementX`と`screenX`に 仕様で定義されているものとは異なる単位を使用します。ブラウザーとオペレーティングシステムによって、`movementX`の単位は物理ピクセルであったり、論理ピクセルであったり、 CSS ピクセルであったりします。
>:::
と書いてあります...。これはマズいやつや。

具体的なブラウザごとの使用単位は、
```
Edge:    物理ピクセル
Chrome:  物理ピクセル
Firefox: CSSピクセル
Safari:  論理ピクセル
```
とのこと。これでは、想定していた移動量とスケールがズレてしまいそう。これは使えない...。
やはり、直前の座標を保存して、移動量を自分で算出するのがベストなようです。

### 気をつける所
ポインターイベントは、それぞれのポインターに対して発火します。
例えば、3本指でドラッグした時は、`pointermove`が3回ダブってしまいます。
モバイルで個別の対応が必要になることもあるかもしれません。

# ブラウザの対応状況
主要ブラウザは、2020年には全て対応したみたいです。安心して使えそうです。

# 参考

https://developer.mozilla.org/ja/docs/Web/API/Pointer_events
「不等式が２つある三角不等式(triangle inequality)を教えて下さい。」をAI先生に聞きました。

# ChatGPT先生へ
# Gemini先生へ
#### 質問リスト
・質問リストの追番は無視して下さい。１個ずつです。途中の回答表示は省略しています。
```
質問リスト
1.不等式が２つある三角不等式を教えて下さい。
2.上記をsympyのソースコードで教えて下さい。
3.質問リストに、最後の質問も含めて下さい。
4.１つ前も
5.もう一つ
6.改めて、私の質問をMarkdown形式でリスト化して出力して。Markdown形式でリストからアスタリスクは削除して。 1行目に、# 質問リスト の文字を出力して。追番を付けて下さい。
```
>補足
SymPyは、数式処理のための強力なライブラリですが、不等式の自動証明に関してはまだ発展途上の部分があります。三角不等式のような基本的な不等式であっても、SymPyだけで完全に自動的に証明することは難しい場合があります。

```python
from sympy import *

x = 3
y = -2

# 三角不等式の左辺
left1 = abs(x) - abs(y)
left2 = abs(x + y)

# 三角不等式の右辺
right = abs(x) + abs(y)

# 不等式の検証
print(left1 <= left2)  # True
print(left2 <= right)  # True
```

# copilot先生へ

# いつもの? sympyの実行環境と 参考のおすすめです。
[(テンプレート)](https://qiita.com/mrrclb48z/items/00dd08b0317069be9342)

https://qiita.com/mrrclb48z/items/b6351b48e47bcf6265c0

# いつもと違うおすすめです。
### wikipedia

https://en.wikipedia.org/wiki/Triangle_inequality

https://ja.wikipedia.org/wiki/三角不等式
TypeScriptのデータ型について 〜オブジェクト型〜

### はじめに

こちらは「TypeScriptのデータ型について 〜プリミティブ型〜」の続きです。
(記事リンク：https://qiita.com/I_D_EEE/items/73653a526fed213d004b)
この記事は、プログラミング初学者が学んだ内容をアウトプットする場として書いています。
他の方にとっては当たり前の内容かもしれませんが、同じように学び始めた方の参考になれば幸いです。

なお、記事内容に誤りがあれば、ぜひコメントで指摘していただけると嬉しいです。よろしくお願いします！


## オブジェクト型

オブジェクト型は複数の値をまとめて扱うための型です。
プリミティブ型と同様に、実際に使ったことのないもの、あるいは理解が及んでいないもの（存在を知っているだけのもの）もあることはご容赦ください。

### object型

オブジェクトリテラルを表す型です。

```typescript
let person: object = { name: "山田太郎", age: 25 };
```

通常はより具体的な型（インターフェースや型エイリアス）を使用します。

```typescript
interface Person {
  name: string;
  age: number;
}

let person: Person = { name: "山田太郎", age: 25 };
```

### array型

同じ型の値を複数持つ配列を表します。2つの記法があります。

```typescript
let numbers: number[] = [1, 2, 3, 4, 5];
// または
let strings: Array<string> = ["りんご", "バナナ", "みかん"];
```

### tuple型

固定長の配列で、各要素の型が決まっている型です。

```typescript
let person: [string, number] = ["山田太郎", 25];
```

### enum型

名前付きの定数セットを定義できる型です。

```typescript
enum Color {
  Red,
  Green,
  Blue
}

let c: Color = Color.Green; // 1
```

デフォルトでは0から始まる数値が割り当てられますが、明示的に値を指定することもできます。

```typescript
enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT"
}
```

## 特殊な型について

### any型

どんな型の値も許容する型です。TypeScriptの型チェックを無効にするため、本番環境では極力使用を避けた方が良さそうです。

```typescript
let value: any = "こんにちは";
value = 123; // OK
value = true; // OK
```

### unknown型

`any`型と似ていますが、より安全です。値を使用する前に型チェックが必要です。

```typescript
let value: unknown = "こんにちは";

// 使用前に型チェックが必要
if (typeof value === "string") {
  console.log(value.toUpperCase());
}
```

### void型

関数が値を返さないことを表す型です。

```typescript
function greet(): void {
  console.log("こんにちは");
}
```

### never型

決して発生しない値の型です。
例外をスローする関数（必ずエラーを発生させる関数）や、無限ループを持つ関数の戻り値型として使用されます。

```typescript
function error(message: string): never {
  throw new Error(message);
}
```

## 複合型

### ユニオン型（Union Types）

複数の型のいずれかを持つことができる型です。

```typescript
let id: string | number;
id = "abc123"; // OK
id = 123;      // OK
```

### インターセクション型（Intersection Types）

複数の型を組み合わせた型です。

```typescript
interface Name {
  name: string;
}

interface Age {
  age: number;
}

type Person = Name & Age;

const person: Person = {
  name: "山田太郎",
  age: 25
};
```


## まとめ

TypeScriptのデータ型は、プログラムの安全性と可読性を高める重要な機能です。
プリミティブ型とオブジェクト型の違いを理解し、適切な型を選択することで、より堅牢なコードを書けるようになります。（そうなれるように願ってます）


## 参考資料

- [TypeScript公式ドキュメント](https://www.typescriptlang.org/docs/)
- [TypeScript Deep Dive日本語版](https://typescript-jp.gitbook.io/deep-dive/)
MySQLをインストールしようとしたらエラーになった原因と解決 初心者メモ

# 状況
EC2上にApacheをインストールし終えたので次にMySQLをインストールしたい。

# エラー
```
sudo yum install -y mysql-community-server
Last metadata expiration check: 0:04:50 ago on Sun Apr  6 00:00:00 2025.
Error:
 Problem: conflicting requests
```

# 原因
EC2のインスタンスがAmazon Linux2023であった

# 解決策1
EC2インスタンスをAmazon Linux2で作り直す。
もっとも簡単でLinux2を前提にした情報が多いため現状ではこちらがおすすめかも。

# 解決策2
Amazon Linux 2023でもMySQLを使うことは可能ですが、Amazon Linux 2までのように簡単にはいかず、MySQL公式リポジトリの追加が必要です。

もしPostgreSQLでも問題ない場合は、Amazon Linux 2023ではPostgreSQLの方がインストールを行います（dnf install postgresql-server でOK）


# 解決策3
MySQL公式リポジトリを追加
```
sudo dnf install -y https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm
```
リポジトリを有効にして確認
```
sudo dnf repolist enabled | grep mysql

```
mySQLをインストール
```
sudo dnf install -y mysql-community-server

```
Next.js個人備忘録④：Next.jsでTypeScriptとJavaScript、どちらを使うか迷ったため、記事にまとめてみた

## はじめに
Next.jsでは、TypeScriptとJavaScriptのどちらでも開発可能です。

:::note info
**個人の備忘録程度の走り書きとなっておりますが、温かい目で見守っていただければ幸いです。**
:::

本記事では、ローカル開発の観点も含め、それぞれの使い分けについて整理しておきます。

## 書こうと思ったきっかけ
受講しているITスクールでNext.jsの技術選定についてディスカッションがあり、TypeScriptとJavaScriptの使い分けについて悩んだため、整理を兼ねてまとめておきたいと思いました。

## TypeScriptをおすすめする理由
- 型安全でバグを未然に防げる
- コード補完やリファクタリングが強力
- Next.js公式がTypeScriptを完全サポート
- 多くのライブラリが型定義付きで利用可能

## JavaScriptでもOKなケース
- Next.jsの初学者でまずは雰囲気を掴みたい
- 個人の小規模プロジェクト
- 学習コストを下げてすぐ動かしたい時

## ローカル開発でのおすすめ
| 開発スタイル | TypeScript | JavaScript |
|---------------|------------|-------------|
| 本格的に作りたい | ✅ おすすめ | ❌ |
| 試作目的 | ◯ できれば | ✅ OK |
| 長期運用前提 | ✅ 強く推奨 | ❌ |
| まず触りたい | ◯ 徐々に慣れる | ✅ OK |

TypeScriptで始めると保守や拡張に強くなります。初学者はJavaScriptから始めて、慣れてきたらTypeScriptに移行するのも良い選択です。

## まとめ
Next.jsでは、しっかりと作り込みたいアプリケーションやチーム開発ではTypeScriptが強く推奨されます。

:::note warn
***一方で、手軽に始めたい、まずは雰囲気を掴みたいという場合にはJavaScriptも有効です。***
:::

最終的にはプロジェクトの規模や目的、メンバーのスキルセットに応じて柔軟に選択していくのがベストです！
TypeScriptのデータ型について 〜プリミティブ型〜


### はじめに

私はプログラミング初学者です。
この記事は、自分が学んだ内容をアウトプットする場として書いています。
他の方にとっては当たり前の内容かもしれませんが、同じように学び始めた方の参考になれば幸いです。

なお、記事内容に誤りがあれば、コメントでご指摘いただけると嬉しいです。どうぞよろしくお願いします！



## TypeScriptのデータ型とは

TypeScriptはJavaScriptに**型**の概念を追加した言語です。型を使うことで、開発時のミスを減らしたり、コードの可読性を高めたりすることができます。

TypeScriptのデータ型は大きく分けて以下の2種類があります：

1. プリミティブ型（基本型）
2. オブジェクト型

今回はプリミティブ型について、学んだ内容をまとめてみます。
実際に使ったことのないもの、あるいは理解が及んでいないもの（存在を知っているだけのもの）もあることはご容赦ください。


## プリミティブ型（基本型）

プリミティブ型は、JavaScript/TypeScriptにおける最も基本的なデータ型です。これらは単一の値を表します。

### string型

文字列を表す型です。シングルクォート（'）、ダブルクォート（"）、またはバッククォート（`）で囲んで表現します。

```typescript
let name: string = "山田太郎";
let greeting: string = '今日は晴れですね';
let message: string = `こんにちは、${name}さん`; // テンプレートリテラル
```

### number型

数値を表す型です。整数、浮動小数点数、2進数、8進数、16進数などを表現できます。

```typescript
let age: number = 25;
let height: number = 175.5;
let binary: number = 0b1010; // 2進数（10）
let octal: number = 0o744;   // 8進数（484）
let hex: number = 0xf00d;    // 16進数（61453）
```

### boolean型

`true`または`false`の論理値を表す型です。

```typescript
let isActive: boolean = true;
let hasPermission: boolean = false;
```

### null型とundefined型

`null`と`undefined`はそれぞれ独自の型を持ちます。

```typescript
let n: null = null;
let u: undefined = undefined;
```

通常、これらは他の型と組み合わせて使用します（別記事で後述予定のユニオン型など）。

### symbol型

ES6から導入された、一意で不変の値を表す型です。

```typescript
let sym1: symbol = Symbol();
let sym2: symbol = Symbol('key'); // 説明付きのシンボル
```

### bigint型

ES2020から導入された、任意精度の整数を表す型です。

```typescript
let bigNumber: bigint = 9007199254740991n;
```

続きは以下へ
「TypeScriptのデータ型について 〜オブジェクト型〜」
(記事リンク：https://qiita.com/I_D_EEE/items/b86d6a72994ceadff2d3)
「一次分数変換(linear fractional transformation)とは何ですか。」をAI先生に聞きました。

# ChatGPT先生へ
```
一次分数変換について教えて下さい。
wikipediaの一次分数変換の記事に日本の文献がありませんでした。
日本の文献を教えて下さい。
一次分数変換を研究しておられるのは、数研出版様だけですか。
https://ja.m.wikipedia.org/wiki/一次分数変換#参考文献
```
(回答抜粋)
ご指摘のとおり、ウィキペディアの「一次分数変換」記事の参考文献には日本語の文献が含まれていません。​しかし、...
・「一次分数変換を利用した連立代数方程式の高精度計算法」（1996年）
・「連立代数方程式の数値解法における一次分数変換の新たな適用について」（1998年）

https://ipsj.ixsq.nii.ac.jp/records/134100?utm_source=chatgpt.com

例えば、大阪教育大学や佐賀大学のリポジトリ...

https://osaka-kyoiku.repo.nii.ac.jp/records/2091637?utm_source=chatgpt.com



# Gemini先生へ
#### 質問リスト
・質問リストの追番は無視して下さい。１個ずつです。途中の回答表示は省略しています。
```
質問リスト
1.一次分数変換とは何ですか。
2.上記をsympyのソースコードで教えて下さい。結果も一緒に表示して下さい。
3.上記を作図で教えて下さい。
```

```python
from sympy import symbols, simplify

# 変数の定義
z, a, b, c, d = symbols('z a b c d')

# 一次分数変換の定義
def linear_fractional_transform(z, a, b, c, d):
  return (a*z + b) / (c*z + d)

# 例1: 平行移動
w1 = linear_fractional_transform(z, 1, 1, 0, 1)
print("例1 (平行移動): w = ", simplify(w1))

# 例2: 拡大・縮小
w2 = linear_fractional_transform(z, 2, 0, 0, 1)
print("例2 (拡大・縮小): w = ", simplify(w2))

# 例3: 反転
w3 = linear_fractional_transform(z, 0, 1, 1, 0)
print("例3 (反転): w = ", simplify(w3))

# 例4: 単位円板から上半平面への変換
w4 = linear_fractional_transform(z, 1, -1, 1, 1)
print("例4 (単位円板から上半平面への変換): w = ", simplify(w4))
```
```
実行結果:
例1 (平行移動): w = z + 1
例2 (拡大・縮小): w = 2*z
例3 (反転): w = 1/z
例4 (単位円板から上半平面への変換): w = (z - 1)/(z + 1)
```
実行結果:
```python
import sympy
import matplotlib.pyplot as plt
import numpy as np

# 変数の定義
z = sympy.symbols('z')

# 一次分数変換の定義
def linear_fractional_transform(z, a, b, c, d):
  return (a*z + b) / (c*z + d)

# 変換する図形の定義 (例: 単位円)
theta = np.linspace(0, 2*np.pi, 100)
z_vals = np.exp(1j*theta)

# 変換のパラメータ
a, b, c, d = 1, -1, 1, 1

# 変換後の図形を計算
w_func = linear_fractional_transform(z, a, b, c, d)
w_vals = [sympy.lambdify(z, w_func)(val) for val in z_vals]

# 作図
plt.figure(figsize=(8, 4))

plt.subplot(1, 2, 1)
plt.plot(np.real(z_vals), np.imag(z_vals))
plt.title("Original Figure")
plt.xlabel("Real")
plt.ylabel("Imaginary")
plt.axhline(0, color='black', linewidth=0.5)
plt.axvline(0, color='black', linewidth=0.5)
plt.grid(True)
plt.axis('equal')

plt.subplot(1, 2, 2)
plt.plot(np.real(w_vals), np.imag(w_vals))
plt.title("Transformed Figure")
plt.xlabel("Real")
plt.ylabel("Imaginary")
plt.axhline(0, color='black', linewidth=0.5)
plt.axvline(0, color='black', linewidth=0.5)
plt.grid(True)
plt.axis('equal')

plt.tight_layout()
plt.show()
```

# copilot先生へ

# いつもの? sympyの実行環境と 参考のおすすめです。
[(テンプレート)](https://qiita.com/mrrclb48z/items/00dd08b0317069be9342)

https://qiita.com/mrrclb48z/items/b6351b48e47bcf6265c0

# いつもと違うおすすめです。
### wikipedia

https://en.wikipedia.org/wiki/Linear_fractional_transformation

https://ja.wikipedia.org/wiki/一次分数変換
#0096（2025/04/06） リスコフの置換原則（Liskov Substitution Principle: LSP）

# リスコフの置換原則（Liskov Substitution Principle: LSP）

リスコフの置換原則（Liskov Substitution Principle: LSP）は、オブジェクト指向設計におけるSOLID原則の一つであり、Barbara Liskovによって提唱されました。この原則は、「**サブタイプはその親型と置き換えても正しく動作しなければならない**」というものです。この記事では、上級プログラマー向けに、LSPが守られていないケースと守られているケースをPythonおよびTypeScriptで紹介します。

## LSPの本質

リスコフの置換原則は、継承関係にあるクラスの振る舞いに一貫性を求める原則です。サブクラスが親クラスの契約（前提・期待される動作）を破るような実装を持つと、継承は破綻し、バグや保守性の低下につながります。

---

## Pythonによる例

### ❌ LSPが破られている例

```python
class Bird:
    def fly(self):
        print("I can fly")

class Ostrich(Bird):
    def fly(self):
        raise NotImplementedError("Ostriches can't fly")

def make_bird_fly(bird: Bird):
    bird.fly()

# 実行時に例外が発生する
make_bird_fly(Ostrich())
```

この例では、`Ostrich` が `Bird` を継承しているにもかかわらず、`fly()` を呼び出すと例外になります。これは **親クラスの契約に違反している**ため、LSP違反です。

### ✅ LSPが守られている例

```python
from abc import ABC, abstractmethod

class Bird(ABC):
    @abstractmethod
    def move(self):
        pass

class FlyingBird(Bird):
    def move(self):
        print("Flying in the sky")

class WalkingBird(Bird):
    def move(self):
        print("Walking on the ground")

class Sparrow(FlyingBird):
    pass

class Ostrich(WalkingBird):
    pass

def move_bird(bird: Bird):
    bird.move()

# 正常に動作する
move_bird(Sparrow())
move_bird(Ostrich())
```

この設計では、`Bird` を抽象クラスにし、動作に応じたサブタイプ（`FlyingBird`, `WalkingBird`）に分割しています。`move()` によって期待される振る舞いは常に一貫しており、LSPを満たしています。

---

## TypeScriptによる例

### ❌ LSPが破られている例

```typescript
class Rectangle {
    constructor(public width: number, public height: number) {}

    setWidth(width: number) {
        this.width = width;
    }

    setHeight(height: number) {
        this.height = height;
    }

    getArea(): number {
        return this.width * this.height;
    }
}

class Square extends Rectangle {
    setWidth(width: number) {
        this.width = width;
        this.height = width;
    }

    setHeight(height: number) {
        this.width = height;
        this.height = height;
    }
}

function render(rect: Rectangle) {
    rect.setWidth(4);
    rect.setHeight(5);
    console.log(rect.getArea()); // 期待される出力: 20
}

// しかし、Squareでは 5 * 5 = 25 となる → LSP違反
render(new Square());
```

この例では、`Square` は `Rectangle` を継承していますが、面積の挙動が異なり、親クラスのインターフェース契約を破っています。

### ✅ LSPが守られている例

```typescript
interface Shape {
    getArea(): number;
}

class Rectangle implements Shape {
    constructor(public width: number, public height: number) {}

    getArea(): number {
        return this.width * this.height;
    }
}

class Square implements Shape {
    constructor(public size: number) {}

    getArea(): number {
        return this.size * this.size;
    }
}

function render(shape: Shape) {
    console.log(shape.getArea());
}

render(new Rectangle(4, 5)); // 20
render(new Square(5));       // 25
```

この例では、`Rectangle` と `Square` は `Shape` インターフェースを実装する別のエンティティとして扱われており、**互いに置換可能ではない前提を回避**してLSPを守っています。

---

## まとめ

リスコフの置換原則は、サブクラスが親クラスと互換性を持つことを保証する設計指針です。上級開発者にとっては、継承ではなく**インターフェースや委譲**を活用して、置換可能性と振る舞いの一貫性を設計する能力が求められます。LSP違反のコードは一見正常に見えても、実行時に不具合を引き起こすため、設計時に注意深く判断することが重要です。
【コルーチン】launchとsuspendの役割を改めて確認する

## コルーチンとは
**コルーチンとは**、軽量なスレッドのようなもので、非同期処理や並行処理を効率的に行うための仕組みです。Kotlinでは、コルーチンを使うことで、複雑な非同期処理をシンプルに書くことができます。

### コルーチンの基本
通常、スレッド（Thread）を使って並行処理を行うと、スレッドの作成や切り替えに多くのリソースが必要になります。しかし、コルーチンは軽量で、スレッドを直接使うことなく並行処理を行います。これにより、パフォーマンスを大きく向上させることができます。

コルーチンは中断と再開ができるため、時間のかかる処理をバックグラウンドで実行し、必要なタイミングで再開することが可能です。

### コルーチンを使う利点
- **軽量:** コルーチンはスレッドに比べて非常に軽量で、数万個のコルーチンを同時に動かすことができます。

- **簡単に非同期処理を記述できる:** コルーチンを使うことで、非同期処理を直感的に書けます。コールバック地獄（ネストしたコールバックの問題）を回避できます。

- **同期的なコードに見える非同期処理:** コルーチン内のコードは通常の同期的なコードのように書けますが、非同期で動作します。

### コルーチンの基本的な使い方
コルーチンを使うには、まず`launch`や`suspend`などのコルーチン関連の関数を使います。以下は、コルーチンの基本的な使い方を示す簡単な例です。

```kotlin
import kotlinx.coroutines.*

fun main() {
    // コルーチンを非同期に実行
    GlobalScope.launch {
        // バックグラウンドで処理を行う
        delay(1000L) // 非同期に1秒間待機
        println("Hello from Coroutine!")
    }

    println("Hello from Main Thread!")
    Thread.sleep(2000L) // メインスレッドが終了する前にコルーチンが実行されるように待機
}
```

1. `GlobalScope.launch`:
- コルーチンを非同期に実行するために使います。この関数が呼ばれると、非同期処理がバックグラウンドで開始されます。

2. `delay(1000L)`:
- コルーチン内で時間がかかる処理（例えばネットワーク通信など）を待つために使います。ここでは、1秒間非同期に待機しています。delayは非同期処理を中断して他の処理を行うことができ、実際にスレッドをブロックすることはありません。

3. `Thread.sleep(2000L)`:
- メインスレッドが終了する前に、コルーチンが実行されるように待つために使っています。Thread.sleepはメインスレッドをブロックしますが、コルーチン内のdelayはスレッドをブロックしません。

### コルーチンの重要な要素

1. `launch`:
- 新しいコルーチンを非同期に開始します。launchは、非同期で実行されるタスクを開始するために使用され、結果を返さない（Unit 型）コルーチンを作成します。

2. `suspend`:
- 非同期処理を行う関数に使うキーワードです。suspend関数はコルーチン内で呼び出す必要があり、実行中に中断して再開できる関数です。例えば、delayやネットワーク通信の関数がsuspend関数です。

3. `GlobalScope`:
- コルーチンのスコープ（実行範囲）を定義します。GlobalScopeは、アプリケーション全体でコルーチンを使う場合に使用されます。実際には、スコープを指定してコルーチンのライフサイクルを管理することが推奨されますが、簡単な例ではGlobalScopeがよく使われます。

### コルーチンを使った非同期処理の例
例えば、ネットワークからデータを非同期で取得する処理をコルーチンを使って書くと、以下のように書けます。

```kotlin
import kotlinx.coroutines.*

suspend fun fetchDataFromNetwork(): String {
    delay(2000L)  // 非同期に2秒間待機（例えば、実際にはネットワークからデータを取得）
    return "Network data"
}

fun main() {
    GlobalScope.launch {
        val data = fetchDataFromNetwork()  // suspend関数をコルーチン内で呼び出す
        println(data)  // "Network data" と表示される
    }
    
    println("Fetching data...")
    Thread.sleep(3000L)  // コルーチンが終了するのを待つため
}
```
このコードでは、`fetchDataFromNetwork`が`suspend`関数として定義され、非同期にデータを取得しています。メインスレッドをブロックせずにバックグラウンドで非同期処理を実行できます。

## まとめ
- **コルーチン**は軽量なスレッドのようなもので、非同期処理や並行処理をシンプルに実行するための仕組みです。

- **launchを使ってコルーチンを非同期に開始し、suspend**を使って非同期の中断・再開を行うことができます。

- コルーチンは、スレッドを直接使わずに非同期処理を効率的に行い、コードを簡潔で直感的に書けるようにします。

コルーチンを使うことで、複雑な非同期処理を簡単に実装でき、アプリケーションのパフォーマンスも向上します。
[Linux][system] パーティションとファイルシステムの作成プロセス_mkswap, swapon, swapoff

# HDDのパーティションとファイルシステムの作成プロセス

```mermaid
graph TD;
    A[物理HDD] -->|1.パーティション作成| B[パーティション]

    subgraph "パーティション作成コマンド"
        B1[MBR: fdisk / GPT: gdisk]
        B2[parted]
    end

    A --> B1
    A --> B2
    B1 --> B
    B2 --> B

    B -->|2.フォーマット| C[ファイルシステム]

    subgraph "フォーマットコマンド"
        C1[mkfs.ext4]
        C2[mkfs.xfs]
        C3[mkfs.vfat]
    end

    B --> C1
    B --> C2
    B --> C3
    C1 --> C
    C2 --> C
    C3 --> C

    C -->|3.マウント| D[使用可能なストレージ]

    subgraph "マウントコマンド"
        D1[mount /etc/fstab]
    end

    C --> D1
    D1 --> D

    B -->|4.スワップ領域作成| E[スワップ領域]

    subgraph "スワップ関連コマンド"
        E1[mkswap]
        E2[swapon]
        E3[swapoff]
    end

    B --> E1
    E1 --> E

    E -->|5.スワップ有効化| F[使用可能なスワップ領域]
    E --> E2
    E2 --> F

    F -->|6.スワップ無効化| G[スワップ無効化]
    F --> E3
    E3 --> G

```

# HDDのパーティションとファイルシステムの作成プロセス詳細
### 1. HDDをパーティション分割
- `fdisk（MBR）`、`gdisk（GPT）`または`parted`を使用  

### 2. ファイルシステムを作成（フォーマット）
- `mkfs.ext4`（Linux用）  
- `mkfs.xfs`（XFS（CentOS等）ファイルシステム用）  
- `mkfs.vfat`（FAT32, USB用）  

### 3. パーティションをマウント
- `mount`コマンドで一時的にマウント  
- `/etc/fstab`を編集し永続的にマウント  

### 4. スワップ領域の作成
- `mkswap /dev/sdX` を使用し、スワップ領域をフォーマット  
- `swapon -s` で現在のスワップ領域を確認  

### 5. スワップ領域の有効化
- `swapon /dev/sdX` でスワップ領域を有効化  
- `/etc/fstab` にエントリを追加して再起動後も有効化  

### 6. スワップ領域の無効化
- `swapoff /dev/sdX` で特定のスワップ領域を無効化  
- `swapoff -a` で全スワップ領域を無効化
flaskで画像ファイル保存をする

flaskでSNSのようなwebアプリ開発をしているのですが、画像保存機能を実装できたのでまとめます。
## 背景
SNSのようなアプリでは画像も一緒に見えた方が文章だけの投稿よりイメージがつきやすいと思ったため、この機能を実装することにしました。
## 機能概要
大まかに以下のように機能を実装すればよいはずです。
1. html
   1-1. ファイルを受け取る
   1-2. バックエンドにファイルを渡す
1. python（バックエンド）
   2-1. ファイルを受け取る
   2-2. ストレージに保存する
## 機能詳細
それぞれの機能を実装していきます。
### 1.html
ファイルの受け取りのためには、formメソッドのenctypeを"multipart/form-data"に設定する必要があります。
```html: index.html
<h1>新規投稿</h1>
<form method="POST" enctype="multipart/form-data">
    <label for="title">タイトル</label>
    <input type="text" name="title">
    <label for="body">内容</label>
    <input type="text" name="body">
    <!-- 1-1.ファイルを受け取る -->
    <label for="file">写真</label>
    <input type="file" name="file">
    <!-- 1-2.ファイルをバックエンドに渡す -->
    <input type="submit" value="投稿">
</form>
```
### 2.python
htmlでenctypeを"multipart/form-data"に設定した場合、ファイルを取得するにはrequest.files['file']とする必要があります。

:::note warn
request.form.get['file']としていたため、バックエンド側でうまくファイルの受け取りができませんでした。
:::
```python: app.py
import os
from werkzeug.utils import secure_filename

# ファイルの拡張子を指定
ALLOWED_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif']

# 画像ファイルの拡張子が指定のものかどうか確認する関数
def allowed_file(filename):
    if '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS:
        return True
    else:
        print('allowed_file: 許可されていないファイル拡張子です')
        return False

# 画像をアップロードするフォルダを指定
UPLOAD_FOLDER = 'uploads'

# リクエストにファイルが含まれる場合にストレージにファイルを
def upload_file(request):
    # 2-1.ファイルを受け取る
    file = request.files['file']
    if file is None:
        return None
    elif file.filename == '':
        return None
    elif file and allowed_file(file.filename):
        filename = secure_filename(file.filename)
        filepath = os.path.join(UPLOAD_FOLDER, filename)
        # 2-2. ストレージに保存する
        file.save(filepath)
        return filepath
    else:
        return None
```
## まとめ
html、pythonの箇所に記載した内容を理解できておらずファイルの受け取りがうまくできませんでした。同じ内容で進めないでいる人の助けになれば幸いです。
DeepSearchの精度改善手法：URL Rankingについて

最近、DeepSearchの精度向上に非常に興味を持ち、さまざまな論文や各社の研究レポートを調べてみました。そんな中、jina.aiという企業が提案する、rerankを参考にした精度向上アプローチがとても興味深かったので、皆さんにも紹介しようと思います。


原文はこちら⇩

https://jina.ai/news/snippet-selection-and-url-ranking-in-deepsearch-deepresearch

# 1. URL Rankingとは

この手法は「URL Ranking」と呼ばれており、DeepSearch特有の課題に対処するための手法となります。
具体的には、LLMが検索をするときに、まずあるキーワードで検索を行い、数十件のウェブページをざっとチェック。その後、各ページ内にあるリンク先もクリックして確認し、また別のキーワードで再検索するというプロセスを繰り返すため、最終的に数百件のページを参照することが通常です。しかし、どのページの情報を参照するかは必ずしも一定ではなく、ランダムに選ばれてしまう場合もあるのです。
この不安定さが、deepsearchの精度にばらつきを生む原因となっていました。

そこで、jina.aiはベクトル検索で用いられるrerank処理を参考に、各URLにもランキングを付与して評価するというアプローチを提案しました。

# 2. ランキング指標

主なランキング基準は以下となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3556459/c0497ea9-46b2-457a-9057-cd24be41ec2d.png)

**1. 最終更新日時（Last Update Time）**
DeepSearchでは、最新情報を参照することが重要ですので、古い情報よりも更新された最新情報を優先するという考え方です。

**2.ドメイン構造（Path Structure）**
URLのドメイン構造を分析することで、内容が類似しているかどうかを判断します。似たドメインが多数存在する場合、その情報は重複している可能性が高く、重要度が下がると考えられます。

**3.ドメイン出现頻度（Frequency Signals）**
特定のURLが頻繁に出現する場合、その情報はより重要で信頼性が高いと見なされます。

**4.セマンティックな関連性（Semantic Relevance）**
質問内容と、URLに付随するテキスト（例：タイトル）との類似度を計算し、どれだけ関連性があるかを評価します。

これらの基準に基づいて、最終的には質の高い上位4件のウェブサイトが参照候補として選ばれます。
jina.aiによれば、この手法の導入でDeepSearchの精度が大幅に向上し、安定性も確保されたとのことです。（ただし、具体的に数値でどのくらい向上したかは特に記載されていません）

調整可能なパラメータは以下となります⇩

```
// Calculate boosted weights
export const rankURLs = (urlItems: SearchSnippet[], options: any = {}, trackers: TrackerContext): any[] => {
  // Default parameters for boosting - can be overridden
  const {
    freqFactor = 0.5,           // How much to boost based on term frequency
    hostnameBoostFactor = 0.5,  // How much to boost based on hostname frequency
    pathBoostFactor = 0.4,      // How much to boost based on path frequency
    decayFactor = 0.8,          // Decay factor for longer paths (0-1)
    jinaRerankFactor = 0.8,     // How much to boost based on Jina reranking
    minBoost = 0,               // Minimum boost score
    maxBoost = 5,                // Maximum boost score cap
    question = '',              // Optional question for Jina reranking
    boostHostnames = [],        // Optional hostnames to boost
  } = options;
```

# 3. 動作確認
jina.aiのDeepSearchはオープンソースで公開されているため、実際に試してみました。
「RAGの精度改善手法教えてください」と質問すると、以下のような回答が返ってきました。結果は概ね良好で、出力も安定しており、複数回試しても参照されるサイトはほぼ同じでした。
（ちなみに、jina.aiは中国人によって設立された企業のため、DeepSearchでは中国語のウェブサイトが優先的に参照される設定になっています。そのため、日本語で質問しても主に中国語のサイトが参照され、結果が生成されました。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3556459/c1aec90e-0b5a-44a1-a002-f8a11a1653ba.png)

最後まとめた結果：

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3556459/7fc37bf7-424b-44be-95ed-3099a0e0f691.png)

興味ある方試してみてください：

https://search.jina.ai/