随机播放对象的有效方法


20

我正在为一些测验软件编写程序。我有一个问题类,其中包含用于问题,答案,选项,标记和否定标记的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;    
}

我想打乱所有问题,但要打乱所有问题,则所有对象都需要打乱。我本可以以这种方式解决这个问题:

首先,我将不会使用此设计,也不会使用String而不是ArrayList<String>类型作为实例变量,然后会使用该Collections.shuffle方法来随机播放对象。但是我的团队坚持采用这种设计。

现在,问题类别包含输入问题时不断增加的ArrayLists。现在如何洗牌?


30
我讨厌绝对地谈论,但是如果您的团队坚持这种设计,那么它们是错误的。告诉他们!告诉他们我是这么说的(我是在互联网上写的,所以我必须是对的)。
约阿希姆·绍尔

10
是的,告诉他们这里有很多人告诉您,这种设计是典型的初学者错误。
布朗

6
出于好奇:您的团队在此设计中看到了哪些优势
约阿希姆·绍尔

9
Java命名约定是CamelCase用于类名,而camelCase用于变量名。
图兰斯·科尔多瓦

我认为您需要就这个糟糕的设计决定与您的团队面对面。如果他们继续坚持下去,找出原因。如果只是固执己见,也许就开始考虑在不久的将来找到一支新球队。如果他们确实有这种结构的原因,则应根据其优点考虑这些原因。
Ben Lee 2013年

Answers:


95

您的团队遇到一个常见问题:对象拒绝

您可以尝试创建一个名为的类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);

39
它甚至不是对象拒绝,而是数据结构拒绝
jk。

22

你不知道 您创建另一个索引列表/队列,并对其进行混洗。然后,您遍历索引来驱动其他集合的“混洗”顺序。

甚至在您的情况中,事情分开了,单独的订购集合也提供了许多好处(并行性,重新安置原始集合时的速度很高,等等)。


10
我不愿意将其投票:如果此设计确实已修复,这是第二好的解决方案,但坚持认为该设计是如此错误,以至于我不想对此提出任何建议。(嗯,无论如何都被赞成;
约阿希姆·绍尔

3
@joachimSauer-我同意,但还有许多其他(不太令人反感的)场景,其中原始集合必须保持静态,而通过它们的路径需要变化。
Telastyn

4
是的我知道。对于这些情况,改组索引集合是正确的方法。我唯一担心的是,OP团队只会接受这个说法并说“足够好”,而无需重新查看其设计。
约阿希姆·绍尔

1
对于没有权限修改/重新编码基础集合类或结构的自由的情况,例如对于必须由OS维护的集合使用API​​的情况,此答案特别有用。对索引进行改组是一个很好的洞察力,即使它不像重做底层设计那样敏锐,也可以独立存在。
Hardmath

@Joachim Sauer:实际上,改组索引不一定是解决上述问题的最佳方法。请参阅我的答案。
Michael Borgwardt

16

我同意其他答案,即正确的解决方案是使用适当的对象模型。

但是,实际上很容易以相同的方式对多个列表进行混洗:

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

那是一种完美的方式!现在,对于“排序”情况,我们只需要找到一个以这种方式应用时会产生排序列表的种子,然后我们使用该种子将所有列表随机排序!
约阿希姆·绍尔

1
@JoachimSauer:嗯,排序不是问题的一部分。尽管对于给定的RNG是否有系统的方法来找到这样的种子,这是一个有趣的问题。
Michael Borgwardt

2
@MichaelBorgwardt一旦遇到了17个问题,您将无法在Java Random使用的48位中表达随机播放的数量(log_2(17!)= 48.33)
棘手怪胎

@ratchetfreak:对我来说听起来不像是一个真正的问题。而且,如果需要的话,使用SecureRandom也很简单。
Michael Borgwardt

4
@Telastyn:索引列表是IMO的间接层,从概念上讲使您的解决方案更加复杂,是否提高性能取决于列表在改组后被访问的频率。但是,给人以实际的测验答案大小,性能差异将是微不足道的。
Michael Borgwardt

3

创建一个类Question2

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

然后创建一个映射question到的函数ArrayList<Question2>Collection.Shuffle用于该结果,然后创建第二个函数映射ArrayList<Question2>question

然后,去您的团队,并说服他们使用a ArrayList<Question2>代替可以question大大改善他们的代码,因为这样做可以为他们节省很多不必要的转换。


1
这是一个好主意,但前提是先验更改设计的尝试失败了。
塞巴斯蒂安·雷德尔

@SebastianRedl:有时候,在代码中向人们展示解决方案时,更容易说服人们采用更好的设计。
布朗

1

我最初的天真和错误答案:

只需创建(至少)n随机数,然后在for循环中为您拥有的每个列表将项目n 与项目交换即可i

用伪代码:

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指出编码恐怖文章。现在,我变得更加聪明了:-)无论如何,Jeff Atwood还展示了如何使用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算法可以解决您的问题,而无需更改数据模型。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.