起動時にテクニカル電話画面が表示されました。これが私が尋ねられた技術的な質問と私の答えです。 これらの回答をどう思いますか?より良い答えを投稿してください:-)
質問1:標準の52枚のカードデッキを(基本的に任意の言語で)どう表現しますか?どうやってデッキをシャッフルしますか?
Answer: "Card"構造体またはクラスを含む配列を使用します。カードの各インスタンスには、一意の識別子がいくつかあります。配列内の位置か、[0、51]の範囲の一意の整数メンバー変数です。配列をインデックス0からインデックス51まで1回トラバースして、カードをシャッフルします。ランダムにi番目のカードを「別のカード」と交換します(このシャッフルアルゴリズムが正確に機能する方法を覚えていません)。各カードにsame確率を使用することに注意してください...これは、このアルゴリズムの落とし穴です。アルゴリズムは Programming Pearls からのものであると述べました。
質問2:大きなスパース行列を表す方法は?行列は非常に大きくなる可能性があります... 1000x1000のように...比較的少数(〜20)のエントリのみが非ゼロです。
Answer:配列をゼロ以外のエントリのリストに圧縮します。配列内の特定のエントリ(i、j)について...単一の整数kに「マップ」(i、j)...次に、辞書またはハッシュテーブルへのキーとしてkを使用します。 1000x1000スパース配列マップ(i、j)の場合、f(i、j)= i + j * 1001のようなものを使用してkにマップします。1001は、1にすべてのiとjの最大値を加えたものです。私はこのマッピングがどのように機能したかを正確に思い出しませんでした...しかし、インタビュアーはアイデアを思いつきました(私は思う)。
これらの良い答えはありますか?私が2番目の質問を終えた後、面接官が恐ろしい「今のところ私が持っているすべての質問だ」と言ったので、私は不思議に思っています。
乾杯!
カードから始めるのが良いでしょう。ここに、より現実的なモデリングが必要な場合の私の考えを示します。デッキには52枚以上または以下のカードを含めることができるという事実を考慮する必要があります(たとえば、ジョーカーが含まれている場合や、ユーカーデッキの場合など)。したがって、配列の代わりに、多分一般的な列挙可能なリスト(C#のList<Card>
など)を使用できます。これにより、個々のカードの挿入と削除も簡単になります(たとえば、取引のシミュレーション用)。
次に、シャッフルするために、そのリストを任意のサイズの2つのスタックに分割し、カードを上からポップし、それらを3番目のリストにプッシュして、毎回どの半分を選択するかをランダムに選択します。それはより現実的でしょう。
2番目の質問については、それは私には合理的に聞こえます。
まあ、統計的に正しい方法でシャッフルするのは 驚くほど難しい です。私はそれがトリッキーであることを知っているので、私がそれを正しく行っていることを確認するためにそれを調べます。
自分でプログラムする場合は、数千回実行して、それが正しいかどうかを確認してください。そしてそれをレビュアーに指摘してください。
質問1:(基本的に任意の言語で)標準の52カードデッキをどのように表現しますか?どうやってデッキをシャッフルしますか?
Javaでは、私は使用します:
ArrayList<Card> deck
_データ型と名前。Collections.shuffle(deck)
またはCollections.shuffle(deck, myRnd)
は、デッキをシャッフルします。質問2:大規模なスパース行列を表す方法は?行列は非常に大きくなる可能性があります... 1000x1000のように...比較的少数(〜20)のエントリのみが非ゼロです。
Javaでは、ゼロ以外の要素のみを次の場所に格納します。
HashMap<TupleN, Data> matrix
_を一般的なデータ型として使用します。ここで、TupleN
は(カスタムハッシュ関数を使用した)値クラスであり、要素の場所を含みます。HashMap<Long, Data> m
_に組み合わせ、m.get(((Long)i1<<32)+i2);
を使用します。カードを並べ替えるのに最適な方法ではありません。
詳細については、Kneuthを参照してください。
より良いアルゴリズムは:
1) Have a deck of all cards (a)
2) Have a deck with 0 cards (b)
3) Pick one card at random from (a) and remove it.
4) Add picked card to top of (b)
5) While (a) is not empty goto (3)
6) (b) is now shuffled.
これを実装する場合、単一のデッキを使用できることに注意してください。秘密は、いったんカードが選択されると、二度と選択されないことです(つまり、シャッフルされたデッキに移動されると、触れられません)。
2番目の質問では、マトリックスで最適化したい操作を尋ねました。スパース行列の最適な実装は、実行される操作に依存するようです。
両方の質問に対する答えは、「それは何をしたいかによって異なります」です。
トランプは、値とスートのデカルト積の要素と見なすことができ、2つの列挙型を構成するペアとして実装されます。学校レベルに最適OOレッスン。ただし、たとえば、メモリが限られているシステム(つまり、2GB FSMを使用できない)に最適化されたポーカーハンド評価を実装している場合、それは参考になった表現ではありません。これは(一部は関連するプロジェクトオイラー問題で、友人が興味を示したことが原因で)カードごとに64ビットの整数を使用して、ビット表現23456789TJQKA00200300400500600700800900T00J00Q00K00A00C00D00H00Sを使用しました。 。
スパース行列の場合、実装している行列の種類とそれらを使って何をしたいかを本当に考える必要があります。たとえば、対角行列は非常に特殊な実装に適しています。
正直言って、私よりも賢い人がすでにそのような問題を解決していることを知っているし、組み込みネットワークでは頻繁に出てこないので、特にそれについて考えることに多くの時間を費やしたことがないからです。プログラミングでは、公開されているアルゴリズムやサードパーティのライブラリを調べることから始めます。検索に数分かかりますか、それとも自分で思いつく最高のものを教えてくれますか?
「自分で思いつくことができる最高のもの」はあなたのものと非常に似ていますが、カードが手で複製されないようにするために必要なデータ構造、カードの「ビュー」と「モデル」のようなアプリケーションの詳細に入るかもしれません、」など.
これにより、すでに配置されている要素に再度触れないようにすることができませんか?
for (i = 51; i >= 2; i--) {
swap(arr[i], arr[Rand() % i]);
}
swap(arr[0], arr[1]); //just to be safe.
(各カードに同じ確率を使用する場合は注意してください...これは、このアルゴリズムの落とし穴です。)