Cách hiệu quả để xáo trộn các đối tượng


20

Tôi đang viết một chương trình cho một số phần mềm đố vui. Tôi có một lớp câu hỏi chứa ArrayLists cho câu hỏi, câu trả lời, tùy chọn, nhãn hiệu và dấu âm. Một cái gì đó như thế này:

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ôi muốn xáo trộn tất cả các câu hỏi, nhưng để các câu hỏi được xáo trộn, tất cả các đối tượng cần được xáo trộn. Tôi đã có thể tiếp cận vấn đề này theo cách này:

Trước hết, tôi sẽ không sử dụng thiết kế này và sử dụng Chuỗi không ArrayList<String>gõ làm biến Collections.shuffleđối tượng và sau đó sẽ sử dụng phương thức để xáo trộn các đối tượng. Nhưng nhóm của tôi nhấn mạnh vào thiết kế này.

Bây giờ, lớp câu hỏi đang chứa ArrayLists ngày càng tăng khi mục nhập cho các câu hỏi được thực hiện. Làm thế nào để xáo trộn các câu hỏi bây giờ?


30
Tôi ghét nói chuyện tuyệt đối, nhưng nếu nhóm của bạn khăng khăng thiết kế này, thì họ đã sai. Noi vơi họ! Nói với họ rằng tôi đã nói như vậy (và tôi đã viết nó trên Internet, vì vậy tôi phải đúng).
Joachim Sauer

10
Vâng, nói với họ rằng có rất nhiều người ở đây nói với bạn rằng loại thiết kế này là một sai lầm điển hình cho người mới bắt đầu.
Doc Brown

6
Vì tò mò: nhóm của bạn nhìn thấy những lợi thế gì trong thiết kế này?
Joachim Sauer

9
Các quy ước đặt tên Java là CamelCase cho các tên lớp và camelCase cho các tên biến.
Tulains Córdova

Tôi nghĩ rằng bạn cần phải đối đầu với nhóm của bạn về quyết định thiết kế khủng khiếp này. Nếu họ tiếp tục nhấn mạnh, tìm hiểu tại sao. Nếu đó chỉ là sự bướng bỉnh, có lẽ hãy bắt đầu nghĩ đến việc tìm một đội mới trong tương lai không xa. Nếu họ có lý do cho cấu trúc này, thì hãy xem xét những lý do đó trên giá trị của họ.
Ben Lee

Câu trả lời:


95

Nhóm của bạn bị một vấn đề phổ biến: từ chối đối tượng .

Thay vì một lớp chứa một câu hỏi duy nhất với tất cả thông tin liên quan đến nó, bạn cố gắng tạo một lớp có tên questionchứa tất cả các câu hỏi trong một trường hợp duy nhất.

Đó là cách sai lầm để làm điều đó, và nó làm phức tạp những gì bạn cố gắng làm rất nhiều ! Sắp xếp (và xáo trộn) các mảng song song (hoặc Danh sách) là một công việc khó chịu và không có API chung cho nó, đơn giản vì bạn thường muốn tránh nó .

Tôi đề nghị bạn cấu trúc lại mã của bạn như thế này:

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

Bằng cách này, xáo trộn câu hỏi của bạn trở nên tầm thường (sử dụng Collections.shuffle()):

Collections.shuffle(questionList);

39
nó thậm chí không từ chối đối tượng đó là từ chối cấu trúc dữ liệu
jk.

22

Bạn không. Bạn tạo một danh sách / hàng đợi các chỉ mục và xáo trộn đó. Sau đó, bạn lặp lại các chỉ mục điều khiển thứ tự "xáo trộn" của các bộ sưu tập khác của bạn.

Ngay cả bên ngoài kịch bản của bạn với những thứ tách ra, bộ sưu tập đặt hàng riêng biệt cung cấp một số lợi ích (song song, tốc độ khi gắn lại bộ sưu tập ban đầu là tốn kém, v.v.).


10
Tôi miễn cưỡng bỏ phiếu này: đó là giải pháp tốt nhất tiếp theo nếu thiết kế này thực sự cố định, nhưng nhấn mạnh vào thiết kế này là quá sai, tôi không muốn đưa ra bất kỳ đề xuất nào về nó. (meh, dù sao cũng được nâng cấp ;-))
Joachim Sauer

3
@joachimSauer - trong khi tôi đồng ý, có rất nhiều tình huống khác (ít gây khó chịu hơn) trong đó bộ sưu tập gốc phải duy trì trạng thái tĩnh trong khi đường dẫn đi qua chúng cần thay đổi.
Telastyn

