Здесь много длинных фрагментов кода. Лучше думать больше и меньше кода.
Мы хотели бы генерировать каждую возможность ровно один раз без каких-либо затрат. Оказывается, это возможно только при постоянном количестве усилий на каждую цифру.
Как бы вы это сделали без кода? Получите 10 карт и напишите цифры от 0 до 9. Нарисуйте строку из 9 квадратов на столе. Выберите карту. Поместите его в первый квадрат, другой во второй и т. Д. Когда вы выбрали 9, у вас есть свой первый номер. Теперь удалите последнюю карту и замените ее каждой возможной альтернативой. (В этом случае всего 1). Каждый раз, когда все квадраты заполняются, у вас есть еще один номер. Когда вы сделали все альтернативы для последнего квадрата, сделайте это для последнего 2. Повторите с последними 3 и т. Д., Пока вы не рассмотрите все альтернативы для всех ящиков.
Написание сжатой программы для этого - выбор простых структур данных. Используйте массив символов для строки из 9 квадратных.
Используйте другой массив для набора карт. Чтобы удалить элемент из набора размера N, хранящегося в массиве A [0 .. N-1], мы используем старый трюк. Скажем, элемент, который вы хотите удалить, - A [I]. Сохраните значение A [I] в сторону. Затем скопируйте последний элемент A [N-1] «вниз», переписывая A [I]. Новый набор A [0 .. N-2]. Это прекрасно работает, потому что нас не интересует порядок в наборе.
Остальное - использовать рекурсивное мышление для перечисления всех возможных альтернатив. Если я знаю, как найти все варианты из набора символов размера M в строку размера N, то для получения алгоритма просто выберите каждый возможный символ для первой позиции строки, затем повторите выбор, чтобы выбрать остальную часть N-1 символов из оставшегося набора размеров M-1. Мы получаем хорошую 12-строчную функцию:
#include <stdio.h>
// Select each element from the given set into buf[pos], then recur
// to select the rest into pos+1... until the buffer is full, when
// we print it.
void select(char *buf, int pos, int len, char *set, int n_elts) {
if (pos >= len)
printf("%.*s\n", len, buf); // print the full buffer
else
for (int i = 0; i < n_elts; i++) {
buf[pos] = set[i]; // select set[i] into buf[pos]
set[i] = set[n_elts - 1]; // remove set[i] from the set
select(buf, pos + 1, len, set, n_elts - 1); // recur to pick the rest
set[n_elts - 1] = set[i]; // undo for next iteration
set[i] = buf[pos];
}
}
int main(void) {
char buf[9], set[] = "0123456789";
select(buf, 0, 9, set, 10); // select 9 characters from a set of 10
return 0;
}
Вы не упомянули, нормально ли положить ноль в первую позицию. Предположим, что это не так. Поскольку мы хорошо понимаем алгоритм, легко избежать выбора нуля в первую позицию. Просто пропустите эту возможность, заметив, что !pos
в C имеет значение 1, если pos равно 0 и 0. Если вам не нравится эта слегка неясная идиома, попробуйте (pos == 0 ? 1 : 0)
как более читаемую замену:
#include <stdio.h>
void select(char *buf, int pos, int len, char *set, int n_elts) {
if (pos >= len)
printf("%.*s\n", len, buf);
else
for (int i = !pos; i < n_elts; i++) {
buf[pos] = set[i];
set[i] = set[n_elts - 1];
select(buf, pos + 1, len, set, n_elts - 1);
set[n_elts - 1] = set[i];
set[i] = buf[pos];
}
}
int main(void) {
char buf[9], set[] = "0123456789";
select(buf, 0, 9, set, 10);
return 0;
}