elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: ¿Eres nuevo? ¿Tienes dudas acerca del funcionamiento de la comunidad? Lee las Reglas Generales


  Mostrar Temas
Páginas: [1] 2
1  Programación / Programación C/C++ / Resolución de sistemas de ecuaciones lineales de grado "n" en: 30 Diciembre 2021, 22:38 pm
Dejo aquí un método -que creo que funciona- para la resolución de un sistema de "n" ecuaciones lineales con "n" incógnitas.
Está basado en el algoritmo de Gauss (no sé si otros lo llaman de Gauss-Jordan u otros de Gauss-Jacobi). Sea como sea que lo llamen consiste en dejar la matriz de coeficientes en una matriz triangular con 0 (ceros) por debajo de la diagonal principal de a matriz de coeficientes. Y luego ir resolviendo despejando in cógnitas de atrás hacia delante.

Sé que es un método poco eficiente. Para empezar los errores de cálculos sucesivos (especialmente en sistema muy grandes con muchas ecuaciones) pueden producir desbordamientos (overflow) o que, si hay términos relativamente pequeños respecto a otros, se produzcan reducciones a "1" que desvirtúen el resultado final, para sistemas de ecuaciones muy particulares. Como mi intención es simplemente una primera aproximación para reslover sistemas del tipo de los que se dan en Ingeniería (cálculo de estructuras, mallas de tuberías, etc...) de momento éso no me importa demasiado.

También la forma de introducir los datos es totalmente ineficiente -a mano-. Es sólo para pruebas; lo suyo es que sea a partir de datos de matrices que previamente se hayan almacenado en disco. Es más, esas matrices no se introducirán a mano -ni siquiera en disco- sino que provendrán de otros programas que las habrán guardado en disco a partir de otros datos (matrices de rigidez de una estructura -de construcción- a partir de sus datos de longitud de barras, inercia, material, etc; o longitud de tubería, diámetro, material, persión, etc...).

Por lo tanto, el hecho de la forma de introducción de datos no debe de ser valorado; solo el funcionamiento del algoritmo de resolución del sistema.

Por lo tanto, respecto al programa en general, me gustaría tener opiniones sobre:
- Sobre todo: ¡fallos que pueda tener! - He probado varios sistemas, algunos con bastantes incógnitas, y los ha resuelto. He probado varios sistemas con fallos "evidentes" (filas = 0; columnas = 0; filas/columnas = combinación lineal de otras filas/columnas;...) y me los ha detectado. En ese sentido, mi miedo es más bien -no que no me detecte sistemas sin solución-, sino que sistemas que SÍ tienen solución, me los detecte como que no la tienen. ¿Veis algún fallo? ¿Alguna forma de mejorarlo?

- ¿Se podría mejorar, mediante un algoritmo iterativo, la solución? He visto cosas, pero ninguna lo suficientemente clara -para mi- sobre cómo mejorar una solución a partir de una primera solución. En concreto he leído sobre los métodos de Richardson, de Jacobi, de Gauss-Seidel, que incluso empiezan con vectores/matrices de soluciones "alejadas" de la solución verdadera; cuando se podría mplementar desde una primera solución "muy aproximada" - la que ofrece el algoritmo que aquí se da-; pero no sé como implementarla.

