web-dev-qa-db-ja.com

オブジェクトをシャッフルする効率的な方法

いくつかのクイズソフトウェアのプログラムを書いています。質問、回答、オプション、マーク、ネガティブマークのArrayListを含む質問クラスがあります。このようなもの:

class question
{
    private ArrayList<Integer> index_list;
    private ArrayList<String> question_list;        
    private ArrayList<String> answer_list;      
    private ArrayList<String> opt1_list;        
    private ArrayList<String> opt2_list;    
}

すべての質問をシャッフルしたいのですが、質問をシャッフルするには、すべてのオブジェクトをシャッフルする必要があります。私はこの方法でこの問題に取り組みました:

まず、私はこのデザインを使用せず、文字列ArrayList<String>タイプ変数をインスタンス変数として使用し、Collections.shuffleメソッドでオブジェクトをシャッフルします。しかし、私のチームはこの設計を主張しています。

現在、質問へのエントリが作成されるにつれて、質問クラスには増加するArrayListが含まれています。質問を今すぐ整理する方法は?

20
user1369975

あなたのチームは一般的な問題に悩まされています: オブジェクト拒否

すべての情報が関連付けられた単一の質問を保持するクラスの代わりに、単一のインスタンスですべての質問を保持するquestionというクラスを作成しようとします。

それはそれをするのに間違った方法であり、あなたがしようとすることを複雑にしますたくさん!並列配列(またはリスト)のソート(およびシャッフル)は厄介なビジネスであり、単純な通常はまったく避けたいであるため、共通のAPIはありません。

次のようにコードを再構築することをお勧めします。

_class Question
{
    private Integer index;
    private String question;        
    private String answer;      
    private String opt1;        
    private String opt2;    
}

// somewhere else
List<Question> questionList = new ArrayList<Question>();
_

このようにして、質問をシャッフルすることは簡単になります( Collections.shuffle() を使用):

_Collections.shuffle(questionList);
_
95
Joachim Sauer

あなたはしません。インデックスの別のリスト/キューを作成し、それをシャッフルします。次に、他のコレクションの「シャッフル」順序を駆動するインデックスを反復処理します。

シナリオの外で物事が分かれている場合でも、個別の順序付けコレクションにはいくつかの利点があります(並列処理、元のコレクションを再装着するときの速度が高くつくなど)。

22
Telastyn

正しい解決策は適切なオブジェクトモデルを使用することであるという他の回答にも同意します。

ただし、実際には複数のリストを同じ方法でシャッフルするのは非常に簡単です。

Random rnd = new Random();
long seed = rnd.nextLong();

rnd.setSeed(seed);
Collections.shuffle(index_list, rnd);
rnd.setSeed(seed);
Collections.shuffle(question_list, rnd);
rnd.setSeed(seed);
Collections.shuffle(answer_list, rnd);
...
16

クラスを作成するQuestion2

class Question2
{
    public Integer index_list;
    public String question_list;        
    public String answer_list;      
    public String opt1_list;        
    public String opt2_list;    
}

次に、questionArrayList<Question2>にマッピングする関数を作成し、その結果にCollection.Shuffleを使用して、ArrayList<Question2>questionにマッピングするための2つ目の関数を作成します。

その後、チームに行き、questionの代わりにArrayList<Question2>を使用するとコードが大幅に改善され、不要な変換が大幅に削減されることを説得します。

3
Doc Brown

私の元の素朴で、間違った答え:

(少なくとも)n乱数を作成し、アイテムnとアイテムiを、それぞれのリストのforループで交換します。

疑似コード:

for (in i = 0; i < question_list.length(); i++) {
  int random = randomNumber(0, questions_list.length()-1);
  question_list.switchItems(i, random);
  answer_list.switchItems(i, random);
  opt1_list.switchItems(i, random);
  opt2_list.switchItems(i, random);

}

更新:

コーディングホラー記事を指摘してくれたthe_Lotusに感謝します。私は今、ずっと賢く感じています:-)とにかく、ジェフ・アトウッドは Fisher-Yatesアルゴリズム を使用して、それを正しく行う方法も示しています:

for (int i = question_list.Length - 1; i > 0; i--){
  int n = Rand.Next(i + 1); //assuming Rand.Next(x) returns values between 0 and x-1
  question_list.switchItems(i, n);
  answer_list.switchItems(i, n);
  opt1_list.switchItems(i, n);
  opt2_list.switchItems(i, n);
}

ここでの主な違いは、各要素が一度だけスワップされることです。

そして、他の答えはあなたのオブジェクトモデルに欠陥があることを正しく説明していますが、あなたはそれを変更する立場にないかもしれません。したがって、Fisher-Yatesアルゴリズムは、データモデルを変更せずに問題を解決します。

1
codingFriend1