前提知識:経済的な選択と分析 · 分業・比較優位・交換
朝食店が価格を1元引き上げたら、収入は増えるでしょうか?答えを急がずに。店主は販売数量が減る可能性もあれば、ちょうど新しいオフィスビルの開業で新規顧客が増える可能性もあります。価格と販売数量の結果だけでは、その背後にある変化を区別できません。本篇では、誰が取引し、価格がどのように計画を調整するかを説明し、次に数量の反応がどの程度大きいかを問います。
以下はすべて架空の教育用市場です。商品品質、所得、取引ルールを一時的に固定し、課税や第三者の影響は考慮しません。その後、これらの条件を一つずつ戻していきます。「価格のみを変更する」ことと「他の条件も変化する」ことを区別することは、需給図と弾性を理解するための共通する前提条件です。
4人の買い手と4人の売り手はどのように選ぶか
各買い手は最大で1食の朝食を購入し、最高支払意思額はそれぞれ12、10、8、6です。4人の独立した売り手はそれぞれ1食分を作ることができ、機会費用は2、4、6、8です。金額は元、数量は食分です。価格5で試してみましょう。4人の買い手全員が買いたいと考えていますが、コストが2と4の2人の売り手しか取引に応じません。お金を持っていても買えない買い手がいます。
ここでは、買い手の評価額が価格より厳密に高く、売り手のコストが価格より厳密に低い必要があります。ちょうど等しい人は無差別であり、以下の表はこの境界を避けています。
| 試された価格 | 買いたく、かつ買える人 | 供給する意思のある人 | この価格のみで取引した場合 |
| 5 | 4 | 2 | 最大2食、買い手間で競争 |
| 7 | 3 | 3 | 3食の取引で双方の計画が整合 |
| 9 | 2 | 4 | 最大2食、一部の売り手に注文なし |
商品が同一で、情報が入手可能であり、価格が変動可能であると仮定します。価格5では、買えなかった高評価額の買い手は価格上昇に同意する可能性があり、元々朝食を作らなかった売り手も参加する可能性があります。価格9では、売れ残った人が注文を獲得するために価格を下げる可能性があります。価格7では、評価額が7より高い3人の買い手と、コストが7より低い3人の売り手が取引でき、さらに1食増えると、コスト8が残り買い手の評価額6を超えてしまいます。
これは価格調整の圧力を説明していますが、現実が毎日この経路をたどることを証明するものではありません。朝食がすでに作られており、店舗がいつでも価格を変更できない場合、短期的な結果は列や在庫残となる可能性があります。均衡とは、モデル内で売買計画が整合することを指し、「全員が満足」したり「分配が公平」であることではありません。お金が少ない人は、朝食を必要としていても、高い貨幣支払意思額を持たない可能性があります。
個人の選択から需要関係へ
次に、価格を低くから高くまで順に試し、各価格での購入者数を記録します。価格がある買い手の最高支払意思額を超えると、その人は退出します。したがって、少数の参加者によって階段状の構造が形成され、自然な滑らかな線にはなりません。供給も同様です。価格がある売り手の機会費用を超えると、その人が参加します。
参加者が十分に多く、差異が十分に細かくなると、これらの階段状の構造を連続的な関係で近似できます。以下に、より大きな朝食市場を想定し、関連する価格帯で簡略化された線形関係を採用します。
需要量 Qd = 120 − 10P
供給量 Qs = 20 + 10P
これは上記の8人から厳密に導き出された方程式ではなく、統計推定でもありません。これは次に共通して使用する教育用モデルです。Pが1元増加し、他の需要条件が変わらない場合、10食分購入が減り、売り手は10食分多く供給しようとする。局所的な近似を、負の価格やあり得ない数量に無制限に外挿しないでください。
両辺を等しくし、120−10P = 20+10P とおくと、P=5、Q=70 が得られます。確認してみましょう:需要は 120−50=70、供給は 20+50=70 です。価格が一時的に4の場合、需要は80、供給は60です。差額20は、すでに売れた数量ではなく、未実現の購買計画です。価格が6の場合、20食分の過剰供給が生じます。
なぜ価格と数量が同時に上昇するのか
オフィスビルの開業により、あらゆる価格で需要が20食分増えたと仮定します。今は Qd=140−10P で、元の供給関係は変わりません。新しい均衡を解くと P=6、Q=80 となります。価格がより高く、取引数量も増えています。これは「他の条件が固定されている場合、価格上昇は需要量を減少させる」という法則を否定するものではありません。私たちは、異なる2つの需要関係上の点を比較しているのです。
一言で区別してみましょう:店主が看板価格を5から6に変更し、顧客の他の条件が変わらない場合、これは元の需要関係に沿って70から60へ移動することです。新規顧客の参加は、あらゆる価格での需要を変化させ、需要関係そのもののシフトです。現実に価格が変わったからといって、自動的に前者の変化しか起こらないと断定しないでください。
図を準備しています
条件を変えて結果を確かめる基準はP=5、Q=70。需要増で6、80、供給減で6、60。一度に一条件を変える。
{"id":"economics-05","title":"条件を変えて結果を確かめる","summary":"基準はP=5、Q=70。需要増で6、80、供給減で6、60。一度に一条件を変える。","height":750,"html":"<div class=\"controls\"><label for=\"scenario\"><span data-i18n=\"scenario\"></span><select id=\"scenario\"><option value=\"0\" selected data-i18n=\"baseline\"></option><option value=\"1\" data-i18n=\"moreDemand\"></option><option value=\"2\" data-i18n=\"lessSupply\"></option></select></label></div><button id=\"reset\" type=\"button\" data-i18n=\"reset\"></button><div id=\"graphic\"></div><div id=\"results\" class=\"results\" role=\"status\" aria-live=\"polite\" data-i18n-label=\"result\"></div><p class=\"scope\" data-i18n=\"scope\"></p>","css":"body{padding:20px}h2{font-size:19px;margin:0 0 14px}.controls{display:grid;gap:16px}label{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px;font-size:15px}select,input{grid-column:1/-1;width:100%;min-width:0;font:inherit}select{padding:10px;background:var(--paper);color:var(--ink);border:1px solid var(--rule);border-radius:6px;white-space:normal}input{accent-color:var(--accent)}button{min-height:44px;margin:16px 0;font:inherit}.plot{display:block;width:100%;max-width:560px;height:auto;margin:auto}.plot text{fill:var(--ink);font-size:14px}.legend{display:flex;gap:12px;flex-wrap:wrap;margin:8px 0;font-size:14px}.legend i{display:inline-block;width:16px;height:3px;margin-right:5px;vertical-align:middle}.results{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-top:16px}.metric{border:1px solid var(--rule);padding:10px;border-radius:6px;min-width:0}.metric span{display:block;font-size:14px;min-height:3.5em;overflow-wrap:anywhere}.metric strong{font-size:22px;font-weight:600}.barrow{margin:14px 0;font-size:15px}.barlabel{display:flex;justify-content:space-between;gap:10px}.bar{height:16px;background:var(--surface);margin-top:5px}.bar i{display:block;height:100%;background:var(--accent)}.stack{display:flex;height:35px;margin:15px 0}.stack span{display:flex;align-items:center;justify-content:center;color:var(--paper);font-size:13px}.scope{font-size:14px;color:var(--muted);margin:14px 0 0;line-height:1.65}@media(max-width:380px){body{padding:14px 10px}.results{gap:6px}.metric{padding:8px}.metric strong{font-size:19px}}.accounts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.account{border:1px solid var(--rule);border-radius:8px;padding:12px;min-width:0}.account h3{font-size:16px;margin:0 0 12px}.account dl{margin:0}.account dt{font-size:13px;overflow-wrap:anywhere}.account dd{margin:2px 0 12px;font-variant-numeric:tabular-nums}.account .changed{font-weight:700;color:var(--accent)}.area-legend{font-size:14px;line-height:1.7}.area-legend span{display:block}.area-legend i{display:inline-block;width:12px;height:12px;margin-right:6px}@media(max-width:480px){.accounts{grid-template-columns:1fr}}","js":"const N=5,DEFAULTS={\"scenario\": 0};\n/* Pure, deterministic teaching models. Values are synthetic, not estimates. */\nfunction economy(n,p){\n if(n===6){const q=p.remaining,P0=10,P1=12,Q0=100;return{metrics:{oldRevenue:P0*Q0,newRevenue:P1*q,retainedGain:(P1-P0)*q,lostRevenue:P0*(Q0-q),elasticity:Math.abs(((q-Q0)/((q+Q0)/2))/((P1-P0)/((P1+P0)/2)))},revenueAreas:{remaining:q,oldPrice:P0,newPrice:P1,oldQuantity:Q0}};}\n if(n===5){const shift=p.scenario===1?20:0,supply=p.scenario===2?-20:0,P=(100+shift-supply)/20,Q=20+supply+10*P;return{metrics:{price:P,quantity:Q},plot:{xmax:140,ymax:14,x:'quantity',y:'price',lines:[{key:'demand',points:[[0,(120+shift)/10],[120+shift,0]]},{key:'supply',points:[[Math.max(0,20+supply),0],[140,(120-supply)/10]]},{key:'base',points:[[0,5],[140,5]],dash:true}],dot:[Q,P]}};}\n if(n===8){const b=p.response,bs=10,t=p.tax,Pb=5+bs*t/(b+bs),Ps=5-b*t/(b+bs),Q=70-b*bs*t/(b+bs);return{metrics:{buyer:Pb,seller:Ps,quantity:Q,revenue:t*Q,loss:.5*t*(70-Q)},plot:{xmax:100,ymax:20,x:'quantity',y:'price',areas:[{key:'revenue',points:[[0,Ps],[Q,Ps],[Q,Pb],[0,Pb]]},{key:'loss',points:[[Q,Ps],[Q,Pb],[70,5]]}],lines:[{key:'demand',points:[[0,5+70/b],[Math.min(100,70+5*b),Math.max(0,5+(70-100)/b)]]},{key:'supply',points:[[20,0],[100,8]]},{key:'base',points:[[0,5],[100,5]],dash:true},{key:'wedge',points:[[Q,Ps],[Q,Pb]]}],dot:[Q,Pb]}};}\n if(n===18){let index=100+p.weight*p.rise/100;return{metrics:{index,inflation:index-100},bars:[{key:'base',v:100},{key:'new',v:index}],max:180};}\n if(n===19){let E=60,U=10,N=30;if(p.scenario===1){E+=5;U-=5;}if(p.scenario===2){U-=5;N+=5;}if(p.scenario===3){U+=5;N-=5;}return{metrics:{employment:E,unemployment:U,participation:100*(E+U)/100,urate:100*U/(E+U),erate:E},stack:[E,U,N],stackKeys:['employment','unemployment','outside']};}\n if(n===20){const step=p.scenario;let reserves=100,loan=0,deposit=80,equity=20,br=0,bd=0;if(step!==0){loan=60;deposit=140;}if(step===2){reserves-=60;deposit-=60;br=60;bd=60;}if(step===3){loan=40;deposit=120;}const vals={reserves,loan,deposit,equity,bankBreserves:br,bankBdeposits:bd,borrowerDeposit:step===2?0:loan,borrowerGoods:step===2?60:0,borrowerDebt:loan,merchantDeposit:bd,merchantGoods:step===2?0:60};const prev=step>1?economy(20,{scenario:1}).accounts:economy20initial();return{metrics:{reserves,loan,deposit,equity,bankBreserves:br,bankBdeposits:bd,assets:reserves+loan,liabilities:deposit+equity},accounts:vals,ledger:[['bankA',['reserves','loan','deposit','equity']],['bankB',['bankBreserves','bankBdeposits']],['borrower',['borrowerDeposit','borrowerGoods','borrowerDebt']],['merchant',['merchantDeposit','merchantGoods']]].map(([title,keys])=>({title,items:keys.map(key=>({key,value:vals[key],delta:vals[key]-prev[key]}))}))};}\n if(n===22){const c=p.consume,shock=p.shock,rounds=Array.from({length:8},(_,k)=>shock*c**k);return{metrics:{total:shock/(1-c),shown:rounds.reduce((a,b)=>a+b,0),multiplier:1/(1-c)},bars:rounds.map((v,k)=>({label:String(k+1),v})),max:100,rounds};}\n if(n===25){const e=p.rate;return{metrics:{importBill:10*e,exportBill:70/e,debtBill:100*e},bills:true};}\n if(n===26){const A=p.tech,s=p.save/100,d=.1,k=(s*A/d)**2,y=A*Math.sqrt(k),trajectory=[];let current=25;for(let t=0;t<=60;t++){const output=A*Math.sqrt(current),investment=s*output,depreciation=d*current;trajectory.push({t,k:current,y:output,consumption:(1-s)*output,investment,depreciation});current+=investment-depreciation;}return{metrics:{capital:k,output:y,consumption:(1-s)*y},trajectory,plot:{xmax:60,ymax:Math.max(10,...trajectory.map(a=>a.y))*1.1,x:'period',y:'perPersonFlow',lines:[{key:'baselineConsumption',points:[[0,7.5],[60,7.5]],dash:true},{key:'outputPath',points:trajectory.map(a=>[a.t,a.y])},{key:'consumptionPath',points:trajectory.map(a=>[a.t,a.consumption])}]}};}\n throw Error('Unknown lesson');\n}\nfunction economy20initial(){return{reserves:100,loan:0,deposit:80,equity:20,bankBreserves:0,bankBdeposits:0,borrowerDeposit:0,borrowerGoods:0,borrowerDebt:0,merchantDeposit:0,merchantGoods:60};}\nif(typeof module!=='undefined')module.exports={economy};\n\nfunction renderTeachingView(r,el){\n if(r.ledger){\n el.innerHTML='<div class=\"accounts\">'+r.ledger.map(a=>'<section class=\"account\"><h3>'+escape(viz.t(a.title))+'</h3><dl>'+a.items.map(v=>`<dt>${escape(viz.t(v.key))}</dt><dd class=\"${v.delta?'changed':''}\">${viz.number(v.value)} (${v.delta>0?'+':''}${viz.number(v.delta)})</dd>`).join('')+'</dl></section>').join('')+'</div><p class=\"scope\">'+escape(viz.t('delta'))+'</p>';\n $('#results').hidden=true;\n }\n if(r.revenueAreas){const a=r.revenueAreas,x=q=>45+q*2.65,y=p=>213-p*14,q=a.remaining;\n let s=`<svg class=\"plot\" viewBox=\"0 0 360 270\" role=\"img\" aria-label=\"${escape(viz.t('figure'))}\"><path d=\"M45 28V213H322\" fill=\"none\" stroke=\"var(--rule)\"/>`;\n const rect=(x0,x1,p0,p1,c)=>`<rect x=\"${x(x0)}\" y=\"${y(p1)}\" width=\"${(x1-x0)*2.65}\" height=\"${(p1-p0)*14}\" fill=\"${c}\" fill-opacity=\".48\" stroke=\"${c}\"/>`;\n s+=rect(0,q,0,10,'#5277ab')+rect(0,q,10,12,'#247756')+rect(q,100,0,10,'#b56838');\n for(const p of[0,10,12])s+=`<text x=\"37\" y=\"${y(p)+4}\" text-anchor=\"end\">${p}</text>`;\n for(const v of[0,50,100])s+=`<text x=\"${x(v)}\" y=\"235\" text-anchor=\"middle\">${v}</text>`;\n s+=`<text x=\"45\" y=\"20\">${escape(viz.t('price'))}</text><text x=\"310\" y=\"261\" text-anchor=\"end\">${escape(viz.t('quantity'))}</text></svg>`;\n el.innerHTML=s+'<div class=\"area-legend\">'+[['retainedBase','#5277ab'],['retainedGain','#247756'],['lostRevenue','#b56838']].map(([k,c])=>`<span><i style=\"background:${c}\"></i>${escape(viz.t(k))}</span>`).join('')+'</div>';\n }\n}\n\nconst $=s=>document.querySelector(s),col=['var(--accent)','var(--second)','#7581ae','#b36b47'];\nconst escape=s=>String(s).replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));\nfunction render(){\n let p={};for(const k of Object.keys(DEFAULTS)){p[k]=Number($('#'+k).value);let o=$('#out-'+k);if(o)o.textContent=viz.number(p[k]);}\n const r=economy(N,p);document.body.dataset.result=JSON.stringify(r.metrics);document.body.dataset.params=JSON.stringify(p);\n $('#results').innerHTML=Object.entries(r.metrics).map(([k,v])=>`<div class=\"metric\"><span>${escape(viz.t(k))}</span><strong data-metric=\"${k}\">${viz.number(v,2)}</strong></div>`).join('');\n const el=$('#graphic');el.replaceChildren();\n if(r.plot){const a=r.plot,x=q=>48+q/a.xmax*278,y=v=>205-v/a.ymax*164;\n let svg=`<svg class=\"plot\" viewBox=\"0 0 360 258\" role=\"img\" aria-label=\"${escape(viz.t('figure'))}\"><path d=\"M48 30V205H333\" fill=\"none\" stroke=\"var(--rule)\"/>`;\n for(const q of [0,a.xmax/2,a.xmax])svg+=`<text x=\"${x(q)}\" y=\"226\" text-anchor=\"middle\">${viz.number(q,1)}</text>`;\n for(const v of [0,a.ymax/2,a.ymax])svg+=`<text x=\"39\" y=\"${y(v)+4}\" text-anchor=\"end\">${viz.number(v,1)}</text>`;\n svg+=`<text x=\"48\" y=\"20\">${escape(viz.t(a.y))}</text><text x=\"326\" y=\"251\" text-anchor=\"end\">${escape(viz.t(a.x))}</text>`;\n (a.areas||[]).forEach((region,j)=>{svg+=`<polygon points=\"${region.points.map(([xx,yy])=>x(xx)+','+y(yy)).join(' ')}\" fill=\"${j===0?'#4c8c9d':'#bd7446'}\" fill-opacity=\".30\"/>`;});\n a.lines.forEach((l,j)=>{svg+=`<polyline fill=\"none\" stroke=\"${col[j]}\" stroke-width=\"${l.key==='wedge'?5:2.5}\" ${l.dash?'stroke-dasharray=\"5 4\"':''} points=\"${l.points.map(([xx,yy])=>x(xx)+','+y(yy)).join(' ')}\"/>`;});\n if(a.dot)svg+=`<circle cx=\"${x(a.dot[0])}\" cy=\"${y(a.dot[1])}\" r=\"5\" fill=\"var(--ink)\"/>`;\n el.innerHTML=svg+'</svg><div class=\"legend\">'+a.lines.map((l,j)=>`<span><i style=\"background:${col[j]}\"></i>${escape(viz.t(l.key))}</span>`).join('')+(a.areas||[]).map((a,j)=>`<span><i style=\"height:10px;background:${j===0?'#4c8c9d':'#bd7446'}\"></i>${escape(viz.t(a.key))}</span>`).join('')+'</div>';\n }else if(r.bars){el.innerHTML=r.bars.map(b=>`<div class=\"barrow\"><div class=\"barlabel\"><span>${escape(b.key?viz.t(b.key):viz.t('round')+' '+b.label)}</span><strong>${viz.number(b.v)}</strong></div><div class=\"bar\"><i style=\"width:${Math.abs(b.v)/Math.max(r.max,...r.bars.map(a=>Math.abs(a.v)))*100}%\"></i></div></div>`).join('')+(N===22?`<p class=\"scope\">${escape(viz.t('negative'))}</p>`:'');\n }else if(r.stack){el.innerHTML='<div class=\"stack\">'+r.stack.map((v,j)=>`<span style=\"width:${v}%;background:${col[j]}\">${v}</span>`).join('')+'</div><div class=\"legend\">'+r.stackKeys.map((k,j)=>`<span><i style=\"background:${col[j]}\"></i>${escape(viz.t(k))}</span>`).join('')+'</div>';}\nrenderTeachingView(r,el);\n}\nfor(const k of Object.keys(DEFAULTS))$('#'+k).addEventListener('input',render);\n$('#reset').addEventListener('click',()=>{for(const[k,v]of Object.entries(DEFAULTS))$('#'+k).value=v;render();});render();\n","audio":false,"strings":{"base":"基準","baseline":"基準のまま","demand":"需要","figure":"現在の条件による図。正確な値は下の結果に示す。","lessSupply":"供給 −20","moreDemand":"需要 +20","price":"単価 P","quantity":"数量 Q","reset":"基準に戻す","result":"計算結果","scenario":"変化を選ぶ","scope":"基準はP=5、Q=70。需要増で6、80、供給減で6、60。一度に一条件を変える。","supply":"供給"}}
「新規顧客の増加」と「供給の減少」の両方が価格を押し上げるかどうかをまず予測し、その後で確認します。供給が20減少すると、新しい均衡は6、60となります。両方の変化で価格の方向は同じですが、数量の方向は逆です。同時に発生した場合、数量の変化は両方の変化の大きさに依存し、「コストが上がったし、需要も旺盛だ」という一言だけでは判断できません。
価格上限も同じモデルで追跡できます。上限が4で有効な制約の場合、買いたい人は80、売りたい人は60です。60食分がすべてマッチしても、依然として買えない人がいます。政策評価では、誰が手に入れられるか、待ち時間がどのくらいか、供給をどのように補うかといった問いを投げかける必要があり、80の需要を実際消費とみなしたり、不足分をそのまま評価としたりしてはいけません。
販売数量が減っても、なぜ収入が増えるのか
価格の方向は明確ですが、「販売数量がどれだけ減るか」が、値上げ後の営業収入を決定します。ここでは、1つの店舗の1回の価格設定比較に焦点を当て、別の店舗レベルの需要設定に明確に切り替えます:元の価格10で100食販売、営業収入1000。新しい価格12。店舗の需要は市場全体の需要とは異なり、顧客が他店へ移動できるかが重要な条件です。
まず推測してみましょう:もし90食売れた場合、元の収入より少なくなるでしょうか?変化を2つの部分に分解すれば、直感で総数を推測する必要はありません。
- 残った90食分は、1食あたり2元多く受け取り、180元多く受け取る。
- 消えた10食分は、元の1食あたり10元の支払いを失い、100元少なく受け取る。
- 両者を差し引くと、収入は純増80;直接確認すると
12×90=1080 となる。
もし残ったのが70食分だけなら、140元多く受け取るが、300元少なく受け取り、純減160となり、収入は840となる。注意:売れなくなった部分は元の価格で評価する。両方の部分に新価格を使うと、変化が重複計算される。
図を準備しています
条件を変えて結果を確かめる価格10→12、元の販売数100。90個残ると増収180、減収100で差引+80。70個では−160。青は残る売上、緑は増収、橙は減収。
{"id":"economics-06","title":"条件を変えて結果を確かめる","summary":"価格10→12、元の販売数100。90個残ると増収180、減収100で差引+80。70個では−160。青は残る売上、緑は増収、橙は減収。","height":750,"html":"<div class=\"controls\"><label for=\"remaining\"><span data-i18n=\"remaining\"></span><output id=\"out-remaining\" for=\"remaining\"></output><input id=\"remaining\" type=\"range\" min=\"60\" max=\"100\" step=\"1\" value=\"90\"></label></div><button id=\"reset\" type=\"button\" data-i18n=\"reset\"></button><div id=\"graphic\"></div><div id=\"results\" class=\"results\" role=\"status\" aria-live=\"polite\" data-i18n-label=\"result\"></div><p class=\"scope\" data-i18n=\"scope\"></p>","css":"body{padding:20px}h2{font-size:19px;margin:0 0 14px}.controls{display:grid;gap:16px}label{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px;font-size:15px}select,input{grid-column:1/-1;width:100%;min-width:0;font:inherit}select{padding:10px;background:var(--paper);color:var(--ink);border:1px solid var(--rule);border-radius:6px;white-space:normal}input{accent-color:var(--accent)}button{min-height:44px;margin:16px 0;font:inherit}.plot{display:block;width:100%;max-width:560px;height:auto;margin:auto}.plot text{fill:var(--ink);font-size:14px}.legend{display:flex;gap:12px;flex-wrap:wrap;margin:8px 0;font-size:14px}.legend i{display:inline-block;width:16px;height:3px;margin-right:5px;vertical-align:middle}.results{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-top:16px}.metric{border:1px solid var(--rule);padding:10px;border-radius:6px;min-width:0}.metric span{display:block;font-size:14px;min-height:3.5em;overflow-wrap:anywhere}.metric strong{font-size:22px;font-weight:600}.barrow{margin:14px 0;font-size:15px}.barlabel{display:flex;justify-content:space-between;gap:10px}.bar{height:16px;background:var(--surface);margin-top:5px}.bar i{display:block;height:100%;background:var(--accent)}.stack{display:flex;height:35px;margin:15px 0}.stack span{display:flex;align-items:center;justify-content:center;color:var(--paper);font-size:13px}.scope{font-size:14px;color:var(--muted);margin:14px 0 0;line-height:1.65}@media(max-width:380px){body{padding:14px 10px}.results{gap:6px}.metric{padding:8px}.metric strong{font-size:19px}}.accounts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.account{border:1px solid var(--rule);border-radius:8px;padding:12px;min-width:0}.account h3{font-size:16px;margin:0 0 12px}.account dl{margin:0}.account dt{font-size:13px;overflow-wrap:anywhere}.account dd{margin:2px 0 12px;font-variant-numeric:tabular-nums}.account .changed{font-weight:700;color:var(--accent)}.area-legend{font-size:14px;line-height:1.7}.area-legend span{display:block}.area-legend i{display:inline-block;width:12px;height:12px;margin-right:6px}@media(max-width:480px){.accounts{grid-template-columns:1fr}}","js":"const N=6,DEFAULTS={\"remaining\": 90};\n/* Pure, deterministic teaching models. Values are synthetic, not estimates. */\nfunction economy(n,p){\n if(n===6){const q=p.remaining,P0=10,P1=12,Q0=100;return{metrics:{oldRevenue:P0*Q0,newRevenue:P1*q,retainedGain:(P1-P0)*q,lostRevenue:P0*(Q0-q),elasticity:Math.abs(((q-Q0)/((q+Q0)/2))/((P1-P0)/((P1+P0)/2)))},revenueAreas:{remaining:q,oldPrice:P0,newPrice:P1,oldQuantity:Q0}};}\n if(n===5){const shift=p.scenario===1?20:0,supply=p.scenario===2?-20:0,P=(100+shift-supply)/20,Q=20+supply+10*P;return{metrics:{price:P,quantity:Q},plot:{xmax:140,ymax:14,x:'quantity',y:'price',lines:[{key:'demand',points:[[0,(120+shift)/10],[120+shift,0]]},{key:'supply',points:[[Math.max(0,20+supply),0],[140,(120-supply)/10]]},{key:'base',points:[[0,5],[140,5]],dash:true}],dot:[Q,P]}};}\n if(n===8){const b=p.response,bs=10,t=p.tax,Pb=5+bs*t/(b+bs),Ps=5-b*t/(b+bs),Q=70-b*bs*t/(b+bs);return{metrics:{buyer:Pb,seller:Ps,quantity:Q,revenue:t*Q,loss:.5*t*(70-Q)},plot:{xmax:100,ymax:20,x:'quantity',y:'price',areas:[{key:'revenue',points:[[0,Ps],[Q,Ps],[Q,Pb],[0,Pb]]},{key:'loss',points:[[Q,Ps],[Q,Pb],[70,5]]}],lines:[{key:'demand',points:[[0,5+70/b],[Math.min(100,70+5*b),Math.max(0,5+(70-100)/b)]]},{key:'supply',points:[[20,0],[100,8]]},{key:'base',points:[[0,5],[100,5]],dash:true},{key:'wedge',points:[[Q,Ps],[Q,Pb]]}],dot:[Q,Pb]}};}\n if(n===18){let index=100+p.weight*p.rise/100;return{metrics:{index,inflation:index-100},bars:[{key:'base',v:100},{key:'new',v:index}],max:180};}\n if(n===19){let E=60,U=10,N=30;if(p.scenario===1){E+=5;U-=5;}if(p.scenario===2){U-=5;N+=5;}if(p.scenario===3){U+=5;N-=5;}return{metrics:{employment:E,unemployment:U,participation:100*(E+U)/100,urate:100*U/(E+U),erate:E},stack:[E,U,N],stackKeys:['employment','unemployment','outside']};}\n if(n===20){const step=p.scenario;let reserves=100,loan=0,deposit=80,equity=20,br=0,bd=0;if(step!==0){loan=60;deposit=140;}if(step===2){reserves-=60;deposit-=60;br=60;bd=60;}if(step===3){loan=40;deposit=120;}const vals={reserves,loan,deposit,equity,bankBreserves:br,bankBdeposits:bd,borrowerDeposit:step===2?0:loan,borrowerGoods:step===2?60:0,borrowerDebt:loan,merchantDeposit:bd,merchantGoods:step===2?0:60};const prev=step>1?economy(20,{scenario:1}).accounts:economy20initial();return{metrics:{reserves,loan,deposit,equity,bankBreserves:br,bankBdeposits:bd,assets:reserves+loan,liabilities:deposit+equity},accounts:vals,ledger:[['bankA',['reserves','loan','deposit','equity']],['bankB',['bankBreserves','bankBdeposits']],['borrower',['borrowerDeposit','borrowerGoods','borrowerDebt']],['merchant',['merchantDeposit','merchantGoods']]].map(([title,keys])=>({title,items:keys.map(key=>({key,value:vals[key],delta:vals[key]-prev[key]}))}))};}\n if(n===22){const c=p.consume,shock=p.shock,rounds=Array.from({length:8},(_,k)=>shock*c**k);return{metrics:{total:shock/(1-c),shown:rounds.reduce((a,b)=>a+b,0),multiplier:1/(1-c)},bars:rounds.map((v,k)=>({label:String(k+1),v})),max:100,rounds};}\n if(n===25){const e=p.rate;return{metrics:{importBill:10*e,exportBill:70/e,debtBill:100*e},bills:true};}\n if(n===26){const A=p.tech,s=p.save/100,d=.1,k=(s*A/d)**2,y=A*Math.sqrt(k),trajectory=[];let current=25;for(let t=0;t<=60;t++){const output=A*Math.sqrt(current),investment=s*output,depreciation=d*current;trajectory.push({t,k:current,y:output,consumption:(1-s)*output,investment,depreciation});current+=investment-depreciation;}return{metrics:{capital:k,output:y,consumption:(1-s)*y},trajectory,plot:{xmax:60,ymax:Math.max(10,...trajectory.map(a=>a.y))*1.1,x:'period',y:'perPersonFlow',lines:[{key:'baselineConsumption',points:[[0,7.5],[60,7.5]],dash:true},{key:'outputPath',points:trajectory.map(a=>[a.t,a.y])},{key:'consumptionPath',points:trajectory.map(a=>[a.t,a.consumption])}]}};}\n throw Error('Unknown lesson');\n}\nfunction economy20initial(){return{reserves:100,loan:0,deposit:80,equity:20,bankBreserves:0,bankBdeposits:0,borrowerDeposit:0,borrowerGoods:0,borrowerDebt:0,merchantDeposit:0,merchantGoods:60};}\nif(typeof module!=='undefined')module.exports={economy};\n\nfunction renderTeachingView(r,el){\n if(r.ledger){\n el.innerHTML='<div class=\"accounts\">'+r.ledger.map(a=>'<section class=\"account\"><h3>'+escape(viz.t(a.title))+'</h3><dl>'+a.items.map(v=>`<dt>${escape(viz.t(v.key))}</dt><dd class=\"${v.delta?'changed':''}\">${viz.number(v.value)} (${v.delta>0?'+':''}${viz.number(v.delta)})</dd>`).join('')+'</dl></section>').join('')+'</div><p class=\"scope\">'+escape(viz.t('delta'))+'</p>';\n $('#results').hidden=true;\n }\n if(r.revenueAreas){const a=r.revenueAreas,x=q=>45+q*2.65,y=p=>213-p*14,q=a.remaining;\n let s=`<svg class=\"plot\" viewBox=\"0 0 360 270\" role=\"img\" aria-label=\"${escape(viz.t('figure'))}\"><path d=\"M45 28V213H322\" fill=\"none\" stroke=\"var(--rule)\"/>`;\n const rect=(x0,x1,p0,p1,c)=>`<rect x=\"${x(x0)}\" y=\"${y(p1)}\" width=\"${(x1-x0)*2.65}\" height=\"${(p1-p0)*14}\" fill=\"${c}\" fill-opacity=\".48\" stroke=\"${c}\"/>`;\n s+=rect(0,q,0,10,'#5277ab')+rect(0,q,10,12,'#247756')+rect(q,100,0,10,'#b56838');\n for(const p of[0,10,12])s+=`<text x=\"37\" y=\"${y(p)+4}\" text-anchor=\"end\">${p}</text>`;\n for(const v of[0,50,100])s+=`<text x=\"${x(v)}\" y=\"235\" text-anchor=\"middle\">${v}</text>`;\n s+=`<text x=\"45\" y=\"20\">${escape(viz.t('price'))}</text><text x=\"310\" y=\"261\" text-anchor=\"end\">${escape(viz.t('quantity'))}</text></svg>`;\n el.innerHTML=s+'<div class=\"area-legend\">'+[['retainedBase','#5277ab'],['retainedGain','#247756'],['lostRevenue','#b56838']].map(([k,c])=>`<span><i style=\"background:${c}\"></i>${escape(viz.t(k))}</span>`).join('')+'</div>';\n }\n}\n\nconst $=s=>document.querySelector(s),col=['var(--accent)','var(--second)','#7581ae','#b36b47'];\nconst escape=s=>String(s).replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));\nfunction render(){\n let p={};for(const k of Object.keys(DEFAULTS)){p[k]=Number($('#'+k).value);let o=$('#out-'+k);if(o)o.textContent=viz.number(p[k]);}\n const r=economy(N,p);document.body.dataset.result=JSON.stringify(r.metrics);document.body.dataset.params=JSON.stringify(p);\n $('#results').innerHTML=Object.entries(r.metrics).map(([k,v])=>`<div class=\"metric\"><span>${escape(viz.t(k))}</span><strong data-metric=\"${k}\">${viz.number(v,2)}</strong></div>`).join('');\n const el=$('#graphic');el.replaceChildren();\n if(r.plot){const a=r.plot,x=q=>48+q/a.xmax*278,y=v=>205-v/a.ymax*164;\n let svg=`<svg class=\"plot\" viewBox=\"0 0 360 258\" role=\"img\" aria-label=\"${escape(viz.t('figure'))}\"><path d=\"M48 30V205H333\" fill=\"none\" stroke=\"var(--rule)\"/>`;\n for(const q of [0,a.xmax/2,a.xmax])svg+=`<text x=\"${x(q)}\" y=\"226\" text-anchor=\"middle\">${viz.number(q,1)}</text>`;\n for(const v of [0,a.ymax/2,a.ymax])svg+=`<text x=\"39\" y=\"${y(v)+4}\" text-anchor=\"end\">${viz.number(v,1)}</text>`;\n svg+=`<text x=\"48\" y=\"20\">${escape(viz.t(a.y))}</text><text x=\"326\" y=\"251\" text-anchor=\"end\">${escape(viz.t(a.x))}</text>`;\n (a.areas||[]).forEach((region,j)=>{svg+=`<polygon points=\"${region.points.map(([xx,yy])=>x(xx)+','+y(yy)).join(' ')}\" fill=\"${j===0?'#4c8c9d':'#bd7446'}\" fill-opacity=\".30\"/>`;});\n a.lines.forEach((l,j)=>{svg+=`<polyline fill=\"none\" stroke=\"${col[j]}\" stroke-width=\"${l.key==='wedge'?5:2.5}\" ${l.dash?'stroke-dasharray=\"5 4\"':''} points=\"${l.points.map(([xx,yy])=>x(xx)+','+y(yy)).join(' ')}\"/>`;});\n if(a.dot)svg+=`<circle cx=\"${x(a.dot[0])}\" cy=\"${y(a.dot[1])}\" r=\"5\" fill=\"var(--ink)\"/>`;\n el.innerHTML=svg+'</svg><div class=\"legend\">'+a.lines.map((l,j)=>`<span><i style=\"background:${col[j]}\"></i>${escape(viz.t(l.key))}</span>`).join('')+(a.areas||[]).map((a,j)=>`<span><i style=\"height:10px;background:${j===0?'#4c8c9d':'#bd7446'}\"></i>${escape(viz.t(a.key))}</span>`).join('')+'</div>';\n }else if(r.bars){el.innerHTML=r.bars.map(b=>`<div class=\"barrow\"><div class=\"barlabel\"><span>${escape(b.key?viz.t(b.key):viz.t('round')+' '+b.label)}</span><strong>${viz.number(b.v)}</strong></div><div class=\"bar\"><i style=\"width:${Math.abs(b.v)/Math.max(r.max,...r.bars.map(a=>Math.abs(a.v)))*100}%\"></i></div></div>`).join('')+(N===22?`<p class=\"scope\">${escape(viz.t('negative'))}</p>`:'');\n }else if(r.stack){el.innerHTML='<div class=\"stack\">'+r.stack.map((v,j)=>`<span style=\"width:${v}%;background:${col[j]}\">${v}</span>`).join('')+'</div><div class=\"legend\">'+r.stackKeys.map((k,j)=>`<span><i style=\"background:${col[j]}\"></i>${escape(viz.t(k))}</span>`).join('')+'</div>';}\nrenderTeachingView(r,el);\n}\nfor(const k of Object.keys(DEFAULTS))$('#'+k).addEventListener('input',render);\n$('#reset').addEventListener('click',()=>{for(const[k,v]of Object.entries(DEFAULTS))$('#'+k).value=v;render();});render();\n","audio":false,"strings":{"elasticity":"中点法の需要弾力性の絶対値","figure":"現在の条件による図。正確な値は下の結果に示す。","lostRevenue":"失った販売分の減収","newRevenue":"新しい売上","oldRevenue":"元の売上","price":"単価 P","quantity":"数量 Q","remaining":"値上げ後の販売数 / 個","reset":"基準に戻す","result":"計算結果","retainedBase":"残った販売分の元の支払","retainedGain":"残った販売分の増収","scope":"価格10→12、元の販売数100。90個残ると増収180、減収100で差引+80。70個では−160。青は残る売上、緑は増収、橙は減収。"}}
緑色の面積は、維持された販売量における値上げによる収入増、オレンジ色の面積は、消えた販売量によって失われた元の収入である。残った数量を90から70に変更し、どちらの面積が大きいかを観察する。収入が変わらない分岐点は 12Q=1000、つまり約83.33食分が残った場合である。連続的な数量は分析上の近似であり、1食単位で販売する場合、対応する隣接する整数の両側に位置する。
弾性は異なる規模の反応を同じ尺度に置く
「10食分売れなくなった」と言っても、日販20食の店と日販1000食の店では意味が異なります。需要の価格弾力性は、数量と価格の百分率変化を比較します。2つの端点を比較する際、私たちは中点法を用い、正方向と逆方向の計算で同じ基準を使用します。
数量変化率 = (Q1−Q0) / ((Q1+Q0)/2)
価格変化率 = (P1−P0) / ((P1+P0)/2)
需要の価格弾力性の絶対値 = |数量変化率 / 価格変化率|
10から12への変化で、価格変化率は 2/11≈18.18% です。90食分残った場合、数量変化率は −10/95≈−10.53% となり、弾性の絶対値は約0.58です。70食分残った場合は |(-30/85)/(2/11)|≈1.94 となります。前者は1より小さく、数量の百分率反応が弱いことを示し、非弾性と呼びます。後者は1より大きく、弾性があると呼びます。
単位弾性は、収入増と収入減がちょうど相殺される分岐点です。これはこの価格変化、この市場、この期間に対するものであり、商品が常に不変のラベルを持つわけではありません。「食分」を「千食分」に変えると曲線の数値傾きは変わりますが、百分率弾性は変わりません。同じ直線上でも、基点が異なれば、百分率反応も異なります。
営業収入は依然として利益ではありません。価格を下げて多く売り、収入が増えたとしても、追加で作られた朝食は原材料と人件費を消費します。次の記事では取引双方の得失を見てから、企業編でコストを価格決定に組み込みます。この収入図を最適価格決定機と誤解しないでください。
現実の条件を一つ戻す:代替選択肢と時間
隣に同じ種類の新店が開業すれば、本店の顧客は離れやすくなります。「本店の朝食」に対する反応と「全市の朝食」に対する反応を研究することは、同じ問題ではありません。短期的な通勤者は依然として元のルートを通るかもしれませんが、数ヶ月後には通勤や食事の習慣を変えることができ、弾性にも時間的な範囲が生じます。供給側も短期的には設備が固定されており、長期的には拡張可能であり、反応期間を区別する必要があります。
所得弾性は、所得が変化したときにどれだけ購入するかを問い、交差価格弾力性は、別の商品の価格が変化したときにどれだけ購入するかを問います。これらは本商品の需要関係の条件を変更するものであり、「本商品の価格変化、他の条件不変」という弾性と混同して計算してはいけません。現実の2年間の価格と販売量の比が、同時に所得、人口、品質の変化を含んでいる場合、価格自体の効果を識別していません。
シナリオを変えて試してみる
チケット価格が20元から18元へ下がり、販売量が100から110へ増加しました。10枚多く売れたからといって、収入が増加するでしょうか?まず支払いの変化を分解し、中点弾性を計算します。
推論とよくある誤りを展開
元の100枚はそれぞれ2元少なく受け取り、200元減少。追加で売れた10枚は新価格でそれぞれ18元受け取り、180元増加。純減20。収入は2000から1980へ。弾性の絶対値は (10/105)/(2/19)≈0.90 となり、非弾性領域での価格下落による収入減少と一致します。価格下落の分解と上記の価格上昇の分解では、基準を一致させる必要があり、追加で売れたチケットを旧価格で計算しながら、すべての新販売量から2元を控除してはいけません。
ある都市で家賃が10%上昇し、賃貸数量が5%上昇しました。ある人は弾性+0.5を計算し、需要曲線が右上がりだと主張しています。どのような証拠を補う必要がありますか?
推論と行うべき比較を展開
まず、人口、所得、物件数、地域構成、品質が変化したかどうかを確認し、数量がストックか、取引数量か、新規契約かを確認する必要があります。新規人口は需要全体を右へシフトさせ、家賃と数量の同時上昇を引き起こす可能性があります。価格の効果を推定するには、他の条件を区別できる対照群や識別設計が必要です。単に2期間の成長率を割り算しただけでは、このステップは完了していません。「さらに多くのデータが必要」と答えるだけでは不十分で、どのデータがどの説明を除外するために使われるかを説明する必要があります。
参考資料
以下は概念・統計定義の参考資料です。数値例と図は独自の教材用設定です。
CORE · Firms and Markets for Goods and Services · OpenStax · Price Elasticity