- Por último: sé que hay un método muy potente (y con reducción de operaciones y de tiempo de cálculo y de disminución por errores de operaciones (¡justo lo que quiero disminuir!) que es la "condensación de ecuaciones"..., pero no encuentro información viable. Si alguien me puede ofrecer literatura sobre ese tema, pues estaría genial.

Bueno, aquí dejo el código que he hecho, para las críticas/comentarios/ayudas/mejoras/etc... que creáis convenientes (cualquier cosa será bien recibida):

Código
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. void intro_sistema (int n, double ** a);
  5. void combina_fila (int j, int n, double ** a); // Para dejar coeficientes de la columna a cero
  6. void intercambia_fila (int i, int j, int n, double ** a); // Para que no haya coefic. == 0 en la diagonal
  7. void calcula_incognitas (int n, double ** a, double * solucion); // A partir de una matriz ya triangular
  8.  
  9. int main ()
  10. {
  11. int n; // numero ecuaciones lineales
  12. int i, j;
  13. double ** a = NULL; // matriz ampliada: coeficientes | terminos independientes
  14. double * solucion = NULL;
  15.  
  16. printf ("Introducir no. de ecuaciones: ");
  17. scanf ("%d", &n);
  18.  
  19. a = (double **) malloc ( n * sizeof (double *) ); // filas
  20. for (j = 0; j < n; ++j)
  21. a[j] = (double *) malloc ( (n+1) * sizeof (double) ); // columnas
  22. solucion = (double *) malloc ( n * sizeof (double) );
  23.  
  24. intro_sistema (n, a);
  25.  
  26. for (j = 0; j < n-1; j++)  // Procesa elementos diagonal matriz coeficientes
  27. {
  28. if (a[j][j] == 0)
  29. {
  30. for (i = j+1; i < n; i++) // Busca un coeficiente de la misma columna != 0
  31. {
  32. if (a[i][j] != 0)
  33. {
  34. intercambia_fila (i, j, n, a);
  35. break;
  36. }
  37. printf ("\n\nEl sistema no tiene solucion unica"); // No hay coficiente != 0 sobre el que pivotar
  38. return 0;
  39. }
  40. }
  41. combina_fila (j, n, a);
  42. }
  43.  
  44. if (a[n-1][n-1] == 0)
  45. {
  46. printf ("\n\nEl sistema no tiene solucion unica\n"); // Toda la fila inferior es de ceros
  47. return 0;
  48. }
  49. else
  50. {
  51. calcula_incognitas (n, a, solucion);
  52. printf ("\n\n");
  53. for (j = 0; j < n; j++)
  54. printf ("soluc[%d] = %lf\n", j, solucion[j]);
  55. }
  56. return 0;
  57. }
  58.  
  59. void intro_sistema (int n, double ** a)
  60. {
  61. int i, j;
  62. double var;
  63.  
  64. printf ("\nIntroducir matriz de coeficientes del sistema de ecuaciones lineales\n");
  65. for (i = 0; i < n; i++)
  66. for (j = 0; j < n; j++)
  67. {
  68. printf ("a[%d][%d] = ", i, j);
  69. scanf ("%lf", &var);
  70. a[i][j] = var;
  71. }
  72. printf ("\nIntroducir terminos independientes del sistema\n");
  73. for (i = 0; i < n; i++)
  74. {
  75. printf ("b[%d] = ", i);
  76. scanf ("%lf", &var);
  77. a[i][n] = var;
  78. }
  79. }
  80.  
  81. void combina_fila (int j, int n, double ** a)
  82. {
  83. double factor;
  84. int i, k;
  85.  
  86. for (i = j+1; i < n; i++)
  87. {
  88. if (a[i][j] != 0)
  89. {
  90. factor = -a[j][j] / a[i][j];
  91. for (k = j; k < n+1; k++)
  92. a[i][k] = (factor * a[i][k]) + a[j][k];
  93. }
  94. }
  95. }
  96.  
  97. void intercambia_fila (int i, int j, int n, double ** a)
  98. {
  99. double var_intercamb;
  100. int k;
  101.  
  102. for (k = j; k < n+1; k++)
  103. {
  104. var_intercamb = a[i][k];
  105. a[i][k] = a[j][k];
  106. a[j][k] = var_intercamb;
  107. }
  108. }
  109.  
  110. void calcula_incognitas (int n, double ** a, double * solucion)
  111. {
  112. double acumulador;
  113. int i, j;
  114.  
  115. solucion[n-1] = a[n-1][n] / a[n-1][n-1];
  116. for (i = n-2; i >= 0; i--)
  117. {
  118. acumulador = a[i][n];
  119. for (j = i+1; j < n; j++)
  120. acumulador = acumulador - (a[i][j] * solucion[j]);
  121. solucion[i] = acumulador / a[i][i];
  122. }
  123. }
  124.  

Pues nada, lo dicho, que se agradece cualquier comentario.
2  Programación / Programación C/C++ / Un algoritmo para calcular si un grafo es conexo o no en: 12 Diciembre 2021, 15:15 pm
He escrito un código que compruebe si un grafo es o no conexo a partir de la matriz de adherencia del grafo. Creo que funciona, he hecho algunos ejemplos y parece que sí; adjunto unos gráficos. Supongo que debe de ser válido para otros. Pero me gustaría saber si a alguien se le ocurren contra-ejemplos, o si quiere exponer (puede ser por privado) una matriz de adherencia de un grafo (claro, yo los ejemplos que he hecho son a partir de grafos de los que conozco su representación gráfica en papel) para ver si funciona. Pero me gustaría probar con un grafo del que no conozca su representación gráfica en papel, sino solo su matriz de adherencia.

Pero sobre todo querría opiniones sobre el código, posibles fallos, y posibles mejoras. Solo opiniones generales, pseudocódigo quizá..., no hace falta código explícito; tampoco es para volver a escribirlo, que no tengo intención (salvo que vea fallos serios), sino para ver por dónde van los tiros.

Doy una idea general de cómo lo he hecho, luego expongo el código y luego pregunto las cosas que más me interesan.

La idea que se me ha ocurrido es algo parecido a "fuerza bruta". Se trata de recorrer todos los nodos que se pueda, a partir del primero. A partir del nodo inicial del grafo voy recorriendo un camino hasta que llega un nodo en el que no puedo ir más allá, doy marcha atrás y busco otro nodo para proseguir... y así sucesívamente. Voy yendo hacia adelante-atrás-adelante-atrás... Conforme voy agotando caminos posibles voy volviendo hacia atrás cada vez más, hasta que llega un punto en el que vuelvo al nodo de salida -después de haber ido explorando todos los caminos posibles-. En ese momento en el que he vuelto al nodo de partida veo si existe algún nodo al que no he llegado. Si existe es que el grafo es no conexo, ya que de haber habido un camino para llegar hasta él lo habría recorrido. Si no existen nodos sin visitar es que he llegado a todos y el grafo es conexo.

Ahora el código que he escrito y algunos ejemplos que he usado.

Código
  1. // Calcula si un grafo es conexo o no
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4.  
  5. #define ORDEN_MATR (num_nodos * num_nodos)
  6.  
  7. void intro_grafo (int num_nodos, int ** matr_adher);
  8. void procesa_grafo (int num_nodos, int nodo_actual, char * estado_nodo, int * camino, int posicion, char sentido, int ** matr_adher);
  9. char es_conexo (int num_nodos, char * estado_nodo); // =0 si existe algun estado_nodo [i] == 0 ; =1 en caso contrario
  10.  
  11. int main ()
  12. {
  13. int num_nodos; // Nº de nodos del grafo
  14. int ** matr_adher; // Matriz de adherencia del grafo
  15. int nodo_actual; // El que se está procesando en un momento dado
  16. char * estado_nodo; // 1 - si ya se ha pasado por él al menos una vez; 0 - en caso contrario
  17. int * camino; // Guarda los nodos que se van recorriendo
  18. int posicion; // subindice del arreglo camino [] que corresponde a nodo_actual
  19. char sentido; // 1 si se va avanzando hacia nodo no visitado; 0 - si se va retrocediendo a nodo ya visitado
  20. int i, j;
  21.  
  22. printf ("Introducir no. de nodos del grafo: ");
  23. scanf ("%d", &num_nodos);
  24. printf ("el numero es: %d\n", num_nodos);
  25. // Declara arreglos
  26. matr_adher = (int **) malloc ( ORDEN_MATR * sizeof (int *) ); // Filas
  27. for ( i=0; i < num_nodos; i++ )
  28. {
  29. matr_adher [i] = (int *) malloc ( num_nodos * sizeof (int) ); // Columnas
  30. }
  31. estado_nodo = (char *) malloc ( num_nodos * sizeof (char *) );
  32. camino = (int *) malloc ( num_nodos * sizeof (int *) );
  33.  
  34. // Inicializa variables
  35. for (i = 0; i < num_nodos; i++)
  36. for (j = 0; j < num_nodos; j++)
  37. matr_adher [i][j] = 0; // Por defecto ningun nodo esta conectado con nigun otro
  38. for (i = 0; i < num_nodos; i++)
  39. estado_nodo [i] = 0; // Al inicio no se ha visitado ningun nodo
  40. sentido = 1;
  41. posicion = 0;
  42. nodo_actual = 0;
  43. camino [0] = 0;
  44.  
  45. // Ejecucion
  46. intro_grafo (num_nodos, matr_adher);
  47. procesa_grafo (num_nodos, nodo_actual, estado_nodo, camino, posicion, sentido, matr_adher);
  48. if ( es_conexo (num_nodos, estado_nodo) )
  49. printf ("El grafo es conexo\n");
  50. else
  51. printf ("El grafo NO es conexo\n");
  52.  
  53. return 0;
  54. }
  55.  
  56. void intro_grafo (int num_nodos, int ** matr_adher)
  57. {
  58. int nodo1, nodo2, peso;
  59.  
  60. printf ("Introducir conexiones y pesos entre nodos en la forma:\n");
  61. printf ("nudo inicial... nudo final... peso conexion\n\n");
  62. printf ("No es necesario introducir conexiones reciprocas, la conexion i-j automaticamente es = a la j-i\n");
  63. printf ("Una nueva conexion i-j actualiza a la anterior si existiera\n");
  64. printf ("para finalizar el grafo introduzcir nudo incial = 0 y nudo final = 0\n\n");
  65. for (;;)
  66. {
  67. printf ("Nodo inicial: ");
  68. scanf ("%d", &nodo1);
  69. printf ("Nodo final: ");
  70. scanf ("%d", &nodo2);
  71. if (nodo1 == 0 && nodo2 == 0)
  72. return;
  73. else
  74. {
  75. printf ("Peso conexion: ");
  76. scanf ("%d", &peso);
  77. if (nodo1 != nodo2)
  78. {
  79. matr_adher [nodo1][nodo2] = peso;
  80. matr_adher [nodo2][nodo1] = peso;
  81. }
  82. printf ("\n");
  83. }
  84. }
  85. }
  86.  
  87. void procesa_grafo (int num_nodos, int nodo_actual, char * estado_nodo, int * camino, int posicion, char sentido, int ** matr_adher)
  88. {
  89. int j;
  90. printf ("\nfuncion procesa_grafo\n"); // SOLO PARA DEPURACION DEL PROGRAMA - BORAR
  91. printf ("nodo actual: %d\n", nodo_actual); // SOLO PARA DEPURACION DEL PROGRAMA - BORAR
  92. estado_nodo [nodo_actual] = 1;
  93. if ( (nodo_actual == 0) && (sentido == 0) )
  94. return;
  95.  
  96. for (j = 0; j < num_nodos; j++)
  97. if ( (matr_adher [nodo_actual][j] != 0) && (estado_nodo[j] == 0) )
  98. {
  99. posicion += 1; // Recorre la fila de la matriz del nodo en proceso hasta encontrar
  100. camino [posicion] = j; // una conexión con un nodo que no hay sido visitado anteriormente,
  101. sentido = 1; // si lo encuentra cambia el control del proceso a ese nodo
  102. procesa_grafo(num_nodos, j, estado_nodo, camino, posicion, sentido, matr_adher);
  103. }
  104.  
  105. posicion -=1; // Caso de recorrer toda la fila y no encontrar un nodo en avance
  106. sentido = 0;  // retrocede al nodo anterior y cambial el control del proceso a ese punto
  107. procesa_grafo (num_nodos, camino [posicion], estado_nodo, camino, posicion, sentido, matr_adher);
  108. }
  109.  
  110. char es_conexo (int num_nodos, char * estado_nodo)
  111. {
  112. int i;
  113. for (i = 0; i < num_nodos; i++)
  114. if ( estado_nodo [i] == 0 )
  115. return 0;
  116. return 1;
  117. }
  118.  

Algunos ejemplo que he hecho



Algunas consideraciones:
- He considerado que un nodo no puede estar conectado consigo mismo; para saber si los nodos están conectados entre sí no hace falta, y estorba para el algoritmo.
- También he considerado solamente grafos en los cuáles los nodos están conectados en ambas direcciones: si se puede ir desde a hacia b, entonces también se puede ir desde b hacia a. (Creo que existen grafos en los cuáles puede haber trayectorias en un sentido pero no en el contrario).
- El peso de la conexión únicamente es útil para saber si dos nodos están conectados (==0 SI // !=0 NO), daría igual que fuesen sencillamente 1 ó 0. He dejado cualuier entero solamente por generalidad de la función de entrada de datos (no se trata de calcular longitudes mínimas o máximas de distintos caminos). Incluso podrían ser negativos ya que el programa solo considera si son o no igual a cero.
- He utilizado un arreglo (camino []) y una variable (posicion) para ir recorriendo la malla. Aunque creo que se podría utilizar una pila LIFO y push-pop).

Defectos que ya veo:
- Si el nodo 0 está aislado el programa se pierde. Se podría corregir fácilmente haciendo que antes de comenzar a procesar se comprobara que la fila 0 de la matriz no es todo ceros; pero es trivial y no merece la pena; no afecta al algoritmo.
- He visto -a través de las líneas 90 y 91 que están solamente para depuración- que el algoritmo pasa varias veces por nudos que no haría falta (no afecta a la eficacia del código pero sí a la eficiencia del mismo: retardo). Veo que ésto es así por comenzar siempre el análisis de cada nuevo nodo (fila) desde cero (línea 96) en lugar de hacerlo desde el último nodo (columna) que se visitó a partir del nodo_actual. Pero no estoy muy seguro de cómo corregirlo; se me ocurre tener otro arreglo similar a camino [] en el cuál se anote ese nº de columna y comenzar el bucle de la línea 96 en ese lugar en vez de cero. No lo he hecho porque, bueno, lo que me importa es más bien que la idea general funcione, ya que no se trata de una aplicación práctica. Además añadiría más argumentos a la función procesa_grafo, que ya tiene bastantes.

Entonces:
- ¿Algún error de bulto? ¿Fallos, ejemplos de grafos que hagan que claramente el algoritmo no funcione...?
- ¿Alguna idea para corregir el bucle de la línea 96 y que no empiece desde cero?
- ¿Se mejoraría la eficiencia del programa usando una pila en lugar del arreglo y la posición?
- Y lo principal: ¿existiría otro tipo de algoritmo que no fuera recorrer todos los caminos posibles?; no sé, algo de tipo matemático, alguna propiedad matemática de la matriz de adherencia, algún método que use de expresiones matemáticas, en lugar de un algoritmo de recorrido paso a paso, que determine si el grafo es conexo o no. O en caso de algorimo de recorrido, ¿otra forma de hacer el recorrido que sea más eficiente?
- Por último: ¿alguna manera de programar más elegante (o eficaz) sin usar una función recursiva. Tengo entendido que los goto, las variables globales y la recursividad (por este orden) no son muy bienvenidas por los programadores ( al menos en C); y si se puede escribir un código más acorde a los usos habituales o deseables en programación, pues bienvenidos sean consejos.

Gracias de antemano por las respuestas.
3  Programación / Programación C/C++ / Comportamiento desigual mismo código C en Linux-Ubuntu y Windows-MinGW en: 7 Diciembre 2021, 17:36 pm
¿Existe alguna razón por la que un mismo código C -estándar + librerías stdio.h - stdlib.h- se comporte de forma distinta en un entorno Linux-Ubuntu -compilador gcc-, que en otro entorno Windows-compilador-MinGW (gcc).

En concreto:

para una variabe declarada como:
Código
  1. int variable;

y un uso de ella con:
Código
  1. scanf (%d", &variable);

en el entorno Linux-Ubuntu me funciona con cualquier nº entero, pero en Windows-MinGW sólo me funciona si variable <=9. En cuanto  le meto variable = 10, 11, 12,... el programa simplemente se sale al terminal sin dar explicaciones.

NOTA: en el entorno Linux el nombre del programa es el mismo del programa fuente sin ".c"; en en el entorno Windows lo llamo nombre del programa ".exe". Lo he probado tanto desde Visual Studio -a través del run- como directamente en CMD-Windows compilando con gcc a mano y ejecutando a mano nombre-del-programa.exe.
En el entorno Linux siempre en consola compilado y ejecutado a mano.
 
4  Programación / Programación General / Responsabilidad del programador/compilador-S.O. en la eficiencia de la lectura/escritura de datos en disco en: 3 Diciembre 2021, 12:31 pm
Tengo entendido que la rapidez de las operaciones de lectura/escritura en disco dependen de si se hacen varias en bloque o como datos aislados uno a uno. No sé seguro si ésto es así -y menos en los SSD-, es lo que yo tenía entendido; pero si no es así se puede ahorrar la lectura de lo que sigue.

Dado por cierto lo anterior lo que quiero saber es si el compilador o el Sistema Operativo adaptan esas operaciones a la memoria disponible -de una forma transparente para el programador- o debe ser éste quien programe esas operaciones de la forma más eficiente.

Lo aclaro con un ejemplo concreto. Supongamos que un programa hace una multiplicación de matrices que están almacenadas en archivo en disco y que, por su dimensión, no caben en la memoria del ordenador y, por tanto, no pueden leerse de una tacada y operar con ellas en memoria, guardar el propducto también en memoria y al final grabar en disco el resultado de una vez.

Lo más fácil para el programador -pero quizá no lo más eficiente en tiempo de ejecución- es mediante bucle leer un dato de fila de una matriz, leer un dato de columna de la otra, multiplicar y acumular el producto, proceder igual con el siguiente elemento de fila, el siguiente de columna, volver a acumular el producto y cuando se haya terminado el producto fila x columna grabar el resultado en disco; y volver a proceder con fila-columna.

La pregunta es: ¿si se programa de esa forma, en tiempo de ejecución, se ejecuta también así o bien al compilar, o al ejecutar el S.O. realizan las operaciones E/S (disco) en bloques según la RAM disponible y hacen esas operaciones en pequeños bloques en RAM?

¿O bien es responsabilidad del programador -si quiere mejorar la eficiencia- el prever más bucles adicionales y más arreglos adicionales que realizen, por ejemplo, lectura de 100 datos de fila, 100 de columna (el 100 es un ejemplo, podría ser otro), los almacenen en en RAM, operen con ellos, realicen otras 100 lecturas, etc., hasta terminar con fila-columna, almacenar en otro arreglo (de otros 100 datos), proseguir multiplicando, cuando ya tenga 100 resultados escribirlos de golpe en el disco... y proseguir hasta acabr el producto de las matrices?

En resumen: que si el programador para mejorar la efciencia tiene que programar (valga la redundancia) las operaciones de lectura/escritura en bloque adaptados a la RAM, o puede confiar esa labor al compilador o al S.O.: en tiempo de ejecución de forma transparente para él.
5  Informática / Hardware / ¿Cuánto puede durar un ordenador gama media/baja (latop o escritorio) encendido 24h/365d? en: 12 Noviembre 2021, 21:55 pm
¿Hay estudios/muestreos, ya sean hechos por los propios fabricantes o por organizaciones de terceros: comercializadores, usuarios, entidades de control independientes, organizaciones  universitarias, investigadores, organizaciones de consumidores, etc, que hayan estudiado sobre sobre la vida media de los ordenadores de gama medio/baja encendidos contínuamente? ¿Según marcas/modelos? Ya sean portátiles o de sobremesa.

¿Y sobre cuál elemento del ordenador es el que suele fallar primero? Excluídas las pantallas/impresoras/teclados/unidades extraíbles. Referido a microprocesadores, ventiladores, memorias, discos duros.
6  Programación / Programación C/C++ / Escribir las permutaciones de un nº "n" indefinido de elementos en: 4 Noviembre 2021, 23:10 pm
Siguiendo las indicaciones aportadas por Serapis en  este hilo sobre la similtud de las permutaciones de "n" objetos con los números -en base "n"- de "n" dígitos, he escrito un código que creo funciona.

Al menos para 3-4 parece que lo hace correctamente; para más me es difícil saberlo porque visualmente y de memoria empiezan ya a ser muchas permutaciones. Pero por el aspecto visual conforme va calculando y por inducción -si funciona para n, debería de funcionar para n+1- parece que sí. Además he añadido unas líneas (no necesarias) para comprobar el nº total de permutaciones (líneas 14, 35, 45) y contrastarlo con n! y también concuerda.

Cosa distinta será la eficacia y por éso quería tener opiniones. Sobre que secuencias de código  son poco eficaces y cómo se podrían mejorar. Solo ideas generales, ni siquiera pseudo-código, no estoy pensando en escribir una aplicación como tal sino tener una idea de conjunto de cosas que no se deban hacer en programación.

Yo, algunas me huelo. Por ejemplo yo determino la 1ª y 2ª permutaciones porque la 1ª es el nº menor y la siguiente la misma permutando los dos elementos de menor orden (los 2 de la derecha). Pero a partir de ahí ya voy a saco aumentando 1 el nº y comprobando si es permutación o no. Imagino que (matemáticamente) se podrían saltar varios elementos, dependiendo de la base "n"; pero lo sé.

Otra cosa que me huelo que es mejorable es que yo para determinar si es una permutación o no, yo lo hago a cascoporro, comparo a partir del 2º elemento por la izquierda (el [1]) y hasta el último (el [n-1]) con todos los anteriores, y quizá éso haya formas de mejorarlo comparando por parejas o algo así, no sé.

También tras cada nueva permutación encontrada comprueba si es la última, para no seguir el bucle indefinidamente, pero a lo mejor habría alguna forma de ahorrar comprobaciones. A mí no se me ocurre. Solamente comprobar si el total es = a n!; pero éso no loquiero hacer (el cálculo que he hecho del total es solamente a efectos de contrastar si el código coincide con el nº que debe de salir).

Yo lo he intentado hacer lo más simple y legible posible, pero saber si habría otras maneras de hacerlo más legible o más eficaz. Siempre referido a este tipo de algoritmo, no a los otros que se comentaron en el hilo señalado. (Si me animo igual lo intento con el algoritmo que construye una permut. siguiente a una dada).

Código
  1. // Escribe las permutaciones de "n" elementos
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4.  
  5. void imprime (int n,  int [] );
  6. void cuenta_uno (int n, int []); // Aumenta 1 unidad al nº en base "n"
  7. int es_permutacion (int n, int []); // Devuelve 1 si es permutación y 0 si no lo es
  8.  
  9. int main ()
  10. {
  11. int n, i;
  12. int *digitos; // de un nº de "n" dígitos en base "n"
  13. int suma_comprob;
  14. int total_permut = 2; // Solo para comprobar si coincide con n! BORRAR*********
  15.  
  16. printf ("Introducir nº de elementos a permutar: ");
  17. scanf ("%d", &n);
  18. digitos = (int *) malloc ( n*sizeof (int) );
  19. for (i = 0; i < n; ++i) // primera permutación
  20. digitos[i] = i;
  21. imprime (n, digitos);
  22.  
  23. i = digitos[n-2]; // segunda permutación
  24. digitos[n-2] = digitos[n-1];
  25. digitos[n-1] = i;
  26. imprime (n, digitos);
  27.  
  28. // permutaciones subsiguientes
  29. for (;;)
  30. {
  31. cuenta_uno (n, digitos);
  32. if ( es_permutacion(n, digitos) )
  33. {
  34. imprime (n, digitos);
  35. total_permut += 1; // BORRAR*********
  36.  
  37. // Comprueba si es última permutación
  38. suma_comprob = 0;
  39. for (i= 0; i < n; ++i)
  40. if (digitos[i] == n-1 - i)
  41. ++suma_comprob;
  42. if (suma_comprob == n)
  43. {
  44. free (digitos);
  45. printf ("\nTotal permutaciones = %d\n", total_permut); // BORRAR*******
  46. return 0;
  47. }
  48. }
  49. }
  50. }
  51.  
  52. void imprime (int n, int digitos[])
  53. {
  54. int i;
  55. printf ("%d-", digitos[0]);
  56. for (i = 1; i < n-1; ++i)
  57. printf ("%d-", digitos[i]);
  58. printf ("%d\n", digitos[n-1]);
  59. }
  60.  
  61. void cuenta_uno (int n, int digitos[])
  62. {
  63. int posicion; // digito que se trata en un momento dado: [0], [1],...,[n-1]
  64.  
  65. posicion = n-1;
  66. if (digitos[posicion] + 1 == n)
  67. do
  68. {
  69. digitos[posicion]= 0;
  70. posicion -= 1;
  71. }
  72. while (digitos[posicion] == n-1);
  73. digitos[posicion] += 1;
  74. }
  75.  
  76. int es_permutacion (int n, int digitos[])
  77. {
  78. int i, j;
  79.  
  80. // Se comprueba que todos los digitos[] sean distintos entre sí
  81. for (i = 1; i < n; ++i)
  82. for (j = 0; j < i; ++j)
  83. if (digitos[i] == digitos[j])
  84. return 0;
  85. return 1;
  86. }
  87.  
7  Foros Generales / Foro Libre / La matanza del cerdo: estafa por internet con criptomonedas en: 3 Noviembre 2021, 10:15 am

https://www.elperiodico.com/es/sucesos/20211103/chico-conoce-chico-facebook-acaba-12617137

También hay que ser ingenuo para tener una relación meramente electrónica y confiar lo suficiente como para invertir dinero. Hay personas que van pidiendo a gritos que las estafen.
8  Foros Generales / Foro Libre / Física: Óptica: pregunta que no he sabido contestar sobre reflexión de la luz en: 1 Noviembre 2021, 21:07 pm
Me han hecho esta pregunta de Física -Óptica- que no he sabido contestar:
Según las leyes de reflexión de la luz, el ángulo del haz de luz reflejado es igual al del haz de luz incidente. El ángulo da igual el que sea. Hay un ángulo de entrada, una perpendicular a la superficie de reflexión, y un ángulo de salida. Respecto de esa perpendicular, el ángulo de entrada y el de salida son iguales. No hay direcciones ni ángulos preferentes.

Entonces: ¿por qué al mirarte en un espejo tu mano izquierda se refleja en tu derecha, y viceversa, pero tu cabeza no se refleja en tus pies y tus pies en tu cabeza? O, de otra forma, ¿por qué las cosas de la izquierda se reflejan en la dereha y las de derecha se reflejan en la izquierda, pero en cambio, las de arriba no se reflejan abajo y las de abajo arriba? ¿Si en las leyes de reflexión no hay direcciones ni ángulos preferentes?
9  Foros Generales / Sugerencias y dudas sobre el Foro / Propongo que se habilite una forma de dar un tema por solucionado en: 25 Octubre 2021, 23:11 pm
Recientemente se ha restringido la capacidad de modificar/editar mensajes en el tiempo, reduciéndola a un cierto tiempo a partir del mensaje original; por lo visto debido a que  algunos abusaron de la anterior disponibilidad de hacerlo indefinidamente para borrar sus textos escritos anteriormente.

No entro ni salgo en la decisión tomada por el staff. Pero si entro en un asunto colateral derivado del mismo. Como consecuencia de esa restricción no se puede cambiar el indicativo de un mensaje respecto de su estado; lo llamo así a falta de otro calificativo. Me refiero al icono que aparece en la 2ª columna por la izquierda en los tablones de mensajes. El que indica si es una pregunta, si ya está resuelto, etc.

En concreto veo que no se puede cambiar el icono y poner el de que ya está resuelto cuando un tema efectivamente está resuelto.

Por ejemplo me ha pasado en este tema abierto por mi, que ya está resuelto pero sólo puedo indicarlo en mi último mensaje:

https://foro.elhacker.net/programacion_general/iquestexiste_algun_algoritmo_para_escribir_las_pemutaciones_de_n_elementos_sin_almacenar_las_de_n1_elementos-t512494.0.html;msg2251655#msg2251655

Pero no aparece en el tablón general como resuelto:

https://foro.elhacker.net/programacion_general-b18.0/

Aparece en el mensaje, pero no en el tablón. Antes, cuando se podía editar, se podía cambiar el icono del primer mensaje cuando, después de respuestas satisfactorias, se podía dar el tema por resuelto.

Ahora no se puede y, evidentemente, en el primer mensaje no se puede dar el tema por resuelto o no resuelto, pues no se sabe en qué va a acabar la cosa.

Así que propongo que, aunque no se pueda editar el texto pasado cierto tiempo, sí se pueda modificar el icono de resuelto/pregunta/lo-que-sea indefinidamente en el tiempo por el autor del mensaje (no por otros).
10  Programación / Programación General / ¿Existe algún algoritmo para escribir las pemutaciones de n elementos sin almacenar las de n-1 elementos? en: 18 Octubre 2021, 20:10 pm
Pues básicamente lo que quiero saber es éso. Yo sé formar las permutaciones de n elementos a partir de las de n-1 elementos. Solo se necesita partir de la definición de permutación. Si tienes las de n-1 elementos, solo hay que ir intercalando -por ejemplo suponiendo que las tienes escritas con los elementos de izquierda a derecha- el nuevo elemento n desde el lugar 0 hasta el lugar n+1 en todas la permutaciones anteriores de n-1 elementos.

Con lo cual puedes escribir (en teoría) un programa que escriba las permutaciones de cualquier número n de elementos. En realidad serían las permutaciones de n nºs naturales; ya luego tendrías que tener n signos gráficos asociados, pero con con permutar n nºs enteros (considerados como números no como dígitos) bastaría.

Así, para cualquier número n bastaría ir calculando/escrbiendo las permutaciones de 2 elementos, a partir de ellas las de 3 elelementos , y así sucesivamente hasta las de n elementos.

Pero claro, tienes que tener capacidad de almacenaje de las permutaciones de un número de elementos creciente, que además con las permutaciones es brutal; crece factorialmente.

Por eso me he preguntado si existiría un algoritmo que no necesitase almacenar las permutaciones de n-1 elementos, sino que solo mediante bucles fuese capaz de escribir las permutaciones directamente. De existir tal cosa podría ponerse el programa a trabajar, y aunque al final a la larga sería lo mismo, puesto que las permutaciones habría que o guardarlas en disco o guardarlas en papel, y el tiempo de trabajo del programa crecería hasta infinito -según le pidieras permutaciones de un nº creciente de elementos-; pero al menos, no colapsaría previamente por falta de almacenamiento. En todo caso colapsaría en el tiempo.

Yo le he dado algunas vueltas al asunto y creo que no, que no es posible tal algoritmo, y que cualquier algoritmo pasa por tener almacenadas las permutaciones de n-1 elementos para poder calcular las de n elementos. Pero como sé que -en esto- no paso de ser una medianía por eso pregunto.

¿Habría alguna manera de ir reproduciendo las permutaciones de un nº cualquiera de nºs naturales? ¿Sólo mediante bucles o algo así sin almacenar las de un nº de elementos anteriores?

----------------------------

A raíz de esta pregunta me surgió otra que, en puridad, debería estar en un foro diferente, el de Dudas Generales, o algo así. Pero bueno por no complicar la dejo aquí.
Y es: ¿existe alguna aplicación práctica (Ingeniería, Física... lo que sea) que necesite tener las permutaciones de un nº variable elementos?
NO el nº de permutaciones, que si sé que éso se emplea, desde la Física Termodinámica Estadística, pasando por la Mecánica Cuántica, hasta disciplinas probabilísticas. No, no el nº de permutaciones, sino las permutaciones en sí mismas. ¿Hay alguna aplicación práctica que las necesite?
Páginas: [1] 2
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines