给定数字n,请列出所有n位数字,以使每个数字都没有重复的数字

时间:2018-08-30 00:47:46

标签: java c algorithm combinations permutation

我正在尝试解决以下问题。给定整数n,列出所有n位数字,以使每个数字都没有重复的数字。

例如,如果n为4,则输出如下:

0123
0124
0125
...
9875
9876
Total number of 4-digit numbers is 5040

我目前的做法是蛮力。我可以生成所有n位数字,然后使用Set列出所有没有重复数字的数字。但是,我很确定有一种更快,更好和更优雅的方法来实现这一点。

我正在用Java编程,但是我可以用C读取源代码。

谢谢

4 个答案:

答案 0 :(得分:1)

在数学上,第一个数字具有 10 个选项,第二个具有 9 ,第三个具有 8 7 。因此, 10 * 9 * 8 * 7 = 5040

通过编程,您可以使用一些组合逻辑来生成它们。使用功能方法通常可以使代码更整洁。意思是递归建立一个新的字符串,而不是尝试使用StringBuilder或数组来不断修改您现有的字符串。

示例代码

以下代码将生成排列,而无需重复使用数字,无需任何额外的设置或映射/等。

public class LockerNumberNoRepeats {
    public static void main(String[] args) {
        System.out.println("Total combinations = " + permutations(4));
    }

    public static int permutations(int targetLength) {
        return permutations("", "0123456789", targetLength);
    }

    private static int permutations(String c, String r, int targetLength) {
        if (c.length() == targetLength) {
            System.out.println(c);
            return 1;
        }

        int sum = 0;
        for (int i = 0; i < r.length(); ++i) {
            sum += permutations(c + r.charAt(i), r.substring(0,i) + r.substring(i + 1), targetLength);
        }
        return sum;
    }
}

输出:

...
9875
9876
Total combinations = 5040

说明

从@Rick的评论中拉出它,因为它讲得很好,并有助于阐明解决方案。

  

因此,要解释这里发生的情况-递归一个带有三个参数的函数:我们已经使用过的数字列表(我们正在构建的字符串-c),我们尚未使用的数字列表(字符串r)和目标深度或长度。然后,当使用数字时,将其添加到c并从r中删除以进行后续的递归调用,因此您无需检查是否已使用该数字,因为您只需传入尚未使用的数字即可。 / p>

答案 1 :(得分:0)

很容易找到一个公式。即

如果n=110个变体。

如果n=29*10个变体。

如果n=38*9*10个变体。

如果n=47*8*9*10个变体。

答案 2 :(得分:0)

请注意此处的对称性:

0123
0124
...
9875
9876

9876 = 9999-123

9875 = 9999-124

因此对于初学者来说,您可以将工作切成两半。

您可能能够找到一个涵盖情况的正则表达式,例如,如果一个数字在同一字符串中出现两次,则它匹配/失败。

正则表达式是否会更快,谁知道?

特别是对于四位数,您可能嵌套了For循环:

for (int i = 0; i < 10; i++) {
   for (int j = 0; j < 10; j++) {
       if (j != i) {
           for (int k = 0; k < 10; k++) {
               if ((k != j) && (k != i)) {
                   for (int m = 0; m < 10; m++) {
                       if ((m != k) && (m != j) && (m != i)) {
                           someStringCollection.add((((("" + i) + j) + k) + m));

(等)

或者,对于更通用的解决方案,这是递归方便的好例子。例如。您有一个函数,该函数可以获取先前数字的列表以及所需的深度,并且如果所需数字的数量小于深度,则可以进行十次循环迭代(遍历要添加的数字的每个值),如果列表中不存在数字,然后将其添加到列表中并递归。如果深度正确,只需将列表中的所有数字连接起来,然后将其添加到您拥有的有效字符串中即可。

答案 3 :(得分:0)

回溯方法也是一种蛮力方法。

private static int pickAndSet(byte[] used, int last) {
    if (last >= 0) used[last] = 0;
    int start = (last < 0) ? 0 : last + 1;
    for (int i = start; i < used.length; i++) {
        if (used[i] == 0) {
            used[i] = 1;
            return i;
        }
    }
    return -1;
}

public static int get_series(int n) {
    if (n < 1 || n > 10) return 0;
    byte[] used = new byte[10];
    int[] result = new int[n];

    char[] output = new char[n];

    int idx = 0;
    boolean dirForward = true;
    int count = 0;
    while (true) {
        result[idx] = pickAndSet(used, dirForward ? -1 : result[idx]);
    if (result[idx] < 0) {  //fail, should rewind.
      if (idx == 0) break;      //the zero index rewind failed, think all over.

      dirForward = false;
      idx --;
      continue;
    } else {//forward.
        dirForward = true;
    }

    idx ++;
    if (n == idx) {
        for (int k = 0; k < result.length; k++) output[k] = (char)('0' + result[k]);
        System.out.println(output);
        count ++;
        dirForward = false;
        idx --;
    }
    }
    return count;
}