Modo efficiente per mescolare oggetti

20

Sto scrivendo un programma per alcuni software di quiz. Ho una classe di domande contenente gli elenchi di array per domande, risposte, opzioni, segni e segni negativi. Qualcosa del genere:

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;    
}

Voglio mescolare tutte le domande, ma per le domande da mescolare, tutti gli oggetti devono essere mescolati. Avrei affrontato questo problema in questo modo:

Prima di tutto, non avrei usato questo disegno e non ho usato il tipo String non ArrayList<String> come variabili di istanza, e quindi avrei usato il metodo Collections.shuffle per mescolare oggetti. Ma il mio team insiste su questo design.

Ora, la classe delle domande contiene ArrayList in aumento quando viene fatta la voce delle domande. Come mescolare le domande adesso?

    
posta user1369975 28.05.2013 - 13:16
fonte

5 risposte

95

Il tuo team soffre di un problema comune: oggetto negazione .

Invece di una classe che contiene una singola domanda con tutte le informazioni ad essa associate, provi a creare una classe chiamata question che contiene tutte le domande in una singola istanza.

Questo è il modo sbagliato per farlo e questo complica quello che cerchi di fare molto ! Ordinare (e mischiare) array paralleli (o elenchi) è un business spiacevole e non ci sono API comuni per questo, semplicemente perché di solito lo si vuole evitare .

Ti suggerisco di ristrutturare il tuo codice in questo modo:

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>();

In questo modo, mescolare la tua domanda diventa banale (usando Collections.shuffle() ):

Collections.shuffle(questionList);
    
risposta data 28.05.2013 - 13:28
fonte
22

Non lo fai. Fai un altro elenco / coda di indici e mischia quello. Quindi esegui l'iterazione degli indici che gestiscono l'ordine "mescolato" delle altre raccolte.

Anche al di fuori dello scenario in cui le cose si dividono, la raccolta differenziata degli ordini offre una serie di vantaggi (parallelismo, velocità nel reinserire la collezione originale è costoso, ecc.)

    
risposta data 28.05.2013 - 13:32
fonte
16

Sono d'accordo con le altre risposte che la soluzione corretta è usare un modello di oggetto appropriato.

Tuttavia, in realtà è abbastanza facile mescolare più elenchi in modo identico:

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);
...
    
risposta data 28.05.2013 - 14:24
fonte
3

Crea una classe Question2 :

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

Quindi crea una funzione mappando un question a ArrayList<Question2> , usa Collection.Shuffle per quel risultato e crea una seconda funzione per mappare ArrayList<Question2> su question .

In seguito, entra nel tuo team e prova a convincerli che l'utilizzo di ArrayList<Question2> anziché question migliorerebbe molto il loro codice, poiché salverebbe molte di quelle conversioni inutili.

    
risposta data 28.05.2013 - 13:36
fonte
1

La mia originale risposta originale e errata :

Just create (at least) n random numbers and interchange item n with item i in a for loop for each list you have.

In pseudo code:

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);

}

Aggiornamento:

Grazie per il_lotus per aver indicato l'articolo sull'horror in codice. Mi sento molto più intelligente ora :-) In ogni caso Jeff Atwood mostra anche come farlo correttamente, utilizzando l'algoritmo 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);
}

La differenza principale qui è che ogni elemento viene scambiato una sola volta.

E mentre le altre risposte spiegano correttamente che il tuo modello di oggetti è difettoso, potresti non essere nella posizione per cambiarlo. Quindi l'algoritmo Fisher-Yates risolverebbe il tuo problema senza modificare il tuo modello di dati.

    
risposta data 28.05.2013 - 15:20
fonte

Leggi altre domande sui tag