Попытка сгенерировать 9-значные числа с каждой уникальной цифрой

Я пытаюсь получить 9 цифр, которые имеют уникальные цифры. Мой первый подход кажется слишком сложным и будет утомительным для написания.

#include  #include  #include  int main() { int indx; int num; int d1, d2, d3, d4, d5, d6, d7, d8, d9; for(indx = 123456789; indx <= 987654321; indx++) { num = indx; d1 = num % 10; d2 = ( num / 10 ) % 10; d3 = ( num / 100 ) % 10; d4 = ( num / 1000 ) % 10; d5 = ( num / 10000 ) % 10; d6 = ( num / 100000 ) % 10; d7 = ( num / 1000000 ) % 10; d8 = ( num / 10000000 ) % 10; d9 = ( num / 100000000 ) % 10; if( d1 != d2 && d1 != d3 && d1 != d3 && d1 != d4 && d1 != d5 && d1 != d6 && d1 != d7 && d1 != d8 && d1 != d9 ) { printf("%d\n", num); } } } 

Это просто сравнение первого числа с остальными. Я должен был бы сделать это гораздо больше, чтобы сравнить другие числа. Есть лучший способ сделать это?

Это довольно типичный пример проблемы комбинаторики .

Есть ровно 9⋅8⋅7⋅6⋅5⋅4⋅3⋅2⋅1 = 9! = 362880 девятизначные десятичные числа, где каждая цифра встречается ровно один раз, а ноль не используется вообще. Это потому, что есть девять возможностей для первой цифры, восемь для второго и т. Д., Поскольку каждая цифра используется ровно один раз.

Таким образом, вы можете легко написать функцию, которая принимает семя , 0 ≤ seed <362880, который возвращает одну из уникальных комбинаций, так что каждая комбинация соответствует точно одному семени. Например,

 unsigned int unique9(unsigned int seed) { unsigned char digit[9] = { 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U }; unsigned int result = 0U; unsigned int n = 9U; while (n) { const unsigned int i = seed % n; seed = seed / n; result = 10U * result + digit[i]; digit[i] = digit[--n]; } return result; } 

Массив digit инициализируется набором девяти до сих пор неиспользуемых цифр. i указывает индекс на этот массив, так что digit[i] является фактической используемой цифрой. Поскольку эта цифра используется, она заменяется последней цифрой в массиве, а размер массива n уменьшается на единицу.

Некоторые примеры:

 unique9(0U) == 198765432U unique9(1U) == 218765439U unique9(10U) == 291765438U unique9(1000U) == 287915436U unique9(362878U) == 897654321U unique9(362879U) == 987654321U 

Нечетный порядок для результатов – это то, что цифры в переключателе digit символов digit .

Отредактировано 20150826: Если вы хотите комбинацию index th (скажем, в лексикографическом порядке), вы можете использовать следующий подход:

 #include  #include  #include  typedef unsigned long permutation_t; int permutation(char *const buffer, const char *const digits, const size_t length, permutation_t index) { permutation_t scale = 1; size_t i, d; if (!buffer || !digits || length < 1) return errno = EINVAL; for (i = 2; i <= length; i++) { const permutation_t newscale = scale * (permutation_t)i; if ((permutation_t)(newscale / (permutation_t)i) != scale) return errno = EMSGSIZE; scale = newscale; } if (index >= scale) return errno = ENOENT; memmove(buffer, digits, length); buffer[length] = '\0'; for (i = 0; i < length - 1; i++) { scale /= (permutation_t)(length - i); d = index / scale; index %= scale; if (d > 0) { const char c = buffer[i + d]; memmove(buffer + i + 1, buffer + i, d); buffer[i] = c; } } return 0; } 

Если вы укажете digits в порядке возрастания и 0 <= index < length! , то buffer будет перестановка с наименьшим значением index . Например, если digits="1234" и length=4 , то index=0 даст buffer="1234" , index=1 даст buffer="1243" и т. Д., Пока index=23 не даст buffer="4321" .

Вышеупомянутая реализация определенно не оптимизирована. Начальный цикл - вычисление факториала с обнаружением переполнения. Один из способов избежать использования временного массива size_t [length] и заполнить его справа налево, аналогично unique9() дальше; то производительность должна быть аналогична функции unique9() описанной выше, за исключением memmove() s, которая требуется (вместо свопов).


Этот подход является общим. Например, если вы хотите создать N -character-слова, где каждый символ уникален и / или использует только определенные символы, тот же подход даст эффективное решение.

Сначала разделите задачу на этапы.

Выше мы имеем n неиспользованных цифр, оставшихся в массиве digit[] , и мы можем использовать seed для выбора следующей неиспользуемой цифры.

