技術共有

ゲーム開発面接の質問 7

2024-07-08

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

基礎となる ArrayList データ構造はどのようなものですか?

ArrayList の最下層は配列に基づいて実装されます。これは、配列のサイズを動的に拡大または縮小することによって行われます。容量が十分でない場合は、より大きな配列を作成し、元のデータをコピーし、最後に新しいデータを追加します。

ArrayList の拡張の仕組みは、最初の要素が追加されるときの ArrayList の容量は 10 ですが、新しい要素が追加されるたびに、その容量を超えると元の容量の 2 倍、つまり元の容量 * 2 になります。 、元の容量が 0 の場合、新しい容量は 1 になります。

ArrayList 展開メカニズムの長所と短所:

アドバンテージ:
  1. ArrayList の展開メカニズムは理解しやすく、実装も簡単です。
  2. 拡張された容量は元の容量よりも大きい必要があります。これにより、新しい要素を追加するときに配列のコピーの数が減り、要素の追加効率が向上します。
欠点:
  1. ArrayList 拡張メカニズムはメモリの浪費につながり、メモリ空間の浪費を容易に引き起こす可能性があります。
  2. 容量が大きいと、拡張ごとに大量のメモリと CPU リソースが必要となり、パフォーマンスに影響を与えるため、ArrayList を使用する場合は、初期容量を適切に設定する必要があります。
ArrayList はスレッドセーフではありません

ArrayList の内部実装は配列に基づいています。たとえば、1 つのスレッドが ArrayList のデータを読み取り、別のスレッドが ArrayList にデータを追加または削除すると、データの不整合が発生する可能性があります。 ArrayList のデータを変更する可能性があるため、ArrayList データを読み取るスレッドが不正なデータを読み取り、プログラム エラーを引き起こす可能性があります。

ArrayList スレッドのセキュリティの問題を解決する一般的な方法は次のとおりです。
  1. ArrayList の代わりに Vector を使用します。Vector は同期され、すべてのメソッドは同期によって変更されるため、スレッドセーフです。
  2. ArrayList をスレッド セーフにラップする: Collections.synchronizedList(list) メソッドを使用して、ArrayList をスレッド セーフに変換します。
  3. CopyOnWriteArrayList を使用します。これは、スレッドセーフで効率的なコレクションです。この実装のコストは、読み取りが多く書き込みが少ない同時シナリオに適しています。
  4. ロックを使用してスレッド セーフを実現する: ReentrantLock などのロック メカニズムを使用して、スレッド セーフな ArrayList を実装できます。

スタックとヒープの違い

  • スタックは、先入れ先出しの原則に基づいて、一方の端でのみデータの挿入と削除ができるという特殊な線形テーブルです。これは、関数のパラメーター値、ローカル変数などを保存するために使用できるストレージ構造です。

  • ヒープは、すべてのノードの値がその子ノードの値以上であり、ルート ノードの値が最大または最小であるという事実を特徴とする特別なツリー構造です。ヒープは、並べ替えや検索など、大量のデータを保存するために使用できる動的ストレージ構造です。

コルーチンの最下層

コルーチンの本質は軽量スレッドです。各コルーチンには、関数とそのパラメーター、ローカル変数などを保存するためのスタックがあります。コルーチンは、一時停止、再開、切り替えが可能です。

C# GC (ガベージコレクション) の原理

  1. 参照カウント: オブジェクトが参照されると、その参照カウントは 1 増加します。参照が無効になると、その参照カウントは 1 減少します。オブジェクトの参照カウントが 0 になると、そのオブジェクトはガベージ コレクターによって回収されます。
  2. マークアンドクリア: ガベージ コレクターは、実行中、参照関係に従ってオブジェクトを走査し、アクセス可能なオブジェクトを「生きている」としてマークし、アクセスできないオブジェクトを「デッド」としてマークし、その後、「デッド」とマークされたすべてのオブジェクトをクリアします。
  3. コピー アルゴリズム: ガベージ コレクターは使用可能なメモリを 2 つのブロックに分割し、一度に 1 つのブロックのみを使用します。スペースが不十分な場合は、残ったオブジェクトがスペースの別のブロックにコピーされ、コピーされたオブジェクトは元のブロックから消去されます。空間。
  4. マーク補完アルゴリズム: ガベージ コレクターが実行されると、最初にすべての生き残ったオブジェクトにマークが付けられ、次にすべての生き残ったオブジェクトを一方の端に移動して、未使用のスペース フラグメントを消去します。

ステータス同期とフレーム同期の違い

状態の同期マルチマシンシステム内の各マシンの状態(位置、速度、加速度など)を各制御サイクルで他のマシンに送信し、各マシンの同期を保つことを指します。状態同期は複数台の協調制御のリアルタイム性を実現しますが、制御周期ごとに大量のデータを送信する必要があるため、精度が比較的低い場合があります。

