简体   繁体   English

如何随机组合2个数组的元素,同时确保在所有元素至少使用一次之前不要重用元素?

[英]How to randomly combine elements of 2 arrays while making sure to not reuse an element until all have been used at least once?

Essentially I'm writing a program that produces random poems out of an array of nouns and an array of adjectives. 本质上,我正在编写一个程序,该程序可以从名词数组和形容词数组中产生随机诗歌。

This is accomplished basically using this line 这基本上是使用此行完成的

String poem = adjectives[rand.nextInt(3)]+" "+ nouns[rand.nextInt(3)];

Simple enough, but I'm supposed to make sure that it doesn't reuse the same noun or adjective for the next poems until all of them have been used at least once already. 很简单,但是我要确保在下一首诗中,所有首字母都已被使用至少一次之前,它不会重复使用同一名词或形容词。 I'm not sure how to do that. 我不确定该怎么做。

Convert the arrays to list, so you can use Collections.shuffle to shuffle them. 将数组转换为列表,以便可以使用Collections.shuffle进行随机播放。 Once shuffled, you can then simply iterate over them. 改组后,您可以简单地遍历它们。 The values will be random order, and all words will be used exactly once. 这些值将是随机顺序,并且所有单词将仅使用一次。 When you reach the end of an array of words, sort it again, and start from the beginning. 当您到达单词数组的末尾时,再次对其进行排序,然后从头开始。

If a poem consists of 1 adjective + 1 noun as in your example, then the program could go something like this: 如果一首诗由您的示例中的1个形容词+ 1个名词组成,则该程序可能会如下所示:

List<String> adjectives = new ArrayList<>(Arrays.asList(adjectivesArr));
List<String> nouns = new ArrayList<>(Arrays.asList(nounsArr));
Collections.shuffle(adjectives);
Collections.shuffle(nouns);

int aindex = 0;
int nindex = 0;

for (int i = 0; i < 100; ++i) {
    String poem = adjectives.get(aindex++) + " " + nouns.get(nindex++);
    System.out.println(poem);

    if (aindex == adjectives.size()) {
        aindex = 0;
        Collections.shuffle(adjectives);
    }
    if (nindex == nouns.size()) {
        nindex = 0;
        Collections.shuffle(nouns);
    }
}

The program will work with other number of adjectives and nouns per poem too. 该程序也可以与每首诗使用其他数量的形容词和名词。

If you must use an array, you can implement your own shuffle method, for example using the Fisher-Yates shuffle algorithm: 如果必须使用数组,则可以实现自己的随机播放方法,例如使用Fisher-Yates随机播放算法:

private void shuffle(String[] strings) {
    Random random = new Random();
    for (int i = strings.length - 1; i > 0; i--) {
        int index = random.nextInt(i + 1);
        String temp = strings[i];
        strings[i] = strings[index];
        strings[index] = temp;
    }
}

And then rewrite with arrays in terms of this helper shuffle function: 然后根据此辅助shuffle功能用数组重写:

shuffle(adjectives);
shuffle(nouns);

int aindex = 0;
int nindex = 0;

for (int i = 0; i < 100; ++i) {
    String poem = adjectives[aindex++] + " " + nouns[nindex++];
    System.out.println(poem);

    if (aindex == adjectives.length) {
        aindex = 0;
        shuffle(adjectives);
    }
    if (nindex == nouns.length) {
        nindex = 0;
        shuffle(nouns);
    }
}

What you can do is make two more arrays, filled with boolean values, that correspond to the adjective and noun arrays. 您可以做的是再创建两个数组,用布尔值填充,它们分别对应于adjectivenoun数组。 You can do something like this 你可以做这样的事情

boolean adjectiveUsed = new boolean[adjective.length];
boolean nounUsed = new boolean[noun.length];
int adjIndex, nounIndex;

By default all of the elements are initialized to false . 默认情况下,所有元素都初始化为false You can then do this 然后,您可以执行此操作

adjIndex = rand.nextInt(3);
nounIndex = rand.nextInt(3);
while (adjectiveUsed[adjIndex])
  adjIndex = rand.nextInt(3);
while (nounUsed[nounIndex]);
  nounIndex = rand.nextInt(3);

Note, once all of the elements have been used, you must reset the boolean arrays to be filled with false again otherwise the while loops will run forever. 注意,一旦使用完所有元素,就必须重新设置布尔数组以再次用false填充,否则while循环将永远运行。

There are lots of good options for this. 为此有很多不错的选择。 One is to just have a list of the words in random order that get used one by one and are then refreshed when empty. 一种是只是随机列出一个单词列表,这些单词被一个一个地使用,然后在空时刷新。

private List<String> shuffledNouns = Collections.EMPTY_LIST;

private String getNoun() {
    assert nouns.length > 0;
    if (shuffledNouns.isEmpty()) {
        shuffledNouns = new ArrayList<>(Arrays.asList(nouns));
        Collections.shuffle(wordOrder);
    }
    return shuffledNouns.remove(0);
}

Best way to do this is to create a shuffled queue from each array, and then just start popping off the front of the queues to build your poems. 最好的方法是从每个数组创建一个随机排列的队列,然后从队列的最前面弹出来构建您的诗歌。 Once the queues are empty you just generate new shuffled queues and start over. 队列为空后,您只需生成新的改组队列并重新开始即可。 Here's a good shuffling algorithm: https://en.wikipedia.org/wiki/Fisher–Yates_shuffle 这是一个很好的改组算法: https : //en.wikipedia.org/wiki/Fisher–Yates_shuffle