4
Vâng tôi biết. Và xáo trộn một bộ sưu tập các chỉ số là cách tiếp cận chính xác cho những tình huống đó. Nỗi sợ duy nhất của tôi là nhóm OP sẽ chỉ lấy cái này và nói "đủ tốt", mà không cần xem lại thiết kế của họ.
Joachim Sauer

1
Câu trả lời này có giá trị đặc biệt đối với trường hợp người ta không có quyền tự do sửa đổi / mã hóa lại lớp hoặc cấu trúc bộ sưu tập cơ bản, ví dụ: người ta phải thực hiện với API cho bộ sưu tập được duy trì hệ điều hành. Xáo trộn các chỉ số là một cái nhìn sâu sắc và đứng vững ngay cả khi nó không quan tâm sâu sắc như làm lại thiết kế bên dưới.
hardmath

@Joachim Sauer: thực sự xáo trộn các chỉ số không nhất thiết là giải pháp tốt nhất cho vấn đề như đã nêu. Xem câu trả lời của tôi cho một sự thay thế.
Michael Borgwardt

16

Tôi đồng ý với các câu trả lời khác rằng giải pháp chính xác là sử dụng mô hình đối tượng phù hợp.

Tuy nhiên, thực sự khá dễ dàng để xáo trộn nhiều danh sách theo cách giống hệt nhau:

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

Đó là ... một cách gọn gàng để làm điều đó! Bây giờ, đối với trường hợp "sắp xếp", chúng ta chỉ cần tìm một hạt giống tạo ra một danh sách được sắp xếp khi áp dụng theo cách này và sau đó chúng ta xáo trộn tất cả các danh sách với hạt giống này!
Joachim Sauer

1
@JoachimSauer: tốt, sắp xếp không phải là một phần của vấn đề. Mặc dù đó là một câu hỏi thú vị cho dù có một cách có hệ thống để tìm một hạt giống như vậy cho một RNG nhất định.
Michael Borgwardt

2
@MichaelBorgwardt một khi bạn nhận được hơn 17 câu hỏi, bạn chỉ đơn giản là không thể diễn tả số lượng xáo trộn có thể có trong 48 bit mà java sử dụng ngẫu nhiên (log_2 (17!) = 48,33)
ratchet freak

@ratchetfreak: không có vẻ như là một vấn đề thực sự với tôi. Và thay vào đó, việc sử dụng SecureRandom là không quan trọng nếu bạn phải.
Michael Borgwardt

4
@Telastyn: danh sách các chỉ mục là IMO một lớp không xác định làm cho giải pháp của bạn phức tạp hơn về mặt khái niệm và việc nó có hiệu suất nhiều hay ít phụ thuộc vào tần suất các danh sách được truy cập sau khi xáo trộn. Nhưng sự khác biệt về hiệu suất sẽ không đáng kể với các kích thước thực tế cho một câu đố được trả lời bởi con người.
Michael Borgwardt

3

Tạo một lớp Question2:

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

Sau đó tạo một hàm ánh xạ a questionđến ArrayList<Question2>, sử dụng Collection.Shufflecho kết quả đó và tạo một hàm thứ hai để ánh xạ ArrayList<Question2>trở lại question.

Sau đó, hãy đến nhóm của bạn và cố gắng thuyết phục họ rằng sử dụng ArrayList<Question2>thay vì questionsẽ cải thiện mã của họ rất nhiều, vì nó sẽ giúp họ tiết kiệm rất nhiều chuyển đổi không cần thiết đó.


1
Đây là một ý tưởng tốt, nhưng chỉ sau khi cố gắng thay đổi thiết kế đã thất bại.
Sebastian Redl

@SebastianRedl: đôi khi dễ thuyết phục mọi người về một thiết kế tốt hơn khi chỉ cho họ thấy giải pháp trong mã.
Doc Brown

1

Câu trả lời ngây thơ và sai lầm ban đầu của tôi :

Chỉ cần tạo (ít nhất) nsố ngẫu nhiên và vật phẩm trao đổi n với vật phẩmi trong vòng lặp for cho mỗi danh sách bạn có.

Trong mã giả:

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

}

Cập nhật:

Cảm ơn the_lotus đã chỉ ra bài báo kinh dị mã hóa. Bây giờ tôi cảm thấy thông minh hơn nhiều :-) Dù sao thì Jeff Atwood cũng chỉ ra cách làm đúng, sử dụng thuật toán 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);
}

Sự khác biệt chính ở đây là mỗi phần tử chỉ được hoán đổi một lần.

Và trong khi các câu trả lời khác giải thích chính xác rằng mô hình đối tượng của bạn bị thiếu sót, bạn có thể không ở trong positioin để thay đổi nó. Vì vậy, thuật toán Fisher-Yates sẽ giải quyết vấn đề của bạn mà không thay đổi mô hình dữ liệu của bạn.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.