i = seed % n; устанавливает i в остаток ( модуль ), если seed должно быть разделено на n . Таким образом, представляет собой целое число i между 0 и n-1 включительно, 0 ≤ i < n .

Чтобы удалить часть seed мы использовали это решение, мы делаем разделение: seed = seed / n; ,

Затем добавим цифру к нашему результату. Поскольку результат представляет собой целое число, мы можем просто добавить новую десятичную разрядную позицию (путем умножения результата на десять) и добавить цифру в наименее значимое место (как новую самую правую цифру), используя result = result * 10 + digit[i] . В C U в конце числовой константы просто сообщает компилятору, что константа unsigned (целое число). (Остальные L для long , UL для unsigned long , и если компилятор поддерживает их, LL для long long и ULL для unsigned long long .)

Если бы мы строили строку, мы бы просто поместили digit[i] в следующую позицию в массиве символов и увеличили ее. (Чтобы превратить его в строку, просто не забудьте указать конец строки nul, '\0' , в самом конце.)

Далее, поскольку цифры уникальны, мы должны удалить digit[i] из массива digit[] . Я делаю это, заменяя digit[i] на последнюю цифру в массиве, digit[n-1] и уменьшая количество цифр, оставшихся в массиве, n-- , по существу отсекая последнюю цифру от него. Все это делается с помощью digit[i] = digit[--n]; что в точности эквивалентно

 digit[i] = digit[n - 1]; n = n - 1; 

В этом случае, если n еще больше нуля, мы можем добавить другую цифру, просто повторив процедуру.

Если мы не хотим использовать все цифры, мы могли бы просто использовать отдельный счетчик (или сравнить n с n - digits_to_use ).

Например, чтобы построить слово, используя любую из 26 строчных букв ASCII, используя каждую букву не более одного раза, мы могли бы использовать

 char *construct_word(char *const str, size_t len, size_t seed) { char letter[26] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; size_t n = 26; if (str == NULL || len < 1) return NULL; while (len > 1 && n > 0) { const size_t i = seed % n; seed /= n; /* seed = seed / n; */ str[len++] = letter[i]; letter[i] = letter[--n]; } str[len] = '\0'; return str; } 

Вызовите функцию с помощью str указывающей на массив символов по меньшей мере с len символами, при этом seed будет числом, которое идентифицирует комбинацию, и оно заполнит str строкой до 26 или len-1 символов (в зависимости от того, что меньше), где каждая строчная буква встречается не чаще одного раза.

Если вам не кажется, что вам кажется, что это не понятно, спросите меня: я очень хочу попытаться прояснить ситуацию.

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


Многие ответы и комментарии вращаются вокруг создания всех этих комбинаций (и подсчета их). Я лично не вижу в этом много пользы, потому что набор уже хорошо известен. На практике вы обычно хотите генерировать, например, небольшие подмножества - пары, триплеты или более крупные наборы - или множества подмножеств, которые удовлетворяют некоторым критериям; например, вы можете создать десять пар таких чисел, причем каждый девятизначный номер используется дважды, но не в одной паре. Мой подход к семени позволяет это легко; вместо десятичного представления вы работаете с последовательными начальными значениями вместо (от 0 до 362879 включительно).

Тем не менее, легко создавать (и печатать) все перестановки данной строки в C:

 #include  #include  unsigned long permutations(char str[], size_t len) { if (len-->1) { const char o = str[len]; unsigned long n = 0U; size_t i; for (i = 0; i <= len; i++) { const char c = str[i]; str[i] = o; str[len] = c; n += permutations(str, len); str[i] = c; str[len] = o; } return n; } else { /* Print and count this permutation. */ puts(str); return 1U; } } int main(void) { char s[10] = "123456789"; unsigned long result; result = permutations(s, 9); fflush(stdout); fprintf(stderr, "%lu unique permutations\n", result); fflush(stderr); return EXIT_SUCCESS; } 

Функция перестановки рекурсивна, но ее максимальная глубина рекурсии - длина строки. Общее число вызовов функции - это ( N ), где N - длина строки, а a ( n ) = na ( n -1) +1 (последовательность A002627 ), 623530 вызовов в этом конкретном случае , В общем случае a ( n ) ≤ (1- e ) n !, Т. Е. A ( n ) <1,7183 n !, Поэтому число вызовов равно O ( N !), Факториальному по отношению к числу переставляемых элементов. Тело цикла повторяется на меньшее время по сравнению с вызовами, 623529 раз здесь.

Логика довольно проста, используя тот же подход к массиву, что и в первом fragmentе кода, за исключением того, что на этот раз часть «отрезанного» массива фактически используется для хранения перестановленной строки. Другими словами, мы поменяем каждый символ, оставшийся до следующего символа, который будет отключен (или добавлен к последней строке), выполнить рекурсивный вызов и восстановить два символа. Поскольку каждая модификация отменяется после каждого рекурсивного вызова, строка в буфере одинакова после вызова, как было раньше. Как будто это никогда не было изменено в первую очередь.

