Учитывая число n, перечислите все n-значные числа так, чтобы у каждого номера не было повторяющихся цифр. - PullRequest
0 голосов
/ 30 августа 2018

Я пытаюсь решить следующую проблему. Получив целое число n, перечислите все n-значные числа, чтобы у каждого номера не было повторяющихся цифр.

Например, если n равно 4, то вывод будет следующим:

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

Мой нынешний подход - грубая сила. Я могу сгенерировать все n-значные числа, затем, используя Set, перечислить все числа без повторяющихся цифр. Тем не менее, я уверен, что есть более быстрый, лучший и элегантный способ сделать это.

Я программирую на Java, но могу читать исходный код на C.

Спасибо

Ответы [ 4 ]

0 голосов
/ 30 августа 2018

Метод возврата также является методом грубой силы.

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;
}
0 голосов
/ 30 августа 2018

Математически у вас есть варианты 10 для первого числа, 9 для второго, 8 для третьего и 7 за 4-е. Итак, 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 для последующих рекурсивных вызовов, поэтому вам не нужно проверять, используется ли она уже, потому что вы передаете только те, которые еще не использовались.

0 голосов
/ 30 августа 2018

Обратите внимание на симметрию здесь:

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));
* * 1016 (и т.д.)

В качестве альтернативы, для более обобщенного решения это хороший пример удобной природы рекурсии. Например. у вас есть функция, которая берет список предыдущих цифр и требуемую глубину, и если количество требуемых цифр меньше, чем глубина, просто сделайте цикл из десяти итераций (через каждое значение для добавляемой вами цифры), если цифра в списке уже не существует, затем добавьте ее в список и введите. Если вы находитесь на правильной глубине, просто объедините все цифры в списке и добавьте их в коллекцию допустимых строк.

0 голосов
/ 30 августа 2018

легко найти формулу. т.е.

, если n=1, есть 10 варианты.

если n=2 есть 9*10 вариантов.

если n=3 есть 8*9*10 вариантов.

если n=4 есть 7*8*9*10 вариантов.

...