Nesneleri karıştırmanın etkili yolu


20

Bazı sınav yazılımları için bir program yazıyorum. Soru, cevap, seçenekler, işaretler ve negatif işaretler için ArrayLists içeren bir soru sınıfı var. Bunun gibi bir şey:

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

Tüm soruları karıştırmak istiyorum, ancak soruların karıştırılması için tüm nesnelerin karıştırılması gerekiyor. Bu soruna şu şekilde yaklaşırdım:

Her şeyden önce, bu tasarımı kullanmazdım ve ArrayList<String>örnek değişkenleri olarak yazmaz String'i kullanmazdım ve daha sonra Collections.shuffleyöntemi nesneleri karıştırmak için kullanırdı. Ancak ekibim bu tasarımda ısrar ediyor.

Şimdi soru sınıfı, sorulara giriş yapılırken artan ArrayLists içeriyor. Soruları şimdi nasıl karıştırırım?


30
Mutlak konuşmaktan nefret ediyorum, ancak ekibiniz bu tasarımda ısrar ederse, yanlışlar. Anlat onlara! Onlara böyle söylediğimi söyle (ve internette yazdım, bu yüzden doğru olmalıyım).
Joachim Sauer

10
Evet, onlara burada bu tür bir tasarımın tipik bir başlangıç ​​hatası olduğunu söyleyen birçok insan olduğunu söyleyin.
Doc Brown

6
Meraktan: ekibiniz bu tasarımda ne gibi avantajlar görüyor?
Joachim Sauer

9
Java adlandırma kuralları sınıf adları için CamelCase ve değişken adları için camelCase'dir.
Tulains Córdova

Bu korkunç tasarım kararı konusunda ekibinizle yüzleşmeniz gerektiğini düşünüyorum. Israr etmeye devam ederse, nedenini öğrenin. Sadece inatçılık ise, çok uzak olmayan bir gelecekte yeni bir takım bulmayı düşünmeye başlayabilirsiniz. Bu yapı için nedenleri varsa, bu nedenleri esaslarına göre düşünün.
Ben Lee

Yanıtlar:


95

Ekibiniz ortak bir sorundan muzdarip: nesne reddetme .

İlişkili tüm bilgileri içeren tek bir soru içeren bir sınıf questionyerine, tüm soruları tek bir örnekte tutan adlı bir sınıf oluşturmaya çalışırsınız .

Bu konuda yanlış bir yol var ve çok yapmaya çalıştığınız şeyi zorlaştırıyor ! Paralel dizileri (veya Listeleri) sıralamak (ve karıştırmak) kötü bir iştir ve bunun için ortak bir API yoktur, çünkü genellikle bundan kaçınmak istersiniz .

Kodunuzu şu şekilde yeniden yapılandırmanızı öneririm:

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

Bu şekilde, sorunuzun karıştırılması önemsiz hale gelir (kullanarak Collections.shuffle()):

Collections.shuffle(questionList);

39
nesne reddi bile değil, veri yapısı reddi
jk.

22

Yapmazsın. Dizinlerin başka bir listesini / sırasını oluşturursunuz ve karıştırırsınız. Ardından, diğer koleksiyonlarınızın "karıştırılmış" sırasını yönlendiren dizinleri yineleyin.

Senaryonuzun dışında bölünmüş şeylerle bile olsa, ayrı sipariş koleksiyonu bir takım faydalar sağlar (paralellik, orijinal koleksiyonu yeniden yüklerken hız, vb.).


10
Ben bu kadar oy kullanmaya isteksiz değilim: o sonraki en iyi çözüm iff bu tasarım aslında sabittir, ancak bu tasarım ısrar olduğunu bu yüzden bu konuda herhangi bir öneri vermek istemem ki yanlış. (meh, yine de seçildi ;-))
Joachim Sauer

3
@joachimSauer - kabul ederken, orjinal koleksiyonun durağan kalması gerektiğinde diğer koleksiyonların statik kalması gereken çok sayıda (daha az rahatsız edici) senaryo var.
Telastyn

4
Evet biliyorum. Ve bir endeks koleksiyonunu karıştırmak, bu durumlar için doğru yaklaşımdır. Benim tek korkum, OP ekibinin tasarımlarını tekrar gözden geçirmeden bunu alıp "yeterince iyi" diyeceğidir.
Joachim Sauer

1
Bu Yanıt, özellikle temel koleksiyon sınıfını veya yapısını gözden geçirme / yeniden kodlama özgürlüğüne sahip olmadığı durumlarda değerlidir; örneğin, bir OS tarafından tutulan koleksiyon için bir API ile ilgili olması gerekir. Endeksleri karıştırmak büyük bir içgörüdür ve temeldeki tasarımı yeniden yapmak kadar içgörü olmasa bile kendi başına ayakta durur.
hardmath

@Jachim Sauer: Aslında endeksleri karıştırmak, sorunun belirtildiği gibi en iyi çözüm olması gerekmez. Bir alternatif için cevabımı görün.
Michael Borgwardt

16

Diğer cevaplara, doğru çözümün uygun bir nesne modeli kullanmak olduğuna katılıyorum.

Bununla birlikte, birden fazla listeyi aynı şekilde karıştırmak oldukça kolaydır:

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

Bu ... bunu yapmanın temiz bir yolu! Şimdi, "ayırma" durumu için, bu şekilde uygulandığında sıralı bir liste üreten bir tohum bulmamız ve ardından bu tohumla tüm listeleri karıştırmamız gerekiyor!
Joachim Sauer

1
@JoachimSauer: sıralamak sorunun bir parçası değildi. Belirli bir RNG için böyle bir tohum bulmanın sistematik bir yolu olup olmadığı ilginç bir soru olsa da.
Michael Borgwardt

2
@MichaelBorgwardt 17'den fazla soru aldığınızda, java rastgele 48 bitlik olası karıştırmaları ifade edemezsiniz (log_2 (17!) = 48.33)
cırcır ucube

@ratchetfreak: bana gerçek bir sorun gibi gelmiyor. Gerekirse bunun yerine SecureRandom'u kullanmak önemsizdir.
Michael Borgwardt

4
@Telastyn: Dizinler listesi IMO'dur ve çözümünüzü kavramsal olarak daha karmaşık hale getiren bir dolaylama katmanıdır ve az ya da çok performans göstermesi, listelerin karışıklıktan sonra ne sıklıkta erişildiğine bağlıdır. Ancak, bir testin insanlar tarafından yanıtlanması için gerçekçi boyutlar göz önüne alındığında performans farklılıkları önemsiz olacaktır.
Michael Borgwardt

3

Bir sınıf oluşturun Question2:

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

Sonra bir haritalama bir işlevi oluşturmak questioniçin ArrayList<Question2>kullanımı, Collection.Shufflebu sonucun, ve haritalama için ikinci işlevi oluşturun ArrayList<Question2>için arka question.

Daha sonra, ekibinize gidin ve bir ArrayList<Question2>yerine kullanmanın questionkodlarını çok fazla geliştireceğine ikna etmeye çalışın , çünkü bu gereksiz dönüşümlerin çoğunu kurtaracaktır.


1
Bu iyi bir fikir, ancak sadece a priori tasarımı değiştirme denemeleri başarısız olduktan sonra.
Sebastian Redl

@SebastianRedl: Bazen sadece çözümü kodda gösterirken insanları daha iyi bir tasarıma ikna etmek daha kolaydır.
Doc Brown

1

Orijinal naif ve yanlış cevabım:

Sadece (en azından) nrasgele sayılar oluşturun ve n sahip iolduğunuz her liste için for döngüsünde öğe ile değişim yapın .

Sahte kodda:

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

}

Güncelleme:

Kodlama korku makalesini işaret ettiğiniz için the_lotus için teşekkürler. Şimdi çok daha akıllı hissediyorum :-) Her neyse Jeff Atwood da Fisher-Yates algoritmasını kullanarak nasıl doğru yapılacağını gösteriyor :

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

Buradaki ana fark, her bir elemanın sadece bir kez değiştirilmesidir.

Diğer cevaplar nesne modelinizin kusurlu olduğunu doğru bir şekilde açıklasa da, değiştirmek için pozitioin içinde olmayabilirsiniz. Fisher-Yates algoritması, sorununuzu veri modelinizi değiştirmeden çözecektir.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.