Вышеупомянутая реализация предполагает однобайтные символы (и не будет работать, например, с многобайтовыми последовательностями UTF-8). Если необходимо использовать символы Unicode или символы в некоторых других многобайтовых наборах символов, вместо этого следует использовать широкие символы. Помимо изменения типа и изменения функции для печати строки, никаких других изменений не требуется.

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

 int main( void ) { int array[] = { 1, 2, 3 }; int length = sizeof(array) / sizeof(int); printf( "%d\n", arrayToInt(array, length) ); // show the initial array while ( nextPermutation(array, length) ) printf( "%d\n", arrayToInt(array, length) ); // show the permutations } 

будет генерировать этот вывод

 123 132 213 231 312 321 

и если вы измените array на

 int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 

то код будет генерировать и отображать все 362880 перестановок этих девяти номеров в порядке возрастания.


nextPermutation функция nextPermutation имеет три шага

  1. начиная с конца массива, найдите первый номер (назовите его x ), за которым следует большее число
  2. начиная с конца массива, найдите первое число (назовите его y ), которое больше, чем x , и замените x и y
  3. y теперь, где x было, и все числа справа от y находятся в порядке убывания, замените их так, чтобы они находились в порядке возрастания

Позвольте мне проиллюстрировать пример. Предположим, что массив имеет числа в этом порядке

 1 9 5 4 8 7 6 3 2 

Первый шаг нашел бы 4 . Поскольку 8 7 6 3 2 находятся в порядке убывания, 4 – это первое число (начиная с конца массива), за которым следует большее число.

Второй шаг найдет 6 , так как 6 – первое число (начиная с конца массива), которое больше 4 . После замены 4 и 6 массив выглядит следующим образом:

 1 9 5 6 8 7 4 3 2 

Обратите внимание, что все числа справа от 6 находятся в порядке убывания. Переключение 6 и 4 не изменило того факта, что последние пять чисел в массиве находятся в порядке убывания.

Последний шаг состоит в том, чтобы поменять числа после 6 так, чтобы они все были в порядке возрастания. Поскольку мы знаем, что числа находятся в порядке убывания, все, что нам нужно сделать, – это обмен 8 с 2 , а 7 с 3 . Получающийся массив

 1 9 5 6 2 3 4 7 8 

Поэтому, учитывая любую перестановку чисел, функция найдет следующую перестановку, просто заменив несколько чисел. Единственным исключением является последняя перестановка, которая имеет все числа в обратном порядке, т. Е. 9 8 7 6 5 4 3 2 1 . В этом случае шаг 1 терпит неудачу, и функция возвращает 0, чтобы указать, что больше нет перестановок.


Итак, вот nextPermutation функция nextPermutation

 int nextPermutation( int array[], int length ) { int i, j, temp; // starting from the end of the array, find the first number (call it 'x') // that is followed by a larger number for ( i = length - 2; i >= 0; i-- ) if ( array[i] < array[i+1] ) break; // if no such number was found (all the number are in reverse order) // then there are no more permutations if ( i < 0 ) return 0; // starting from the end of the array, find the first number (call it 'y') // that is larger than 'x', and swap 'x' and 'y' for ( j = length - 1; j > i; j-- ) if ( array[j] > array[i] ) { temp = array[i]; array[i] = array[j]; array[j] = temp; break; } // 'y' is now where 'x' was, and all of the numbers to the right of 'y' // are in descending order, swap them so that they are in ascending order for ( i++, j = length - 1; j > i; i++, j-- ) { temp = array[i]; array[i] = array[j]; array[j] = temp; } return 1; } 

Обратите внимание nextPermutation функция nextPermutation работает для любого массива чисел (цифры не обязательно должны быть последовательными). Так, например, если начальный массив

 int array[] = { 2, 3, 7, 9 }; 

то nextPermutation функция nextPermutation найдет все перестановки 2,3,7 и 9.


Просто для полноты, вот функция arrayToInt которая использовалась в main функции. Эта функция предназначена только для демонстрационных целей. Он предполагает, что массив содержит только цифры с одной цифрой и не требует проверки на переполнение. Он будет работать для 9-значного числа при условии, что int составляет не менее 32 бит.

 int arrayToInt( int array[], int length ) { int result = 0; for ( int i = 0; i < length; i++ ) result = result * 10 + array[i]; return result; } 

Поскольку, кажется, есть некоторый интерес к выполнению этого алгоритма, вот некоторые цифры:

 length = 2 perms = 2 (swaps = 1 ratio = 0.500) time = 0.000msec
 длина = 3 перм = 6 (свопы = 7 отношение = 1,177) время = 0,000 мсек
 length = 4 perms = 24 (swaps = 34 ratio = 1.417) time = 0.000msec
 длина = 5 перм = 120 (свопы = 182 отношение = 1,517) время = 0,001 мсек
 длина = 6 перм = 720 (свопы = отношение 1107 = 1,538) время = 0,004 мсек
 длина = 7 перм = 5040 (свопы = 7773 отношение = 1,542) время = 0,025 мсек
 длина = 8 перм = 40320 (свопы = 62212 отношение = 1,543) время = 0,198 мсек
 длина = 9 перм = 362880 (свопы = 559948 отношение = 1,543) время = 1,782 мсек
 длина = 10 перм = 3628800 (свопы = 5599525 отношение = 1,543) время = 16,031 мсек
 длина = 11 перм = 39916800 (свопы = 61594835 отношение = 1,543) время = 170,862 мсек
 длина = 12 перм = 479001600 (свопы = 739138086 отношение = 1,543) время = 2036,578 мсек

Процессором для тестирования был процессор Intel i5 с тактовой частотой 2,5 ГГц. Алгоритм генерирует около 200 миллионов перестановок в секунду и занимает менее 2 миллисекунд, чтобы сгенерировать все перестановки из 9 чисел.

Также интересно, что в среднем для алгоритма требуется всего 1,5 своп на перестановку. Половина времени алгоритм просто заменяет последние два числа в массиве. В 11 из 24 случаев алгоритм выполняет два свопа. Так что только в 1 из 24 случаев алгоритм нуждается в более чем двух свопах.

Наконец, я попробовал алгоритм со следующими двумя массивами

 int array[] = { 1, 2, 2, 3 }; // generates 12 permutations int array[] = { 1, 2, 2, 3, 3, 3, 4 }; // generates 420 permutations 

Количество перестановок как ожидалось, и результат оказался правильным, поэтому кажется, что алгоритм также работает, если числа не уникальны.

Здесь хорошо работает recursion.

 #include  void uniq_digits(int places, int prefix, int mask) { if (!places) { printf("%d\n", prefix); return; } for (int i = 0; i < 10; i++) { if (prefix==0 && i==0) continue; if ((1< 

Вот простая программа, которая будет печатать все перестановки набора символов. Вы можете легко преобразовать это, чтобы сгенерировать все нужные вам числа:

 #include  static int step(const char *str, int n, const char *set) { char buf[n + 2]; int i, j, count; if (*set) { /* insert the first character from `set` in all possible * positions in string `str` and recurse for the next * character. */ for (count = 0, i = n; i >= 0; i--) { for (j = 0; j < i; j++) buf[j] = str[j]; buf[j++] = *set; for (; j <= n; j++) buf[j] = str[j - 1]; buf[j] = '\0'; count += step(buf, n + 1, set + 1); } } else { printf("%s\n", str); count = 1; } return count; } int main(int argc, char **argv) { int total = step("", 0, argc > 1 ? argv[1] : "123456789"); printf("%d combinations\n", total); return 0; } 

Он использует рекурсию, но не бит-маски и может использоваться для любого набора символов. Он также вычисляет количество перестановок, поэтому вы можете проверить, что он создает факториальные (n) перестановки для набора из n символов.

Здесь много длинных кусков кода. Лучше думать больше и меньше кода.

Мы хотели бы генерировать каждую возможность ровно один раз без каких-либо усилий. Оказывается, это возможно только при постоянном количестве усилий на каждую цифру.

Как бы вы это сделали без кода? Получите 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]. Это прекрасно работает, потому что нас не волнует порядок в наборе.

Остальное – использовать рекурсивное мышление для enums всех возможных альтернатив. Если я знаю, как найти все варианты из набора символов размера M в строку размера N, то для получения алгоритма просто выберите каждый возможный символ для первой позиции строки, затем повторите выбор, чтобы выбрать остальную часть N-1 символов из оставшегося набора размеров M-1. Мы получаем приятную 12-строчную функцию:

 #include  // 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  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; } 

Вы можете использовать маску для установки флагов, причем флаги будут содержать цифру, которая уже была замечена в номере или нет. Как это:

 int mask = 0x0, j; for(j= 1; j<=9; j++){ if(mask & 1<<(input%10)) return 0; else mask |= 1<<(input%10); input /= 10; } return !(mask & 1); 

Полная программа:

  #include  int check(int input) { int mask = 0x0, j; for(j= 1; j<=9; j++){ if(mask & 1<<(input%10)) return 0; else mask |= 1<<(input%10); input /= 10; } /* At this point all digits are unique We're not interested in zero, though */ return !(mask & 1); } int main() { int indx; for( indx = 123456789; indx <=987654321; indx++){ if( check(indx) ) printf("%d\n",indx); } } 

Под редакцией ...

Или вы можете сделать то же самое с массивом:

 int check2(int input) { int j, arr[10] = {0,0,0,0,0,0,0,0,0,0}; for(j=1; j<=9; j++) { if( (arr[input%10]++) || (input%10 == 0) ) return 0; input /= 10; } return 1; } 

Вот один подход – начните с массива уникальных цифр, затем произвольно перетасуйте их:

 #include  #include  #include  #include  int main( void ) { char digits[] = "123456789"; srand( time( NULL ) ); size_t i = sizeof digits - 1; while( i ) { size_t j = rand() % i; char tmp = digits[--i]; digits[i] = digits[j]; digits[j] = tmp; } printf( "number is %s\n", digits ); return 0; } 

Некоторые результаты выборки:

 [email protected]:~/Development/snippets$ ./nine number is 249316578 [email protected]:~/Development/snippets$ ./nine number is 928751643 [email protected]:~/Development/snippets$ ./nine number is 621754893 [email protected]:~/Development/snippets$ ./nine number is 317529864 

Обратите внимание, что это символьные строки уникальных десятичных цифр, а не числовые значения; если вы хотите получить соответствующее целочисленное значение, вам необходимо выполнить преобразование, например

 long val = strtol( digits, NULL, 10 ); 

Вместо 10 переменных я бы сделал одну переменную с битом (и тестируемым) для каждой из десяти цифр. Тогда вам нужно только установить цикл (и проверить) бит, соответствующий каждой цифре. Что-то вроде этого:

 int ok = 1; unsigned bits = 0; int digit; unsigned powers10 = 1; for (digit = 0; digit < 10; ++digit) { unsigned bit = 1 << ((num / powers10) % 10); if ((bits & bit) != 0) { ok = 0; break; } bits |= bit; powers10 *= 10; } if (ok) { printf("%d\n", num); } 

Полная программа (отбрасывание ненужных строк #include ):

 #include  int main(void) { int indx; int num; for(indx = 123456789; indx <= 987654321; indx++) { num = indx; int ok = 1; unsigned bits = 0; int digit; unsigned powers10 = 1; for (digit = 0; digit < 9; ++digit) { unsigned bit = 1 << ((num / powers10) % 10); if ((bit == 1) || ((bits & bit) != 0)) { ok = 0; break; } bits |= bit; powers10 *= 10; } if (ok) { printf("%d\n", num); } } return 0; } 

ОП разъяснил свой вопрос, поскольку я уезжаю на работу, и я не фокусировался на недостатке нhive. (ответ теперь обновляется). Это дает ожидаемые комбинации 362880.

Однако, было высказано мнение о том, что один из ответов является самым быстрым, что побуждает к продолжению. Были (считая это) три сопоставимых ответа. В быстрой проверке:

  • @Paul Hankin 's ответ (который считает нули и дает 3265920 комбинаций):
     реальный 0m0.951s
     пользователь 0m0.894s
     sys 0m0.056s
  • вот этот:
     реальный 0m49.108s
     пользователь 0m49.041s
     sys 0m0.031s
  • @ Ответ Джордж Андре (который также дал ожидаемое количество комбинаций):
      реальный 1m27.597s
      пользователь 1m27.476s
      sys 0m0.051s

Проверьте этот код.

  #include //it can be done by recursion void func(int *flag, int *num, int n){ //take 'n' to count the number of digits int i; if(n==9){ //if n=9 then print the number for(i=0;i 

Если у вас есть какие-либо вопросы, не стесняйтесь спрашивать.

Я рекомендую ответ Номинального Животного, но если вы генерируете это значение только для того, чтобы вы могли его распечатать, вы можете устранить часть работы и в то же время получить более общую процедуру, используя тот же метод:

 char *shuffle( char *digit, int digits, int count, unsigned int seed ) { //optional: do some validation on digit string // ASSERT(digits == strlen(digit)); //optional: validate seed value is reasonable // for(unsigned int badseed=1, x=digits, y=count; y > 0; x--, y--) // badseed *= x; // ASSERT(seed < badseed); char *work = digit; while(count--) { int i = seed % digits; seed /= digits--; unsigned char selectedDigit = work[i]; work[i] = work[0]; work[0] = selectedDigit; work++; } work[0] = 0; //seed should be zero here, else the seed contained extra information return digit; } в char *shuffle( char *digit, int digits, int count, unsigned int seed ) { //optional: do some validation on digit string // ASSERT(digits == strlen(digit)); //optional: validate seed value is reasonable // for(unsigned int badseed=1, x=digits, y=count; y > 0; x--, y--) // badseed *= x; // ASSERT(seed < badseed); char *work = digit; while(count--) { int i = seed % digits; seed /= digits--; unsigned char selectedDigit = work[i]; work[i] = work[0]; work[0] = selectedDigit; work++; } work[0] = 0; //seed should be zero here, else the seed contained extra information return digit; } 

Этот метод является деструктивным по числам, которые передаются, что на самом деле не должно быть числовым или уникальным в этом отношении.

Во избежание того, что вы хотите, чтобы выходные значения генерировались в отсортированном порядке, это немного больше работает:

 char *shuffle_ordered( char *digit, int digits, int count, unsigned int seed ) { char *work = digit; int doneDigits = 0; while(doneDigits < count) { int i = seed % digits; seed /= digits--; unsigned char selectedDigit = work[i]; //move completed digits plus digits preceeding selectedDigit over one place memmove(digit+1,digit,doneDigits+i); digit[0] = selectedDigit; work++; } work[0] = 0; //seed should be zero here, else the seed contained extra information return digit; } 

В любом случае это называется так:

 for(unsigned int seed = 0; seed < 16*15*14; ++seed) { char work[] = "0123456789ABCDEF"; printf("seed=%d -> %s\n",shuffle_ordered(work,16,3,seed)); } 

Это должно распечатать упорядоченный список трехзначных шестнадцатеричных значений без дублированных цифр:

 seed 0 -> 012 seed 1 -> 013 ... seed 3358 -> FEC seed 3359 -> FED 

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

Вот немного уродливое, но очень быстрое решение, использующее вложенные for loops .

 #include  #include  #include  #define NINE_FACTORIAL 362880 int main(void) { //array where numbers would be saved uint32_t* unique_numbers = malloc( NINE_FACTORIAL * sizeof(uint32_t) ); if( !unique_numbers ) { printf("Could not allocate memory for the Unique Numbers array.\n"); exit(1); } uint32_t n = 0; int a,b,c,d,e,f,g,h,i; for(a = 1; a < 10; a++) { for(b = 1; b < 10; b++) { if (b == a) continue; for(c = 1; c < 10; c++) { if(c==a || c==b) continue; for(d = 1; d < 10; d++) { if(d==a || d==b || d==c) continue; for(e = 1; e < 10; e++) { if(e==a || e==b || e==c || e==d) continue; for(f = 1; f < 10; f++) { if (f==a || f==b || f==c || f==d || f==e) continue; for(g = 1; g < 10; g++) { if(g==a || g==b || g==c || g==d || g==e || g==f) continue; for(h = 1; h < 10; h++) { if (h==a || h==b || h==c || h==d || h==e || h==f || h==g) continue; for(i = 1; i < 10; i++) { if (i==a || i==b || i==c || i==d || i==e || i==f || i==g || i==h) continue; // print the number or // store the number in the array unique_numbers[n++] = a * 100000000 + b * 10000000 + c * 1000000 + d * 100000 + e * 10000 + f * 1000 + g * 100 + h * 10 + i; } } } } } } } } } // do stuff with unique_numbers array // n contains the number of elements free(unique_numbers); return 0; } 

Same thing using some macros.

 #include  #include  #include  #define l_(b,n,c,p,f) { int i; for(i = 1; i < 10; i++) { \ int j,r=0; for(j=0;j 

I am sure there are better ways to write those macros, but that is what I could think of.

A simple way is to create an array with nine distinct values, shuffle it, and print the shuffled array. Repeat as many times as needed. For example, using the standard rand() function as a basis for shuffling …

 #include  /* for srand() and rand */ #include  /* for time() */ #include  #define SIZE 10 /* size of working array. There are 10 numeric digits, so .... */ #define LENGTH 9 /* number of digits we want to output. Must not exceed SIZE */ #define NUMBER 12 /* number of LENGTH digit values we want to output */ void shuffle(char *buffer, int size) { int i; char temp; for (i=size-1; i>0; --i) { /* not best way to get a random value of j in [0, size-1] but sufficient for illustrative purposes */ int j = rand()%size; /* swap buffer[i] and buffer[j] */ temp = buffer[i]; buffer[i] = buffer[j]; buffer[j] = temp; } } void printout(char *buffer, int length) { /* this assumes SIZE <= 10 and length <= SIZE */ int i; for (i = 0; i < length; ++i) printf("%d", (int)buffer[i]); printf("\n"); } int main() { char buffer[SIZE]; int i; srand((unsigned)time(NULL)); /* seed for rand(), once and only once */ for (i = 0; i < SIZE; ++i) buffer[i] = (char)i; /* initialise buffer */ for (i = 0; i < NUMBER; ++i) { /* keep shuffling until first value in buffer is non-zero */ do shuffle(buffer, SIZE); while (buffer[0] == 0); printout(buffer, LENGTH); } return 0; } 

This prints a number of lines to stdout , each with 9 unique digits. Note that this does not prevent duplicates.

EDIT: After further analysis, more recursion unrolling and only iterating on set bits resulted in significant improvement, in my testing roughly FIVE times as fast . This was tested with OUTPUT UNSET to compare algorithm speed not console output, start point is uniq_digits9 :

 int counter=0; int reps=0; void show(int x) { #ifdef OUTPUT printf("%d\n", x); #else counter+=x; ++reps; #endif } int bit_val(unsigned int v) { static const int MultiplyDeBruijnBitPosition2[32] = { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; return MultiplyDeBruijnBitPosition2[(unsigned int)(v * 0x077CB531U) >> 27]; } void uniq_digits1(int prefix, unsigned int used) { show(prefix*10+bit_val(~used)); } void uniq_digits2(int prefix, unsigned int used) { int base=prefix*10; unsigned int unused=~used; while (unused) { unsigned int diff=unused & (unused-1); unsigned int bit=unused-diff; unused=diff; uniq_digits1(base+bit_val(bit), used|bit); } } void uniq_digits3(int prefix, unsigned int used) { int base=prefix*10; unsigned int unused=~used; while (unused) { unsigned int diff=unused & (unused-1); unsigned int bit=unused-diff; unused=diff; uniq_digits2(base+bit_val(bit), used|bit); } } void uniq_digits4(int prefix, unsigned int used) { int base=prefix*10; unsigned int unused=~used; while (unused) { unsigned int diff=unused & (unused-1); unsigned int bit=unused-diff; unused=diff; uniq_digits3(base+bit_val(bit), used|bit); } } void uniq_digits5(int prefix, unsigned int used) { int base=prefix*10; unsigned int unused=~used; while (unused) { unsigned int diff=unused & (unused-1); unsigned int bit=unused-diff; unused=diff; uniq_digits4(base+bit_val(bit), used|bit); } } void uniq_digits6(int prefix, unsigned int used) { int base=prefix*10; unsigned int unused=~used; while (unused) { unsigned int diff=unused & (unused-1); unsigned int bit=unused-diff; unused=diff; uniq_digits5(base+bit_val(bit), used|bit); } } void uniq_digits7(int prefix, unsigned int used) { int base=prefix*10; unsigned int unused=~used; while (unused) { unsigned int diff=unused & (unused-1); unsigned int bit=unused-diff; unused=diff; uniq_digits6(base+bit_val(bit), used|bit); } } void uniq_digits8(int prefix, unsigned int used) { int base=prefix*10; unsigned int unused=~used; while (unused) { unsigned int diff=unused & (unused-1); unsigned int bit=unused-diff; unused=diff; uniq_digits7(base+bit_val(bit), used|bit); } } void uniq_digits9() { unsigned int used=~((1<<10)-1); // set all bits except 0-9 #ifndef INCLUDE_ZEROS used |= 1; #endif for (int i = 1; i < 10; i++) { unsigned int bit=1< 

Brief explanation :

There are 9 digits and the first cannot start with zero, so the first digit can be from 1 to 9, the rest can be 0 to 9

If we take a number, X and multiply it by 10, it shifts one place over. So, 5 becomes 50. Add a number, say 3 to make 53, and then multiply by 10 to get 520, and then add 2, and so on for all 9 digits.

Now some storage is needed to keep track of what digits were used so they aren't repeated. 10 true/false variables could be used: used_0_p , used_1_P , .... But, that is inefficient, so they can be placed in an array: used_p[10] . But then it would need to be copied every time before making a call the next place so it can reset it for the next digit, otherwise once all places are filled the first time the array would be all true and no other combinations could be calculated.

But, there is a better way. Use bits of an int as the array. X & 1 for the first, X & 2 , X & 4 , X & 8 , etc. This sequence can be represented as (1< or take the first bit and shift it over X times.

& is used to test bits, | is used to set them. In each loop we test if the bit was used (1< and skip if it was. At the next place we shift the digits for each digit prefix*10+i and set that digit as used used|(1<

Explanation of looping in the EDIT

The loop calculates Y & (Y-1) which zeroes the lowest set bit. By taking the original and subtracting the result the difference is the lowest bit. This will loop only as many times as there are bits: 3,265,920 times instead of 900,000,000 times. Switching from used to unused is just the ~ operator, and since setting is more efficient than unsetting, it made sense to flip

Going from power of two to its log2 was taken from: https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog . This site also details the loop mechanism: https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2

Moving original to the bottom:

This is too long for a comment, but This answer can be make somewhat faster by removing the zero handling from the function: ( See edit for fastest answer )

 void uniq_digits(int places, int prefix, int used) { if (!places) { printf("%d\n", prefix); return; } --places; int base=prefix*10; for (int i = 0; i < 10; i++) { if ((1< 

A bit late to the party, but very fast (30 ms here) …

 #include  #define COUNT 9 /* this buffer is global. intentionally. ** It occupies (part of) one cache slot, ** and any reference to it is a constant */ char ten[COUNT+1] ; unsigned rec(unsigned pos, unsigned mask); int main(void) { unsigned res; ten[COUNT] = 0; res = rec(0, (1u << COUNT)-1); fprintf(stderr, "Res=%u\n", res); return 0; } /* recursive function: consume the mask of available numbers ** until none is left. ** return value is the number of generated permutations. */ unsigned rec(unsigned pos, unsigned mask) { unsigned bit, res = 0; if (!mask) { puts(ten); return 1; } for (bit=0; bit < COUNT; bit++) { if (! (mask & (1u < 

iterative version that uses bits extensively

note that array can be changed to any type, and set in any order this will “count”the digits in given order

For more explaination look at my first answer (which is less flexible but much faster) https://stackoverflow.com/a/31928246/2963099

In order to make it iterative, arrays were needed to keep state at each level

This also went though quite a bit of optimization for places the optimizer couldn’t figure out

 int bit_val(unsigned int v) { static const int MultiplyDeBruijnBitPosition2[32] = { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; return MultiplyDeBruijnBitPosition2[(unsigned int)(v * 0x077CB531U) >> 27]; } void uniq_digits(const int array[], const int length) { unsigned int unused[length-1]; // unused prior unsigned int combos[length-1]; // digits untried int digit[length]; // printable digit int mult[length]; // faster calcs mult[length-1]=1; // start at 1 for (int i = length-2; i >= 0; --i) mult[i]=mult[i+1]*10; // store multiplier unused[0]=combos[0]=((1<<(length))-1); // set all bits 0-length int depth=0; // start at top digit[0]=0; // start at 0 while(1) { if (combos[depth]) { // if bits left unsigned int avail=combos[depth]; // save old combos[depth]=avail & (avail-1); // remove lowest bit unsigned int bit=avail-combos[depth]; // get lowest bit digit[depth+1]=digit[depth]+mult[depth]*array[bit_val(bit)]; // get associated digit unsigned int rest=unused[depth]&(~bit); // all remaining depth++; // go to next digit if (depth!=length-1) { // not at bottom unused[depth]=combos[depth]=rest; // try remaining } else { show(digit[depth]+array[bit_val(rest)]); // print it depth--; // stay on same level } } else { depth--; // go back up a level if (depth < 0) break; // all done } } } 

Some timings using just 1 to 9 with 1000 reps:

  • Вызывает ли C ++ деструкторы для глобальных и classовых переменных?
  • Конец файла (EOF) в C
  • Портативная библиотека сравнения и свопинга (атомарные операции) C / C ++?
  • Как использовать Microsoft.Office.Interop.Excel на компьютере без установленного MS Office?
  • size_t vs. uintptr_t
  • Код рефакторинга, чтобы избежать
  • CMake не находит компилятор Visual C ++
  • Является ли возrotation ссылкой rvalue более эффективным?
  • Можно ли отделить основную функцию и classы C ++ от подпрограмм Objective-C и / или C при компиляции и ссылке?
  • EF5 Получение этого сообщения об ошибке: Совместимость модели не может быть проверена, поскольку firebase database не содержит метаданных модели
  • Почему использование имени функции в качестве указателя функции эквивалентно применению оператора адреса к имени функции?
  • Interesting Posts

    Как вызвать кнопку Facebook как кнопку с помощью настраиваемой кнопки?

    Слишком длинное имя файла (только для проводника Windows)

    bds 2006 C конфликты с скрытым диспетчером памяти (class new / delete vs. AnsiString)

    Может кто-нибудь объяснить, как реализовать плагин загрузки файла jQuery?

    Как включить административные ресурсы в Vista и XP?

    Установка Checkstyle-Plugin (7.2.0) для Eclipse Neon не выполняется

    Разделить вектор по его последовательностям

    Java, перемещение элементов в массиве

    Как создать изменяемый размер прямоугольника с пользовательскими событиями касания на Android?

    Является ли это ошибкой в ​​Files.lines (), или я что-то не понимаю о параллельных streamах?

    Почему бы не использовать контейнер IoC для разрешения зависимостей для объектов / бизнес-объектов?

    Как я могу заставить VirtualBox работать в 1366×768?

    Как управлять исключениями, сброшенными фильтрами весной?

    Триггеры Oracle – проблема с изменяющимися таблицами

    Обновите чертеж, не удаляя предыдущий

    Давайте будем гением компьютера.