¿Cuál es el algoritmo más rápido para generar todos los números [math] abcdefgh [/ math] de modo que [math] a \ leq b \ leq \ cdots \ leq h [/ math]?

Bueno, seguramente podemos hacer ocho bucles for anidados, pero eso es feo, molesto de escribir, propenso a errores y no se generaliza bien. En cambio, aquí hay una bonita solución.

Considere el siguiente “algoritmo”:

  1. Comience con un número “vacío” (es decir, todavía no tiene dígitos).
  2. Para cada valor de 0 a 9, tome tantas copias del dígito como desee y añádalas al número.

Claramente, usando este “algoritmo” podemos generar exactamente los números que queremos. Si solo queremos números de dígitos [matemáticos] [/ matemáticos], todo lo que tenemos que hacer es prestar atención en el paso 2 y asegurarnos de que tomamos un total de dígitos exactamente [matemáticos] d [/ matemáticos].

Si queremos una forma más formal de decir “en total, tome exactamente [math] d [/ math] dígitos”, podemos reformular el mismo “algoritmo” de la siguiente manera:

Comenzamos con un número “vacío” y establecemos el “dígito actual” en cero. Luego, en cualquier orden de nuestra elección, ejecutaremos los siguientes comandos:

  • exactamente 9 veces: incremente el dígito actual
  • exactamente d veces: agrega el dígito actual al número

Ahora podemos hacer las siguientes observaciones:

  • Cada número que podemos generar usando el algoritmo anterior tiene la propiedad deseada. Cada vez que agregamos un dígito al número, es igual o mayor que el dígito anterior que agregamos, que es precisamente lo que queríamos.
  • Podemos generar cada número válido usando el algoritmo anterior. Simplemente vaya dígito por dígito. Por ejemplo, para generar 334, incremente el dígito actual tres veces (para cambiarlo de 0 a 3), añádalo al número actual dos veces (para cambiarlo de “” a “33”), luego incremente el dígito actual nuevamente (para 4), añádalo al número (“334”) y finalmente incremente el dígito actual las últimas cinco veces.
  • Diferentes secuencias de comandos producen diferentes números. Mire el primer comando “agregar” donde difieren. En ese paso, obviamente agregaríamos dos dígitos diferentes.

Por lo tanto, tenemos un algoritmo general que se ve de la siguiente manera:

  1. Ingrese [math] d [/ math]: el número de dígitos.
  2. Genere todas las secuencias de comandos [math] d + 9 [/ math], como se describió anteriormente. En C ++ podemos hacer esto de manera conveniente y eficiente usando la función STL next_permutation .
  3. Para cada secuencia de comandos, calcule y envíe el número correspondiente.

Aquí hay una buena implementación de C ++ anotada:

// lee el número de dígitos
int D;
cin >> D;

// crea la matriz de “comandos”
// el comando 0 agrega el dígito actual, el comando 1 lo incrementa
comandos de vector (9 + D, 0);
para (int i = D; i <D + 9; ++ i) comandos [i] = 1;

// iterar sobre todas las secuencias de “comandos”
hacer {
// procesa la secuencia actual de “comandos”
string current_number = “”;
char current_digit = ‘0’;
para (int cmd: comandos) {
si (cmd) {
++ current_digit;
} más {
número_corriente + = dígito_corriente;
}
}
cout << número_actual << endl;
} while (next_permutation (command.begin (), command.end ()));

Como beneficio final, al iterar sobre las secuencias de comandos en un orden elegido correctamente obtenemos la salida en orden ordenado.

El siguiente código debe explicarse por sí mismo. Imprime todos los números de 8 dígitos de manera que los dígitos estén en orden no decreciente.

#include
int main () {
para (int a = 0; a <10; ++ a)
para (int b = a; b <10; ++ b)
para (int c = b; c <10; ++ c)
para (int d = c; d <10; ++ d)
para (int e = d; e <10; ++ e)
para (int f = e; f <10; ++ f)
para (int g = f; g <10; ++ g)
para (int h = g; h <10; ++ h)
printf (“% d% d% d% d% d% d% d% d \ n”, a, b, c, d, e, f, g, h);
}

Cada ciclo “for” itera uno de los 8 dígitos. El rango de cada ciclo es desde el dígito anterior hasta 9.

No conozco ningún algoritmo más rápido. El bucle más interno itera una vez por salida. En mi PC anterior, genera 24.310 números en un archivo en aproximadamente 50 milisegundos.

Le sugiero que estudie el código y asegúrese de comprenderlo. Luego escribe tu propia versión. Podría ser un mal día si su maestro busca en Google un fragmento de su código y aparece aquí.

Dado que este es un número determinista, y dado que el número es lo suficientemente pequeño, a menos que desee generar estos números con frecuencia, esto se puede hacer utilizando un poco de recursividad.

Para ilustrarlo, escribí este jsfiddle

// Genera el número actual en el índice dado
// idx = el número actual que estamos generando (abc …)
// inicio = el valor mínimo aceptable para este número
// acc = el valor acumulado hasta ahora (es decir, todos los números sumados)
// out = una estructura de datos para recopilar los valores en
función genNum (idx, start, acc, out) {

// Si hemos llegado al final de la secuencia numérica, agréguela al búfer de salida.
if (idx == -1) {
out.push (acc);
regreso;
}

// Genera el siguiente número en la secuencia y agrega este número actual al campo anterior
para (var i = inicio; i <= 9; i ++)
genNum (idx – 1, i, acc * 10 + i, out);

}

var out = [];
genNum (8, 0, 0, fuera);
mspan.textContent = out.join (“”);

Sé que no es C ++, por lo que queda algo de ejercicio para hacerlo, pero ilustra el algoritmo. También se ejecuta en segundos en un navegador, por lo que no sabría cuánto más necesitarías para optimizarlo.

Utiliza la recursividad, que es aceptable incluso en C ++ para esta situación, ya que tenemos una profundidad de recursión máxima efectiva que es muy superficial. Y lo que básicamente hace por recursión es dividir el problema en partes cada vez más pequeñas, es decir, la función para generar el número entero es la suma de generar el primer número y luego generar el resto.

// Esto es tan simple, no requiere comentario.
// bromeando.
def print_all_sorted (cadena) {
if (size (string) == 8) {
println (cadena)
regreso
}
// extraer el último dígito y convertir a int, fallando a 0
last = int (cadena [-1]) ?? 0 0
para (c: [0:10]) {
if (último <= c) {
print_all_sorted (cadena + c)
}
}
}
print_all_sorted (”)

Exactamente como lo explicó Michal Forišek. Solo una forma más simple.

Como el conjunto de tales números es infinito, no existe un algoritmo que pueda generar todos esos números en un tiempo finito. No tiene sentido comparar la eficiencia de los algoritmos que nunca producen una solución, por lo que su pregunta no tiene una respuesta sensata.

Este es el código para Microsoft Wordbasic

para I = 1 a 9

inserte “00000000” y str $ (I)

siguiente yo

Para I = 10 a 99

inserte “0000000” y str $ (I)

siguiente yo

y así sucesivamente