Java中嵌套循环的替代方法?

iqxoj9l9  于 2023-09-29  发布在  Java
关注(0)|答案(4)|浏览(91)

我想创建一个简单的计算组合。

var chars = new char[]{'a', 'b', 'c', 'd'};
saveVariation(chars, 4);

public static void saveVariation(char characters[], int number_of_characters) {
    PrintWriter writer = null;
    try {
        writer = new PrintWriter(new File("variation.txt"));
    } catch (FileNotFoundException e) {

    }
    var length = characters.length;

    switch (number_of_characters) {
        case 2 -> {
            for (var i = 0; i < length; i += 1) {
                for (var j = 0; j < length; j += 1) {
                    writer.println(String.valueOf(characters[i]) + String.valueOf(characters[j]));
                }
            }
        }
        case 3 -> {
            for (var i = 0; i < length; i += 1) {
                for (var j = 0; j < length; j += 1) {
                    for (var k = 0; k < length; k += 1) {
                        writer.println(String.valueOf(characters[i]) + String.valueOf(characters[j]) + String.valueOf(characters[k]));
                    }
                }
            }
        }
        case 4 -> {
            for (var i = 0; i < length; i += 1) {
                for (var j = 0; j < length; j += 1) {
                    for (var k = 0; k < length; k += 1) {
                        for (var l = 0; l < length; l += 1) {
                            writer.println(String.valueOf(characters[i]) + String.valueOf(characters[j]) + String.valueOf(characters[k]) + String.valueOf(characters[l]));
                        }
                    }
                }
            }
        }
    }
    writer.close();
}

如上所述,嵌套循环可以用递归代替,但有一个主要问题。
如果变量数量太多,即使在增加变量后也会出现内存溢出(更多信息请参见此处),因此我将其保存到文件variation.txt中。
所以我需要修改递归来保存到一个文件中(我不知 prop 体如何操作)。而且,我甚至不知道如何确切地使用该文件。问题中的代码可以工作,但它非常复杂。
请帮

mlmc2os5

mlmc2os51#

var chars = new char[] {'a', 'b', 'c'};
int variations = 3;
    
var length = chars.length;
var list = new ArrayList<String>();
StringBuilder out = new StringBuilder();

for (int i = 0; i < variations; i++) out.append(chars[0]);

top:
while (true) {
  list.add(out.toString());
  for (int pos = variations - 1; pos >= 0; pos--) {
    char c = out.charAt(pos);
    int cPos = Arrays.binarySearch(chars, c);
    if (cPos < length -1) {
      // We can just increment this one and we're done
      out.setCharAt(pos, chars[cPos + 1]);
      continue top;
    } else {
      // 9-> 10: Every 'lower' digit is reset to first (0).
      out.setCharAt(pos, chars[0]);
    }
  }
  break;
}

System.out.println(list);

打印:

[aaa, aab, aac, aba, abb, abc, aca, acb, acc,
 baa, bab, bac, bba, bbb, bbc, bca, bcb, bcc,
 caa, cab, cac, cba, cbb, cbc, cca, ccb, ccc]

这样做的方式是考虑,从概念上讲,你想要的是相同的阿拉伯数字系统的工作方式。我们如何计数。0,1,2,3,4,.,998,999的序列是什么,而不是你在这里做的?
因此,算法是清楚的:

  • 我们从头开始:00000在数字中,或者在这个系统中,aaaaaa-每个位置的第一个字符。我们称之为“当前价值”。
  • 然后我们添加当前值,然后用它的增量替换当前值。
  • 为了增加“当前值”,我们将其中最右边的字符替换为下一个字符。这个管用。直到最右边的字符与字符集中的最后一个字符具有相同的值。然后,就像'99'变成'100'一样,我们将这个最右边的字符设置回集合中的 * 第一个 * 字符,我们还没有完成递增-我们现在需要递增倒数第二个字符。那个也可能在最后的“数字”(数字中的9,代码片段中的“c”),在这种情况下,它也必须重置为“a”,我们需要继续我们的递增算法。
  • 如果这个递增算法遍历了整个字符串,我们就中断,因为这意味着我们完成了:如果字符串是ccccccc,我们可以遍历整个字符串的唯一方法-每个位置都有最后一位数字。

