Main Content

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

Parfeval を用いたブラックジャック

この例では Parallel Computing Toolbox™ を使用して、カード ゲームのブラックジャック (「21」とも呼ばれる) をプレイします。別々に数千のゲーム数をプレイする多数のプレーヤーを同時にシミュレートし、利得の統計を表示します。この例では、parfeval を使用してワーカーの並列プールで非同期的にシミュレーションを実行します。こうすることで、結果が使用可能になった時点で結果の表示を更新できます。

関連する例:

この例のコードは以下の関数に含まれています。

function paralleldemo_blackjack_parfeval

逐次的問題の解析

ブラックジャックのプレーヤーは互いに独立しているため、並列でシミュレートできます。これを行うには、問題をいくつかの小さな関数評価に分割します。最大で numPlayers 回のシミュレーションを実行し、各プレーヤーはブラックジャックを numHands 回プレイします。結果が使用可能になったらすぐにプロットし、経過時間が maxSimulationTime 秒を超えるか、ユーザーが実行をキャンセルした場合はシミュレーションを終了します。

numPlayers        = 100;
numHands          = 5000;
maxSimulationTime = 20;

作業を個々の関数評価に分割

関数 parfeval を呼び出して、並列プールのワーカーにおけるシミュレーションの評価を要求します。並列プールは必要に応じて自動的に作成されます。関数 parfevalparallel.Future オブジェクトを返します。このオブジェクトを使用して、結果が使用可能になった時点でこれらの結果にアクセスします。詳細については、pctdemo_task_blackjack のコードを参照してください。

for idx = numPlayers:-1:1
    futures(idx) = parfeval(@pctdemo_task_blackjack, 1, numHands, 1);
end
% Create an onCleanup to ensure we do not leave any futures running when we exit
% this example.
cancelFutures = onCleanup(@() cancel(futures));

結果収集と進行状況監視の設定

並列プール ワーカーはただちに pctdemo_task_blackjack の実行を開始し、結果が使用可能になったら、fetchNext メソッドを使用してすぐにその収集と表示を行うことができます。resultsSoFar を使用して結果を累積します。配列 completed を更新して futures のどの要素が完了したかを示し、カウンター numCompleted をインクリメントします。fetchNext メソッドに任意引数 timeout を指定して、使用可能な新しい結果がない場合はすぐにメソッドが戻るようにします。

resultsSoFar = zeros(numHands, numPlayers); % Allocate space for all results
completed    = false(1, numPlayers);        % Has a given future completed yet
timeout      = 2;                           % fetchNext timeout in seconds
numCompleted = 0;                           % How many simulations have completed
fig          = pctdemo_setup_blackjack(1);  % Create a figure to display results

% Build a waitbar with a cancel button, using appdata to track
% whether the cancel button has been pressed.
hWaitBar = waitbar(0, 'Blackjack progress', 'CreateCancelBtn', ...
                   @(src, event) setappdata(gcbf(), 'Cancelled', true));
setappdata(hWaitBar, 'Cancelled', false);

使用可能になった結果の収集と表示

numPlayers 件の結果が表示されるまで、fetchNext をループで呼び出して結果の収集と表示を行います。fetchNext が新たな結果を返したら、この結果を resultsSoFar に割り当て、配列 completednumCompleted カウンターを更新し、プロットを更新します。ユーザーがウェイトバーのキャンセル ボタンを押した場合、または maxSimulationTime が経過した場合は、ループを早期に中止します。

startTime = clock();
while numCompleted < numPlayers

    % fetchNext blocks execution until one element of futures has completed.  It
    % then returns the index into futures of the element that has now completed,
    % and the results from execution.
    [completedIdx, resultThisTime] = fetchNext(futures, timeout);

    % If fetchNext timed out returning an empty completedIdx, do not attempt to
    % process results.
    if ~isempty(completedIdx)
        numCompleted = numCompleted + 1;
        % Update list of completed futures.
        completed(completedIdx) = true;
        % Fill out portion of results.
        resultsSoFar(:, completedIdx) = resultThisTime;
        % Update plot.
        pctdemo_plot_blackjack(fig, resultsSoFar(:, completed), false);
    end

    % Check to see if we have run out of time.
    timeElapsed = etime(clock(), startTime);
    if timeElapsed > maxSimulationTime
        fprintf('Simulation terminating: maxSimulationTime exceeded.\n');
        break;
    end

    % Check to see if the cancel button was pressed.
    if getappdata(hWaitBar, 'Cancelled')
        fprintf('Simulation cancelled.\n');
        break;
    end

    % Update the waitbar.
    fractionTimeElapsed      = timeElapsed / maxSimulationTime;
    fractionPlayersCompleted = numCompleted / numPlayers;
    fractionComplete         = max(fractionTimeElapsed, fractionPlayersCompleted);
    waitbar(fractionComplete, hWaitBar);
end
fprintf('Number of simulations completed: %d\n', numCompleted);

% Now the simulation is complete, we can cancel the futures and delete
% the waitbar.
cancel(futures);
delete(hWaitBar);
Simulation terminating: maxSimulationTime exceeded.
Number of simulations completed: 74

end