ゴールシークは、数式で特定の目標を達成するために必要な入力値を見つけるための強力な What-If 分析ツールです。
このデモでは、GC.Spread.Sheets.CalcEngine.goalSeek API を使用して、目標に到達するために必要な値を見つける方法を示します。
サンプルシナリオ
このデモには、2 つの実用的な例が含まれています。
例 1: 売上計算
単価(B3): 初期値は $100
販売数量(B4): 50 個
総売上(B5): =B3*B4($5,000 と計算されます)
売上 $10,000 を達成したい場合、ゴールシークは必要な単価または販売数を自動的に決定できます。
例 2: ローン支払額の計算
借入額(B9): $10,000
期間(月)(B10): 18 か月
利率(B11): 5.00%(初期値)
毎月の支払額(B12): =-PMT(B11/12,B10,B9)
月々の支払額を $600 にしたい場合、ゴールシークは必要な利率(約 10%)を見つけることができます。
API
メソッドの構文
パラメータ
名前
型
説明
changingSheet
GC.Spread.Sheets.Worksheet
調整対象セルを含むワークシートです。
changingRow
number
調整対象セルの 0 始まりの行インデックスです。
changingColumn
number
調整対象セルの 0 始まりの列インデックスです。
formulaSheet
GC.Spread.Sheets.Worksheet
数式セルを含むワークシートです。
formulaRow
number
数式セルの 0 始まりの行インデックスです。
formulaColumn
number
数式セルの 0 始まりの列インデックスです。
desiredResult
number
数式セルで達成したい目標値です。
options
IGoalSeekOptions (optional)
ゴールシークの反復動作と精度を制御するオプション設定です。
options.maximumIterations
number (optional)
最大反復回数です。既定値: 200。
options.tolerance
number (optional)
数式結果と目標値の間で許容される最大差分です。既定値: 0.001。
options.callback
(info: IGoalSeekStepInfo) => boolean | void | Promise<boolean | void> (optional)
各反復後に呼び出されるコールバックです。現在の試行解を受け取り、true を返すとゴールシークを早期停止し、false または void を返すと続行します。コールバックが Promise を返す場合、ゴールシークはその Promise が完了するまで待ってから次の反復に進みます。可視化のために一時停止するなどの非同期処理を行う場合は、コールバックを async 関数として宣言できます。
戻り値
boolean: 同期呼び出し(増分計算なし、非同期コールバックなし)の場合、解が見つかったかどうかを示します。
Promise<boolean>: 増分計算のシナリオ、または options.callback が指定されている場合、解が見つかると true、それ以外の場合は false に解決される Promise を返します。
サンプル
注意事項
この API を呼び出すと、変化させるセルは見つかった値で更新されます。解が見つからない場合、そのセルの元の値が復元されます。
callback が指定されている場合、各反復後に現在のステップ情報とともに呼び出されます。true を返す(または true に解決される)と、goalSeek は早期に停止し、現在の値を最終結果として扱います。
window.onload = function () {
var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), { sheetCount: 1 });
initSpread(spread);
};
function initSpread(spread) {
var sheet = spread.getSheet(0);
// サンプルデータを設定
sheet.setValue(0, 0, 'ゴールシークの例');
sheet.getRange(0, 0, 1, 2).font('bold 14px Arial');
sheet.setValue(2, 0, '単価:');
sheet.setValue(2, 1, 100);
sheet.setFormatter(2, 1, '$#,##0.00');
sheet.setValue(3, 0, '販売数量:');
sheet.setValue(3, 1, 50);
sheet.setValue(4, 0, '総売上:');
sheet.setFormula(4, 1, '=B3*B4');
sheet.setFormatter(4, 1, '$#,##0.00');
sheet.getCell(4, 1).backColor('#e3f2fd');
sheet.setColumnWidth(0, 120);
sheet.setColumnWidth(1, 100);
// PMT の例を追加
sheet.setValue(6, 0, 'ローン支払額の例 (PMT)');
sheet.getRange(6, 0, 1, 2).font('bold 14px Arial');
sheet.setValue(8, 0, '借入額:');
sheet.setValue(8, 1, 10000);
sheet.setFormatter(8, 1, '$#,##0.00');
sheet.setValue(9, 0, '期間(月):');
sheet.setValue(9, 1, 18);
sheet.setValue(10, 0, '利率:');
sheet.setValue(10, 1, 0.05);
sheet.setFormatter(10, 1, '0.00%');
sheet.setValue(11, 0, '毎月の支払額:');
sheet.setFormula(11, 1, '=-PMT(B11/12,B10,B9)');
sheet.setFormatter(11, 1, '$#,##0.00');
sheet.getCell(11, 1).backColor('#fff3cd');
// 手順を追加
sheet.setValue(13, 0, '手順:');
sheet.getCell(13, 0).font('bold 12px Arial');
sheet.setValue(14, 0, '1. 例 1: B5 を数式セルとして使用し、売上目標を確認します');
sheet.setValue(15, 0, '2. 例 2: B12 を数式セル、目標値を 600 に設定します');
sheet.setValue(16, 0, ' B11 を変化させるセルとして、必要な利率を求めます');
sheet.setValue(17, 0, '3. 値を入力して「ゴールシークを実行」をクリックします');
// ゴールシークの実装
document.getElementById("runGoalSeek").addEventListener('click', function() {
var formulaCellRef = document.getElementById("formulaCell").value;
var targetValue = parseFloat(document.getElementById("targetValue").value);
var variableCellRef = document.getElementById("variableCell").value;
var maximumIterations = parseInt(document.getElementById("maximumIterations").value) || 100;
var tolerance = parseFloat(document.getElementById("tolerance").value) || 0.001;
var resultDiv = document.getElementById("result");
try {
var formulaCell = parseCellReference(sheet, formulaCellRef);
var variableCell = parseCellReference(sheet, variableCellRef);
var seekResult = GC.Spread.Sheets.CalcEngine.goalSeek(
variableCell.sheet, variableCell.row, variableCell.col,
formulaCell.sheet, formulaCell.row, formulaCell.col, targetValue,
{
maximumIterations: maximumIterations,
tolerance: tolerance
});
if (seekResult) {
resultDiv.innerHTML = '<div class="success">ゴールシークが成功しました。<br/>見つかった値: ' + variableCell.sheet.getValue(variableCell.row, variableCell.col).toFixed(2) + '</div>';
} else {
resultDiv.innerHTML = '<div class="error">ゴールシークで解が見つかりませんでした。<br/>別の目標値を試してください。</div>';
}
} catch (e) {
resultDiv.innerHTML = '<div class="error">エラー: ' + e.message + '</div>';
}
});
}
function parseCellReference(sheet, address) {
var ranges = GC.Spread.Sheets.CalcEngine.formulaToRanges(sheet, address, 0, 0);
if (ranges && ranges.length === 1 && ranges[0].ranges.length === 1 && ranges[0].ranges[0].rowCount === 1 && ranges[0].ranges[0].colCount === 1) {
return {
sheet: sheet.getParent().getSheetFromName(ranges[0].sheetName),
row: ranges[0].ranges[0].row,
col: ranges[0].ranges[0].col
};
} else {
throw new Error('無効なセル参照です: ' + address);
}
}
<!doctype html>
<html style="height:100%;font-size:14px;">
<head>
<meta name="spreadjs culture" content="ja-jp" />
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="$DEMOROOT$/ja/purejs/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css">
<script src="$DEMOROOT$/ja/purejs/node_modules/@mescius/spread-sheets/dist/gc.spread.sheets.all.min.js" type="text/javascript"></script>
<script src="$DEMOROOT$/ja/purejs/node_modules/@mescius/spread-sheets-resources-ja/dist/gc.spread.sheets.resources.ja.min.js" type="text/javascript"></script>
<script src="$DEMOROOT$/spread/source/js/license.js" type="text/javascript"></script>
<script src="app.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<div class="sample-tutorial">
<div id="ss" class="sample-spreadsheets"></div>
<div class="options-container">
<div class="option-row">
<label>数式セルを設定:</label>
<input type="text" id="formulaCell" value="B5" placeholder="例: B5" />
</div>
<div class="option-row">
<label>目標値:</label>
<input type="number" id="targetValue" value="10000" placeholder="例: 10000" />
</div>
<div class="option-row">
<label>変化させるセル:</label>
<input type="text" id="variableCell" value="B3" placeholder="例: B3" />
</div>
<div class="option-row">
<label>最大反復回数:</label>
<input type="number" id="maximumIterations" value="100" placeholder="既定値: 100" />
</div>
<div class="option-row">
<label>許容誤差:</label>
<input type="number" id="tolerance" value="0.001" step="0.0001" placeholder="既定値: 0.001" />
</div>
<div class="option-row">
<input type="button" value="ゴールシークを実行" id="runGoalSeek" />
</div>
<div class="option-row">
<label>結果:</label>
<div id="result" class="result-container"></div>
</div>
</div>
</div>
</body>
</html>
.sample-tutorial {
position: relative;
height: 100%;
overflow: hidden;
}
.sample-spreadsheets {
width: calc(100% - 280px);
height:100%;
overflow: hidden;
float: left;
}
.options-container {
float: right;
width: 280px;
overflow: auto;
padding: 12px;
height: 100%;
box-sizing: border-box;
background: #fbfbfb;
}
.option-row {
margin-bottom: 12px;
}
.option-row label {
display: block;
margin-bottom: 4px;
font-weight: bold;
font-size: 12px;
}
input[type=text],
input[type=number] {
width: 100%;
padding: 6px;
border: 1px solid #ccc;
border-radius: 3px;
box-sizing: border-box;
}
input[type=button] {
width: 100%;
padding: 8px 6px;
margin-bottom: 6px;
background: #007acc;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
font-weight: bold;
}
input[type=button]:hover {
background: #005a9e;
}
.result-container {
padding: 8px;
border-radius: 3px;
min-height: 40px;
}
.success {
background: #d4edda;
color: #155724;
padding: 8px;
border-radius: 3px;
}
.error {
background: #f8d7da;
color: #721c24;
padding: 8px;
border-radius: 3px;
}
body {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}