単純倍率では壊れる「味の複合性」という技術的な壁
レシピの調味料変換で本質的に難しいのは「単一倍率が味の複合性を壊す」点。醤油のように塩味と甘味を同時に持つ調味料を単純に0.8倍すると、塩味も甘味も同率で減少し、「甘さだけ控えたい」という部分的な味調整が実現できない。
さらにレシピは自由記述が多い。「醤油 大さじ2」「鶏肉 300g」「中火で5分」が同じテキスト内に混在し、調味料の行を正確に判別する前処理が必要になる。単位も大さじ・小さじ・ml・gと多様で、固体と液体では密度変換まで求められる。
この記事では、単純倍率方式と複合カテゴリ方式を比較し、正規表現による行分類、4味次元の変換計算、カテゴリ別端数処理の実装詳細を示す。
候補方式の比較
調味料変換のアプローチとして、以下の2方式を検討した。
| 属性 | 単純倍率方式 | 複合カテゴリ方式 | |------|-------------|-----------------|| | 実装難易度 | 低い | 中〜高 | | 部分調整 | 不可(全体一律) | 可能(味次元ごと) | | エッジケース耐性 | 低い | 高い | | 前処理コスト | 低い | 高い(行分類+単位正規化) | | 計算量 | O(n) | O(n)(定数係数が大きい) |
単純倍率方式(不採用)
全調味料に同一の倍率を掛ける方式。実装はシンプルで計算量もO(n)。しかし醤油を0.7倍すると塩味も甘味も同時に30%減少するため、「甘さだけ控えめ」のような部分的な調整要件に対応できない。加重平均の考え方を取り入れる余地がなく、複合調味料の扱いが根本的に破綻する。
複合カテゴリ方式(採用)
調味料を「塩味・甘味・酸味・脂質」の4次元ベクトルで表現し、各次元に独立した倍率を適用する方式。醤油なら [塩味:0.8, 甘味:0.2, 酸味:0.0, 脂質:0.0] というベクトルを持ち、甘味倍率だけを0.7にすれば塩味を維持したまま甘さを抑えられる。計算量は同じO(n)だが、味寄与比率テーブルと正規表現による行分類の前処理が必要。
なぜ複合カテゴリ方式を選んだか
単純倍率方式はUXの単純さと引き換えに味の部分調整要件を放棄する。特に醤油・みりん・ソースのような複合調味料は日本のレシピで頻出するため、部分調整ができないツールは実用性に欠ける。計算量は両方式ともO(n)で差がなく、前処理コストの増加分もレシピの行数(通常20行以下)を考えると無視できる。エッジケース(極少量表現、ゼロ入力)に対しても、味次元ごとに個別ルールを設けられる複合カテゴリ方式のほうが柔軟に対応できる。
実装の詳細
計算の流れ
- レシピテキストを行単位で正規表現により分類する(調味料行/食材行/動詞行/時間温度行)
- 調味料行をパースして「調味料名」「量」「単位」を抽出する
- 単位正規化を行う(大さじ→15ml、小さじ→5ml、カップ→200ml)
- 抽出した調味料を味次元ベクトルに変換する
- ユーザーの味倍率を各次元に適用し、再合成して最終量を算出する
- カテゴリ別の端数処理ルールを適用して表示形式を決定する
正規表現による行分類と味次元変換
レシピテキストの各行を4段階の優先順位で判定する。
行分類の優先順位:
1. 調味料パターン(最優先)
キーワード: 醤油|砂糖|塩|酢|みりん|バター|マヨネーズ...
量パターン: 数値 + 単位(ml|大さじ|小さじ|g|カップ)
→ 両方にマッチした行を調味料として処理
2. 食材パターン
キーワード: 鶏肉|玉ねぎ|にんじん|豚肉...
→ 変換対象外として保持
3. 動詞パターン
キーワード: 混ぜる|炒める|煮る|焼く...
→ 手順行として保持
4. 時間/温度パターン
キーワード: 数字+分|℃|数字+度
→ 条件行として保持
味次元ベクトルの変換と計算例
入力:
醤油 45ml(味ベクトル: [塩味:0.8, 甘味:0.2, 酸味:0.0, 脂質:0.0])
ユーザー指定: 甘さ控えめモード → 塩味倍率 1.0、甘味倍率 0.7
各次元の変換:
塩味寄与 = 45ml × 0.8 × 1.0 = 36.0ml
甘味寄与 = 45ml × 0.2 × 0.7 = 6.3ml
再合成量 = 36.0 + 6.3 = 42.3ml
単純倍率方式との比較:
単純倍率(全体0.7倍): 45ml × 0.7 = 31.5ml
→ 塩味も30%減少してしまい、味全体が薄くなる
複合カテゴリ方式: 42.3ml
→ 塩味は維持しつつ甘味だけ30%減少
カテゴリ別の端数処理
味の種類ごとに丸め精度を変えることで、味覚上の違和感と表示の実用性を両立する。
端数処理ルール:
酸味: 丸めない(小数第2位まで保持)
→ 酸味は微量の差が味に影響するため高精度
甘味: 5ml未満は0.2mlステップ、0.3ml未満は「極少量」表示
塩味: 0.5ml単位で丸め(塩分管理の観点)
脂質: 1ml単位で丸め(大まかな目安で十分)
密度変換テーブル(固体⇔液体):
塩: 1.2 g/ml
砂糖: 0.6 g/ml
小麦粉: 0.5 g/ml
検証結果
ケース1: 甘さ控えめにした家庭用煮物レシピ
醤油ベースの煮物で甘味だけを抑える典型的なケース。
入力値:
- 醤油: 45ml(塩味80%/甘味20%)
- 砂糖: 15g(甘味100%)→ 密度0.6g/mlで25ml相当
- みりん: 10ml(甘味60%/酸味0%/脂質0%)
- 味倍率: 甘味 × 0.7、他は × 1.0
計算結果:
- 醤油: 36.0 + 6.3 = 42.3ml(塩味維持、甘味30%減)
- 砂糖: 25ml × 0.7 = 17.5ml → 10.5g
- みりん甘味寄与: 10ml × 0.6 × 0.7 = 4.2ml
→ 解釈: 甘味の総寄与が約30%減少し、塩味はほぼ維持される。単純倍率方式では醤油も0.7倍されて31.5mlとなり塩味が30%弱まるが、複合カテゴリ方式では塩味36.0mlを保持できる点が決定的な違い。
ケース2: 酸味を強めたドレッシング
プロ向けのアレンジで酸味だけを強調するケース。
入力値:
- 酢: 20ml(酸味100%)
- レモン汁: 5ml(酸味90%/甘味10%)
- オリーブオイル: 30ml(脂質100%)
- 味倍率: 酸味 × 1.3、他は × 1.0
計算結果:
- 酢: 20ml × 1.3 = 26.0ml(丸めなし)
- レモン汁酸味: 5ml × 0.9 × 1.3 = 5.85ml(丸めなし)
- オリーブオイル: 30ml × 1.0 = 30.0ml(脂質なので変化なし)
→ 解釈: 酸味は丸めない設計のため5.85mlがそのまま反映される。単純倍率方式では脂質(オイル)も同率で増減するリスクがあるが、複合カテゴリ方式ではオイル量は不変。味次元の独立性が効いている。
エッジケース: 極少量とゼロ入力
砂糖0.2g相当を甘味0.5倍にすると0.1gとなるが、甘味の端数処理ルールにより「極少量」と表示する。ユーザーが量を意図的に0で指定した場合は味寄与ベクトルをゼロとして計算し、他成分の割合を再正規化しない(ゼロを尊重する設計)。
よくある質問(FAQ)
Q: レシピの一部だけ変換したい場合はどうするか
特定の行を選択して変換対象に指定できる。行選択後に味倍率を設定すると選択行のみが変換され、他の行はそのまま保持される。
Q: 味寄与比率のテーブルはどう決めているか
各調味料の味寄与比率は既存の食品成分分析と実運用でのフィードバックを組み合わせて設定している。加重平均の線形結合で再合成するため、各次元の比率の合計が1.0であれば理論的整合性が保たれる。
Q: データはサーバーに送信される?
すべてブラウザ内で処理される。入力したレシピや調味料情報がサーバーに送信されることはない。ブラウザを閉じれば入力内容は消える。
Q: 大量のレシピを一括処理する場合の注意点は
一括処理では正規表現の誤分類が累積する可能性がある。事前にサンプル抽出で分類精度を確認し、調味料名の辞書にないキーワードがあればカスタム追加で対応してほしい。
まとめ
複合カテゴリ方式は味を4次元に分解して独立に倍率を適用することで、複合調味料の部分的な味調整を可能にする方式。正規表現による行分類と単位正規化の前処理、カテゴリ別端数処理の組み合わせで実用的な精度を確保している。
対応アプリを試したいときはレシピ味変さんを使ってみて。ランダム化や並び替えで調理手順を変えたい場面では公平な配置係も便利。
不具合や要望があれば、お問い合わせページから気軽に教えてほしい。