注意:我使用binarySearch来查找'char'的索引,这只在chars排序时有效。如果不是,你必须写一个快速的小'find index'方法,或者(如果chars非常大,超过500个左右的字符,没有排序,那么每次查看它都很慢,只有这样才能使用这个替代方法):不是在StringBuilder中构建字符,而是构建一个包含索引而不是值的数组。现在不需要查找chars数组中数字'B'出现的位置。然后添加到列表中的代码(这里只是list.add(out.toString()))变得更加复杂,但总体上这在算法上会快一两个数量级。

a11xaf1n

a11xaf1n2#

采用Diego Borba的想法,您可以按如下方式实现它。请注意,以下代码未经测试:

String[] array = new String[]{"a", "b", "c", "d"};

public static List<String> create(int maxLength, String[] array){
    List<String> strings = new ArrayList<>();
    strings.addAll(create("",0, maxLength, array));
    return strings;
}

private static List<String> create(String current, int currentLength, int maxLength, String[] array){
    List<String> strings = new ArrayList<>();
    if(currentLength < maxLength){
        for(int x=0;x<array.length;x++){
           strings.addAll(create(current + array[x],currentLength + 1, maxLength, array));
        }
    }else{
        strings.add(current);
    }
    return strings;
}

代码为每个字母a、B、c和d向空的开始字符串添加一个字符。然后将下一个a、B、c或d添加到已经创建的字符串a、b、c和d中,依此类推。
如果字符串的当前长度小于预期长度,则继续添加字符。否则它会停止并返回列表。最后一步是将所有创建的String添加到String列表中,并将其作为结果返回。

huwehgph

huwehgph3#

我会使用一个循环来 * 遍历(或 * 模拟 *)内部循环,并使用一个数组来保存这个内部循环的索引:

static void saveVariation(BufferedWriter output, int count, int... characters) throws IOException {
    var index = new int[count];
    var length = characters.length;
    while (true) {
        // create and output one word using the indices in array
        for (var i = 0; i < count; i++) {
            output.write(Character.toChars((characters[index[i]])));
        }
        output.newLine();

        // increment the index/indices (last to first)
        for (var i = count-1; i >= 0; i--) {
            if (++index[i] < length) {
                break;
            } else {
                index[i] = 0;
                if (i == 0) {
                    return;
                }
            }
        }
    }
}

此代码接收int而不是char的数组(varargs),因此它可以与任何Unicode代码点一起工作,而不仅仅是可以由char表示的第一个65536。
该方法可以像这样调用

saveVariation(writer, 4, 'a', 'b', 'c', 'd');

应该很容易将int...更改为String...,以用于更复杂的用例。

6tr1vspr

6tr1vspr4#

正如Diego Borba指出的,递归在这里很有用。

// add the next character to the string, or the string to the list
public void variations(List<String> list, int remaining, String soFar, int... codePoints) {
    if (remaining == 0) {
        list.add(soFar);
    } else {
        for (int i = 0; i < codePoints.length; ++i) {
            variations(list, remaining - 1,
                soFar + Character.toString(codePoints[i]), codePoints);
        }
    }
}

// recursively generate all possible combinations
public List<String> getVariation(int number, int... codePoints) {
    List<String> list = new ArrayList<>();
    variations(list, number, "", codePoints);
    return list;
}

使用getVariation(3, "abc".codePoints().toArray())调用它,它将返回:

[aaa, aab, aac, aba, abb, abc, aca, acb, acc,
 baa, bab, bac, bba, bbb, bbc, bca, bcb, bcc,
 caa, cab, cac, cba, cbb, cbc, cca, ccb, ccc]

正如Basil Bourque所指出的,使用整数码位比使用char更好,这就是为什么我用"abc".codePoints().toArray()来调用它,但如果你喜欢,你也可以用getVariation(4, 'a', 'b', 'c', 'd')来调用它。
如果你想将输出发送到一个文件,使用这个:

// add the next character to the string, or write the string to the file
public void variations(PrintWriter writer, int remaining, String soFar, int... codePoints) {
    if (remaining == 0) {
        writer.println(soFar);
    } else {
        for (int i = 0; i < codePoints.length; ++i) {
            variations(writer, remaining - 1,
                soFar + Character.toString(codePoints[i]), codePoints);
        }
    }
}

// recursively generate all possible combinations
public void getVariation(String filename, int number, int... codePoints) throws IOException {
    try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(Paths.get(filename)), UTF_8)) {
        variations(writer, number, "", codePoints);
    }
}

相关问题