フレーム同期これは、各制御サイクルで、マルチマシン システム内の各マシンの制御コマンドが他のマシンに送信され、各マシンの同期が維持されることを意味します。フレーム同期は複数台の協調制御の精度を実現できますが、各制御周期で送信される制御コマンドの数が少ないため、リアルタイム性が比較的低い場合があります。

一般的なデザインパターン

シングルトンパターン
ファクトリーパターン
複合パターン
プロキシパターン

リンクリスト、二分木、ハッシュテーブルの特徴

1. リンクされたリスト:
  • これは線形リスト構造であり、各ノードが次のノードを指すポインタを持ち、リンクされたリストを形成することを特徴とします。
  • ノードの挿入または削除に関係なく、ポインタのポイントを変更するだけでよく、時間計算量は O(1) です。
  • ノードを見つける時間計算量は O(n) であり、ヘッド ノードから順番に検索する必要があります。
  • リンク リストでは、スペース割り当ての問題を考慮する必要はありません。一般に、柔軟なメモリ管理を実現するために、メモリの動的割り当てが使用されます。
2. 二分木:
  • バイナリ ツリーは、各ノードが最大 2 つの子を持つツリー構造です。
  • バイナリ ツリーの検索、挿入、削除操作の時間計算量は、それぞれ O(log2n)、O(log2n)、O(log2n) です。
  • バイナリ ツリーの各ノードは連続的に格納されず、階層的に格納され、各ノードは 2 つの子のみを持つことができるため、ストレージ スペースをより効率的に使用できます。
3. ハッシュテーブル:
  • ハッシュ テーブルは、検索を高速化するためにレコードにアクセスするためにキーをテーブル内の場所にマップするデータ構造です。
  • ハッシュ テーブルの検索時間の計算量は O(1)、挿入および削除の時間計算量は O(n) です。
  • ハッシュ テーブルの実装には、ハッシュ テーブル自体を格納するための追加のスペースが必要であり、ハッシュの衝突の問題を解決する必要があります。

HashMap の基本原理

HashMap の最下層は、配列リンク リスト (赤黒ツリー) を使用して実装され、キーの hashCode 値に従ってデータを格納します。ハッシュに基づいて配列内のデータの位置 (ハッシュ競合) を計算できます。コードを作成し、リンク リスト (赤黒ツリー) を使用して競合データを保存します。 HashMap Java 8 では、リンクされたリストの長さがしきい値 (デフォルトは 8) を超えると、クエリ効率を向上させるために赤黒ツリーに変換されます。容量が足りない場合、デフォルトの負荷率は 0.75 で、容量の 2 倍の拡張方法になります。

リンクされたリストに循環があるかどうかを確認する方法

  1. ハッシュ テーブルを使用して、リンク リスト内の各ノードをスキャンし、ノードのアドレスをハッシュ テーブルに保存します。現在のノードがハッシュ テーブルに既に存在する場合、リンク リストにはサイクルがあることを意味します。
  2. 2 つのポインタを定義します。低速ポインタは一度に 1 ステップずつ移動し、高速ポインタは一度に 2 ステップずつ移動します。高速ポインタが低速ポインタに遭遇した場合、リンク リストにはサイクルがあることを意味します。

スタックとキューの使用シナリオは何ですか?

ブラウザの前進機能と後退機能: ブラウザがアクセスする Web ページは、スタック データ構造を通じて前進機能と後退機能を実現できます。

tcp スティッキー問題について何か知っていますか?

TCP スティッキー問題とは、TCP プロトコルがデータ送信時にデータを断片化しないため、受信側で受信されるデータの量が送信側で送信されるデータの量よりも多くなるという事実を指します。

TCP スティッキー問題を解決する方法は次のとおりです。
  1. 送信側で区切り文字を追加する: 送信側で区切り文字を追加します。受信側は区切り文字を受信した後、区切り文字に基づいてデータを分割できます。
  2. 送信側でヘッダーを追加する: 送信側は、データを送信する前にヘッダーを追加します。ヘッダーには、現在のデータの長さ情報が含まれています。受信側は、ヘッダー内の長さ情報に基づいてデータを分割します。
  3. 送信側でバッファを追加する: データを送信する前に、送信側はまずデータをバッファに入れ、毎回データを受信した後、受信側でデータを送信するかどうかを決定します。データの全長に基づいてデータが完成します。

UDP を使用して単純な TCP を実装する方法

まず、UDP データグラムは、TCP/IP プロトコルでの 3 ウェイ ハンドシェイク プロセスの実装に役立ちます。最初のハンドシェイクでは、クライアントはハンドシェイク要求を含む UDP データグラムを送信します。サーバーがこのメッセージを受信すると、サーバーがクライアントのハンドシェイク要求を受信し、サービスを提供する準備ができていることを示す確認メッセージを返します。 2 回目のハンドシェイクでは、クライアントは再度 UDP データグラムを送信します。今回のメッセージには、サーバーがクライアントを識別できるように、クライアントの IP アドレス、ポート番号などの有用な情報が含まれています。 3 回目のハンドシェイクでは、サーバーは接続が確立され、クライアントがデータの送信を開始できることを示す UDP データグラムを送信します。

次に、UDP データグラムは、TCP/IP プロトコルでのデータ送信プロセスの実現にも役立ちます。クライアントがサーバーにデータを送信する必要がある場合、データは UDP データグラムにカプセル化されてサーバーに送信され、サーバーは UDP データグラムを受信した後、メッセージに含まれるデータを解析し、関連する処理を実行します。

最後に、UDP データグラムは、TCP/IP プロトコルでの接続終了プロセスの実装にも役立ちます。クライアントはサーバーと通信する必要がなくなった場合、クライアントが接続を終了することを示す UDP データグラムを送信できます。サーバーはこのメッセージを受信すると、対応するリソースを解放し、TCP/IP プロトコル全体を完了します。接続プロセス

コルーチンを使用したことがありますか?コルーチンを使用する理由コルーチンが速い理由

コルーチンを使用すると、プログラムが異なるタスク間を切り替えることができるため、プログラムの効率が向上し、プログラムの実行時間が短縮されます。コルーチンを使用すると、プログラムは、1 つのタスクの完了を待ってから別のタスクを開始するのではなく、複数のタスクを切り替えることができます。また、異なるスレッド間で変数を共有できるため、プログラムの実行時間を短縮できます。マルチタスク アプリケーションの場合、コルーチンを使用するとパフォーマンスが大幅に向上し、実行速度が速くなります。

同じ長さの配列またはリンクされたリストを走査する方が速いですか?なぜ?

配列の方が高速です。配列の各要素のアドレスは連続的かつ固定であり、次の要素のアドレスをすぐに取得できますが、リンクされたリストの各要素のアドレスは不連続であり、ポインタをトラバースする必要があるためです。次の要素のアドレスを取得するため、配列のトラバースが高速になります。

仮想関数について話しましょう。なぜ仮想関数が必要なのでしょうか?

仮想関数は、コンパイラによって自動的に定義され、コンパイル時に呼び出すことができるという点で、通常の関数とは異なる特別な関数です。仮想関数の特徴は、その実装がコンパイル時ではなく実行時に決定されることです。
仮想関数の主な目的は、抽象クラスで複数の仮想関数を定義し、そのサブクラスでこれらの関数を実装できるようにすることです。

デストラクターを仮想関数にすることはできますか?仮想関数である必要がありますか?なぜ?仮想関数ではない場合、何が問題になるのでしょうか?

仮想関数である必要はありませんが、仮想関数を使用することをお勧めします。仮想関数の場合、派生クラスのデストラクターを正しく実行できるように、仮想関数は派生クラスによってオーバーライドされるためです。を使用しないと、派生クラスのデストラクターが呼び出されず、メモリ リークなどの問題が発生する可能性があります。

レンダリング パイプラインの紹介

レンダリング パイプラインは、ゲーム シーン データを入力情報から画面に表示される画像に変換するために使用される一連のステップです。

レンダリング パイプラインのプロセスは、準備ステージ、ジオメトリ ステージ、ライティング ステージの 3 つの主要なステージに分かれています。

準備フェーズでは、ゲーム エンジンはゲーム シーンのモデルとテクスチャをグラフィックス プロセッシング ユニット (GPU) にロードし、後続のフェーズで使用するためにデータを整理します。

ジオメトリの段階では、マトリックス変換を使用してモデルを 3 次元空間に配置し、モデルを画面上のピクセルでサポートできる形式に変換します。

照明段階では、光源と照明モデルを使用して各ピクセルの色の値が計算され、結果の画像が最終的に画面に表示されます。

二分探索ツリーの挿入、クエリ、削除の操作と、時間の計算量について話しましょう。

  1. 入れる:
  • 時間計算量: O(log n)
  • 手順:
  1. 挿入されるノードをルート ノードから開始して新しいリーフ ノードとして扱います。
  2. 挿入するノード値が現在のノード値より小さい場合は、現在のノードの左側の子ノードに移動します。
  3. 挿入されるノード値が現在のノード値より大きい場合は、現在のノードの右の子ノードに移動します。
  4. 現在のノードに子ノードがない場合、挿入されるノードは現在のノードの子ノードになります。
  5. それ以外の場合は、子ノードのないノードが見つかるまで手順 2 ~ 4 を繰り返し、挿入するノードをこのノードの子ノードとして使用します。

貪欲アルゴリズムが最適解を得る条件は何でしょうか?

貪欲アルゴリズムが最適解を取得するための条件は、「最適な部分構造」と「貪欲な選択プロパティ」です。

  1. 最適な部分構造: 問題に対する最適な解決策には、問題の部分問題に対する最適な解決策が含まれます。
  2. 貪欲な選択プロパティ: 各ステップで、局所的な最適な選択が行われ、最終結果が全体的な最適なソリューションになります。