Assuming you have the same number of noums and adjectives shuffle both arrays and then merge result. 假设您拥有相同数量的名词和形容词,将两个数组混洗,然后合并结果。 you can shuffle the arrays multiple times if you need (once you get to the end) 您可以根据需要对数组进行多次洗牌(一旦结束)

    shuffleArray(adjectives);
    shuffleArray(nouns);

    for(int i=0;i<3;i++) {
        String poem = adjectives[i] + " " + nouns[i];
    }

A simple method to shuffle the arrays: 一个简单的方法来随机排列数组:

static void shuffleArray( String[] data) {
    for (int i = data.length - 1; i > 0; i--) {
        int index = rnd.nextInt(i + 1);
        int aux = data[index];
        data[index] = data[i];
        data[i] = aux;
    }
}

This might be overkill for this specific problem but it's an interesting alternative in my opinion: 对于这个特定的问题,这可能是多余的,但是在我看来,这是一个有趣的选择:

You can use a linear congruential generator (LCG) to generate the random numbers instead of using rand.nextInt(3) . 您可以使用线性同余生成器(LCG)生成随机数,而不是使用rand.nextInt(3) An LCG gives you a pseudo-random sequence of numbers using this simple formula LCG使用此简单公式为您提供伪随机数字序列

nextNumber = (a * x + b) % m

Now comes the interesting part (which makes this work for your problem): The Hull-Dobell-Theorem states that if your parameters a , b and m fit the following set of rules, the generator will generate every number between 0 and m-1 exactly once before repeating. 现在介绍有趣的部分(这可以解决您的问题):Hull-Dobell定理指出,如果您的参数abm符合以下规则集,则生成器将生成介于0m-1之间的每个数字在重复之前只进行一次

The conditions are: 条件是:

  • m and the offset c are relatively prime m和偏移c是相对质数
  • a - 1 is divisible by all prime factors of m a-1可被m的所有素因子整除
  • a - 1 is divisible by 4 if m is divisible by 4 如果m可被4整除,则a-1可被4整除

This way you could generate your poems with exactly the same line of code as you currently have but instead just generate the array index with the LCG instead of rand.nextInt . 这样,您可以使用与当前完全相同的代码行生成诗歌,而仅使用LCG而不是rand.nextInt生成数组索引。 This also means that this solution will give you the best performance , since there is no sorting, shuffling or searching involved. 这也意味着该解决方案将为您提供最佳性能 ,因为不涉及排序,改组或搜索。

How about keeping two lists for the adjectives and nouns? 如何保留两个形容词和名词的清单? You can use Collections.shuffle() to order them randomly. 您可以使用Collections.shuffle()对其进行随机排序。

import java.util.*;

class PoemGen {
    static List<String> nouns = Arrays.asList("ball", "foobar", "dog");
    static List<String> adjectives = Arrays.asList("slippery", "undulating", "crunchy");

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
        System.out.println(String.format("\nPoem %d", i));
        generatePoem();
        }
    }

    private static void generatePoem() {
        Collections.shuffle(nouns);
        Collections.shuffle(adjectives);
        int nounIndex = nouns.size() - 1;
        int adjectiveIndex = adjectives.size() - 1;
        while (nounIndex >= 0 && adjectiveIndex >= 0) {
            final String poem = adjectives.get(adjectiveIndex--)+" "+ nouns.get(nounIndex--);
            System.out.println(poem);
        }
    }
}

Output: 输出:

Poem 0
crunchy dog
slippery ball
undulating foobar

Poem 1
undulating dog
crunchy ball
slippery foobar

Poem 2
slippery ball
crunchy dog
undulating foobar

Thanks for the responses everyone! 感谢大家的答复! This helped immeasurably. 这确实起到了不可估量的作用。 I am now officially traumatized by the sheer number of ways there are to solve even a simple problem. 现在,解决甚至是一个简单问题的方法之多,使我正式受了创伤。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 随机打印数组元素,直到已打印所有元素 - Printing array elements at random until all elements have been printed 安排好 arrays 后,如何使用 for 循环搜索元素? - How do I search for an element using for loop once the arrays have been arranged? 确保数组中的所有元素都不为零 - Making sure that all elements in array is not zero Android AsyncTask确保所有数据均已加载 - Android AsyncTask making sure all data has been loaded 如何针对已在ArrayList中随机生成的字符串元素验证用户输入? - How to validate user input against string elements that have been randomly generated in an ArrayList? 我有一个日期列表,我想成对使用,然后在pja查询中成对使用,直到所有日期都用完了为止 - i have a list of dates and i want to use then in pairs as from and to in a pja query until all dates have been used end in java 如何确保已接收所有数据包并将其存储在阵列中? - How can I make sure that all packets have been received and stored in an array? 如何在确保最后一个元素指向第一个元素的同时将其排入圆形链表? - How do I enqueue to a circular linked list while making sure that the last element points to the first? 如何在同一个数组中组合两个元素? - How to combine two elements in the same arrays? 是否可以确保在所有servlet过滤器都处理完毕之前将重定向延迟? - Is a redirect guaranteed to be delayed until all servlet filters have been processed?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM