Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Exercice] zCellular : un automate cellulaire !

4 mars 2015 à 23:52:40

Tu peux simplifier la série de condition if(rule[i-1] == 0 && rule[i] == 0 etc...) par :

for(i = 0 ; i < tab.length ; i++)
{
    var g = get_color_of_cell(i-1),
        m = get_color_of_cell(i),
        d = get_color_of_cell(i+1);

    var ruleid = (g == 1 ? 4 : 0) + (m == 1 ? 2 : 0) + (d == 1 ? 1 : 0);
    newtab[i] = rule[ruleid];
}

Ça a été fait par plusieurs personnes ici ;)

-
Edité par Escargotine 4 mars 2015 à 23:52:52

  • Partager sur Facebook
  • Partager sur Twitter
6 mars 2015 à 11:24:11

louk a écrit:

Tu peux simplifier la série de condition if(rule[i-1] == 0 && rule[i] == 0 etc...) par :

for(i = 0 ; i < tab.length ; i++)
{
    var g = get_color_of_cell(i-1),
        m = get_color_of_cell(i),
        d = get_color_of_cell(i+1);

    var ruleid = (g == 1 ? 4 : 0) + (m == 1 ? 2 : 0) + (d == 1 ? 1 : 0);
    newtab[i] = rule[ruleid];
}

Ça a été fait par plusieurs personnes ici ;)

-
Edité par louk le 4 mars 2015 à 23:52:52

Ah ouai astucieux en effet ! Fallait être bien réveillé pour y penser par contre ! :p

-
Edité par damjuve 6 mars 2015 à 11:24:54

  • Partager sur Facebook
  • Partager sur Twitter
Si debugger, c’est supprimer des bugs, alors programmer ne peut être que les ajouter - Edsger Dijkstra
11 avril 2015 à 18:10:48

Salut !

débutant en programmation, je suis fortement intéressé par le jeu de la vie que je trouve vraiment très impressionnant.

mais n'ayant pas encore les bon automatismes, réflexe je bloque sur cette fonction "live" de Klafyvel :

int live(int gauche, int cell, int droite) // fonction qui fait vivre les cellules
{
    if((!gauche && (cell || droite))||(gauche && !cell && !droite)) // si elle doit vivre
    {
        return 1;
    }
    else // sinon elle meurt : P !
    {
        return 0;
    }
}

c'est surtout le regroupement de condition du if qui me terrifie xD

if((!gauche && (cell || droite))||(gauche && !cell && !droite))

je me doute que c'est une version simplifiée et générale qui prend en compte toute les conditions pour que la cell' vive à la prochaine génération.

Mais j'ai vraiment du mal à la comprendre, lors de mes essais perso, j'utilisais ce tableau pour faire des "If" cas par cas (je n'y arrivais pas d’ailleurs .... -_- ) :

Motif initial (t) 111 110 101 100 011 010 001 000
Valeur suivante de la cellule centrale (t+1) 0 0 0 1 1 1 1 0

.... Quelqu'un pourrais éclairer ma lanterne  ? :D

Merci d'avance, Kiki

  • Partager sur Facebook
  • Partager sur Twitter
13 avril 2015 à 11:45:55

@kiki :

En effet c'est une condition qui résume les regles. Je te la décortique dans l'ordre :

- Première condition : if (!gauche) && ((cell) || (droite))

(!gauche), c'est équivalent à (gauche != 1), ou encore (gauche == 0).

Donc on est dans le cas 5, 6, 7, ou 8. On remarque que tout ces cas doivent renvoyer 1, sauf le 8. La deuxieme partie de la condition (cell || droite) a donc pour but d'eliminer ce cas :

pour qu'on ne soit pas dans le cas 8, il faut que ni cell ni droite ne soit égal à 0.

(cell) est équivalent à (cell == 1), parreil pour (droite) équivalent à (droite == 1).

Donc si on récapitule : Si gauche = 0 et que ( ou bien cell ou bien droite ou bien les deux sont égaux à 1 ).

En langage informatique plus explicite : if (gauche == 0 && (cell == 1 || droite == 1))

- La deuxieme partie :

On a géré 3 des cas ou on renvoi 1, il en reste un, le 4e. Donc on le marque telquel :

if (gauche == 1 && cell == 0 && droite == 0)

Dans le cas ou l'une des deux parties est vraie on est bien dans le cas ou on renvoi 1, si non ducoup on renvoi 0 ;)



-
Edité par damjuve 13 avril 2015 à 11:51:51

  • Partager sur Facebook
  • Partager sur Twitter
Si debugger, c’est supprimer des bugs, alors programmer ne peut être que les ajouter - Edsger Dijkstra
13 avril 2015 à 15:00:43

Wow j'ai tout pigé :D

faut que je prenne l'habitude d’écrire des règles générale, j'ai trop l'habitude de faire cas par cas avec des tonnes de if, du coup ça devient vite du n'importe quoi x')

Merci ;)

  • Partager sur Facebook
  • Partager sur Twitter
13 avril 2015 à 16:01:50

Le défaut ici c'est que les conditions de vie ne sont pas éditables (à moins de recompiler).

Comme on l'a posté quelques message plus haut, il y a un algo qui prend en compte des règles variantes :

En mode pas très propre :

  for (var i = 0; i < tab.length; i++)
            {
                var ruleid = 7;
                 
                if (get_color_of_cell(i - 1) == 0 && get_color_of_cell(i) == 0 && get_color_of_cell(i + 1) == 0)
                    ruleid = 0;
                else if (get_color_of_cell(i - 1) == 0 && get_color_of_cell(i) == 0 && get_color_of_cell(i + 1) == 1)
                    ruleid = 1;
                else if (get_color_of_cell(i - 1) == 0 && get_color_of_cell(i) == 1 && get_color_of_cell(i + 1) == 0)
                    ruleid = 2;
                else if (get_color_of_cell(i - 1) == 0 && get_color_of_cell(i) == 1 && get_color_of_cell(i + 1) == 1)
                    ruleid = 3;
                else if (get_color_of_cell(i - 1) == 1 && get_color_of_cell(i) == 0 && get_color_of_cell(i + 1) == 0)
                    ruleid = 4;
                else if (get_color_of_cell(i - 1) == 1 && get_color_of_cell(i) == 0 && get_color_of_cell(i + 1) == 1)
                    ruleid = 5;
                else if (get_color_of_cell(i - 1) == 1 && get_color_of_cell(i) == 1 && get_color_of_cell(i + 1) == 0)
                    ruleid = 6;
                else
                    ruleid = 7;
                 
                newtab[i] = rule[ruleid];
            }

en mode simplifié :

for(i = 0 ; i < tab.length ; i++)
{
    var g = get_color_of_cell(i-1),
        m = get_color_of_cell(i),
        d = get_color_of_cell(i+1);
 
    var ruleid = (g == 1 ? 4 : 0) + (m == 1 ? 2 : 0) + (d == 1 ? 1 : 0);
    newtab[i] = rule[ruleid];
}




  • Partager sur Facebook
  • Partager sur Twitter
Si debugger, c’est supprimer des bugs, alors programmer ne peut être que les ajouter - Edsger Dijkstra
20 avril 2015 à 21:24:45

Bonjour je voudrait qu'on m'aide à lancer l'impression d'un résultat que me donne mon appli en c. Je suis tout nouveau.

  • Partager sur Facebook
  • Partager sur Twitter
22 avril 2015 à 9:48:40

ndji a écrit:

Bonjour je voudrait qu'on m'aide à lancer l'impression d'un résultat que me donne mon appli en c. Je suis tout nouveau.


L'impression, sur papier ?

Ou l'affichage ? (et dans ce cas comme dis pouet_forever : printf)

  • Partager sur Facebook
  • Partager sur Twitter
Si debugger, c’est supprimer des bugs, alors programmer ne peut être que les ajouter - Edsger Dijkstra
28 avril 2015 à 17:00:26

damjuve a écrit:

          L'impression, sur papier ?

Ou l'affichage ? (et dans ce cas comme dis pouet_forever : printf)

les deux. Merci



  • Partager sur Facebook
  • Partager sur Twitter
28 avril 2015 à 17:04:06

Euh et tu veux imprimer quoi ? un jeux de la vie ? Pour quoi faire Oo

Si tu veux tu peux mettre tout ça dans un fichier et imprimer le fichier, je pense que ça sera le plus simple si tu débute.

Si non poste ton code si tu veux plus d'aide, parce que là c'est compliqué de comprendre de quoi tu parle.

  • Partager sur Facebook
  • Partager sur Twitter
Si debugger, c’est supprimer des bugs, alors programmer ne peut être que les ajouter - Edsger Dijkstra
28 avril 2015 à 17:36:04

De toutes façons, ce n'est pas l'endroit pour poster ta question. Crée un nouveau sujet.
  • Partager sur Facebook
  • Partager sur Twitter
29 mai 2015 à 21:56:30

Je pense avoir trouvé une solution, mais j'ai 2 questions d'abord.

  • Est-ce que les colonnes extremes valent-elle toujours 0 ?

Si oui alors mon algo est bon mais j'ai un autre souci : 
le motif 0101010110 entraîne une boucle infinie.

 

  • la règle 30 signifie bien que le nombre doit valoir 30 pour arreter l'automate ??

-
Edité par blixit 29 mai 2015 à 22:00:15

  • Partager sur Facebook
  • Partager sur Twitter
29 mai 2015 à 22:55:39

Tu as plusieurs possibilités pour les cellules/colonnes aux extrémités:

- soit elles gardent une valeur fixe 0 ou 1

- soit l'automate est circulaire, la première cellule est donc la voisine de la dernière (et vice-versa)

- soit le motif est considéré comme infini (c'est ce que suppose la théorie des automates cellulaires élémentaires si tu lis les articles associés sur Wikipedia & autres sources)

- (autres) ?

Ensuite la règle 30 ne signifie pas qu'on s'arrête lorsque le pattern est égal à cette valeur, tu exécutes ton automate sur le nombre de générations que tu veux. La valeur 30 définit les règles de transition entre chaque génération. C'est l'une des 256 règles existantes pour les automates cellulaires élémentaires.

Tu peux très bien trouver des cycles de répétition du même motif quelque soit la convention pour les cellules aux extrémités, suivant laquelle des 256 règles tu utilises.

Je t'invite à relire l'exposé de l'exercice et de consulter les liens fournis par paraze où tout est bien expliqué.

-
Edité par fromvega 29 mai 2015 à 23:08:15

  • Partager sur Facebook
  • Partager sur Twitter
29 mai 2015 à 23:06:44

Merci pour l'explication sur la regle 30. J'avais pas pris le temps de consulter les liens.
il ne me reste plus qu'à rendre mon automate circulaire ou infini pour ne plus rentrer dans cette satanée boucle infinie.
Merci encore.
  • Partager sur Facebook
  • Partager sur Twitter
30 mai 2015 à 1:12:06

Ca y est c'est fait!! 
Mon automate est circulaire. Et je pense que le programme n'est pas 'assez' rapide. J'ai calculé le temps d'exécution avec clock et GetTickCount pour 10 cellules .  Jusqu'a 1.000.000 de générations en  (ancien 0.3s) 0.26s. A 10.000.000 je passe à (ancien 3.7s) 2.8s.  :'(
(EDIT : Mais 3s ca fait beaucoup comparé au 1.6s que je viens de voir chez Nelxiost)

A 30 cellules et  1.000.000 de générations, j'ai un temps de (ancien 1.6 s) 0.77s.
A 30 cellules et  10.000.000 de générations, j'ai un temps de (ancien 16 s) 7.9s.  :'( 

EDIT : le programme gère les 256 règles.   

Je peux améliorer les temps si je peux écrire cette gourmande expression plus efficacement : 
(((ptRs&2)&&0xFF)<<2) + (((ptRs&1)&&0xFF)<<1 ) + ((ptRs&(1<<xZe-1))&&0xFF)
Le hic de ma méthode c'est que je code sur des entiers plutot que sur des tableaux de char. Ca implique que je suis bloqué à 30 cellules (ou 31 si j'utilise uint).

 

-
Edité par blixit 30 mai 2015 à 4:28:37

  • Partager sur Facebook
  • Partager sur Twitter
30 mai 2015 à 3:03:54

Le code : 
En changeant le tableau result par un UCHAR j'ai réduit mes temps d'une sec. La lecture impacte donc considérablement l'exécution du programme.
Merci à Nelxiostpoupou9779 et Pouet_forever. Vos discussions m'ont permis de le constater. mais 2.9 c'est toujours enorme. :'(

int main(){
    int mot = 0b110001;
    int tmpmot = 0;
    int i,j,generations=10000000;
    int nbCellules = 30; //borné par int
    int cpt = 0; 
    //avant bool result[8] = {0,1,1,1,1,0,0,0};
    UCHAR result = 0x1E; //regle 30
    clock_t start, finish;
    DWORD t1, t2;

    printf("Configuration initiale : ");
    writei(mot);

    printf("\nNombre de cellules : %d\n",nbCellules);
    printf("Nombre de generations : %d\n",generations);
    printf("Génération actuelle : %d\n\n",generations);
    system("pause");
    start = clock();
    t1 = GetTickCount();
    while(generations--){
        tmpmot = 0;
        cpt = 0;
        for(i=nbCellules-1; i>=0; i--){
            if(i==nbCellules-1){
                cpt =  (((mot&1)&&0xFF)<<2)
                + (((mot&(1<<nbCellules-2))&&0xFF)<<1)
                + ((mot&(2<<nbCellules-2))&&0xFF);
            }
            else if(i==0){
                cpt = (((mot&2)&&0xFF)<<2)
                + (((mot&1)&&0xFF)<<1 )
                + ((mot&(1<<nbCellules-2))&&0xFF);
            }
            else{
                cpt = (((mot&(1<<i+1))&&0xFF)<<2)
                +(((mot&(1<<i))&&0xFF)<<1)
                +((mot&(1<<i-1))&&0xFF);
            }
            if(cpt)
               //avant tmpmot |= resultt[cpt]<<i;
               tmpmot |= ((result&(1<<cpt))&&0xff)<<i;  
        }

        mot = tmpmot; 
    }
    finish = clock();
    t2 = GetTickCount();
    printf("Temps d'exécution : %.4f s\n",(float)(finish-start)/CLOCKS_PER_SEC);
    printf("Temps d'exécution : %d ms\n\n",t2-t1);
    printf("Configuration finale : ");
    writei(mot); 
    printf("\nGénération actuelle : %d\n",generations);

    return 0;
}



-
Edité par blixit 30 mai 2015 à 4:29:08

  • Partager sur Facebook
  • Partager sur Twitter
2 juin 2015 à 14:15:21

Moi ça me parait normal comme temps. Tu as quoi comme processeur ? J'ai pas testé les performances sur mon code.

-
Edité par Escargotine 2 juin 2015 à 15:04:38

  • Partager sur Facebook
  • Partager sur Twitter
2 juin 2015 à 15:24:30

Processeur : AMD 64 1.60 Ghz.
T'as les memes temps sur ta machine ? t'as quoi comme processeur ?
  • Partager sur Facebook
  • Partager sur Twitter
2 juin 2015 à 16:13:28

Bah ça me parait normal que ça mette ce temps-là. C'est un 64 bits et il n'y a qu'un seul coeur ? J'ai toujours pas essayé sur le mien, j'ai un peu la flemme :-° (je suis sur autre chose, là).

J'imagine que sur le mien ça mettrait moins d'une seconde, vu que j'ai un i7 3630 (4 coeurs, 2.4-3.4 Ghz).

-
Edité par Escargotine 2 juin 2015 à 16:38:49

  • Partager sur Facebook
  • Partager sur Twitter
2 juin 2015 à 16:49:48

Salut, est ce que tu peux ajouter les includes qu'on puisse tester ;)
  • Partager sur Facebook
  • Partager sur Twitter
Si debugger, c’est supprimer des bugs, alors programmer ne peut être que les ajouter - Edsger Dijkstra
2 juin 2015 à 18:57:44

Le mien aussi a 4 coeurs! 
Je commence a me demander si les autres ont fait un automate circulaire ou infini et si la différence  joue sur les temps de réponses.
Mais bon!!
J'ai fait l'exo au meilleure de mes capacités actuelles, c'est ça qui compte.

EDIT : voici les includes et la fonction writei :
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <time.h>
using namespace std;
 
static void writei( int b){
    int i = 0; 
    for(i=32; i>=0; i--)
        printf("%d",(b & (1<<i) ? 1 : 0));  
    printf(".\n");
}

-
Edité par blixit 2 juin 2015 à 19:01:08

  • Partager sur Facebook
  • Partager sur Twitter
3 juin 2015 à 9:52:46

windows.h

Aïe, c'est là que nos chemins ce sépare :p

Sinon c'est du c ou c++, ça a l'aire un peu le bazare ton code.

  • Partager sur Facebook
  • Partager sur Twitter
Si debugger, c’est supprimer des bugs, alors programmer ne peut être que les ajouter - Edsger Dijkstra
3 juin 2015 à 12:15:12

Je teste les perfs de mon code et je dis ce que ça donne (1.000.000 et 10.000.000). Je précise aussi que je suis sous LinuxMint Debian Edition (ça devrait logiquement être un petit peu plus performant que sur Windows (parce que je peux aussi essayer sur Windows)).

EDIT : Tu fais les tests sur 32 colonnes (ou bits) apparemment, je vais faire aussi sur 32.

EDIT : Pour 1.000.000 générations, j'ai 0.25 s. Pour 10.000.000, j'ai 2.31 s.
Comme j'ai un meilleur processeur, c'est normal que j'ai des performances un peu meilleures. À mon avis, ton code ne peut pas être beaucoup plus optimisé (mais je pense qu'il doit y avoir encore un ou deux trucs à améliorer).
Si tu veux voir mon code, va en page 5.

-
Edité par Escargotine 3 juin 2015 à 13:05:14

  • Partager sur Facebook
  • Partager sur Twitter
3 juin 2015 à 12:52:26

damjuve a écrit:

windows.h

Aïe, c'est là que nos chemins ce sépare :p

Sinon c'est du c ou c++, ça a l'aire un peu le bazare ton code.

C'est quoi cette réaction négative de débutant ?? Analyse un peu le code.

Windows c'est juste pour gettickcount, t1 et t2. Mais tu peux les virer.
UCHAR peut etre remplacer par unsigned long char.

iostream et namespace std vient du fait que j'ai ouvert un projet c++ et non c. Tu peux les virer. Le reste est en C.

louk a écrit:

EDIT : Tu fais les tests sur 32 colonnes (ou bits) apparemment, je vais faire aussi sur 32.

Non sur 30 colonnes mais c'est pas loin.
Petite astuce : tu peux changer int en double pour avoir 64 bits et avoir plus de marge.


-
Edité par blixit 3 juin 2015 à 13:08:41

  • Partager sur Facebook
  • Partager sur Twitter
3 juin 2015 à 13:07:59

Pour en revenir à tes résultats, 2.9s ça me parait vraiment raisonnable pour 10 000 000 de générations. C'est quand même 10 000 000 de fois les calculs du nouvel état.

EDIT : Ah, dans ce cas je vais faire des tests sur 30 colonnes. Mais plutôt que des doubles, j'aurais utilisé des long long (entiers sur 64 bits).

Pour 30 bits, j'ai 0.25 s et 2.15 s. Au moins c'est cohérent.

Pour rebondir sur "ça m'a l'air un peu le bazar" (en version corrigée :p), c'est l'indentation qui fait cet effet. Ça pourrait être plus espacé.

-
Edité par Escargotine 3 juin 2015 à 14:52:07

  • Partager sur Facebook
  • Partager sur Twitter
4 juin 2015 à 8:13:04

0.25s et 2.15 pour 30 bits !! wowwww!! trop cool!
de 0.77 et 7.9 à ça ??!!

Je suis assez surpris.
j'ai intérêt donc a changer de PC!!!

J'ai regardé ta version elle est de type infinie, n'est-ce pas ? En gros tu ne gères pas la boucle entre le 1er et le dernier bit ?!

Si je fais pareil je crois que je vais certainement amélioré mes perfs ! J'ai l'impression que les tests sur les bits extremes ralentissent l'algo.
  • Partager sur Facebook
  • Partager sur Twitter
6 juin 2015 à 4:12:47

Coucou, comme je n'arrive pas à dormir, voici ma participation:

#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

typedef int (*parser)(void*, const char*);

static int
auto_match(unsigned int save, unsigned int rule)
{
    return (rule >> save) & 1;
}

static char*
auto_step(char* cells, unsigned int rule)
{
    unsigned int save = cells[0] - '0';
    for(size_t i = 0; cells[i] != '\0'; ++i)
    {
        save = (save << 1) | (cells[i + 1]? cells[i + 1] - '0': 0);
        cells[i] = '0' + auto_match(save & 0x07, rule);
    }
    return cells;
}

static int
optparse(int argc,
         char* argv[],
         const char* optstring,
         void* (*tab)[UCHAR_MAX],
         parser (*parse)[UCHAR_MAX])
{
    int c;
    while((c = getopt(argc, argv, optstring)) != -1)
    {
        switch(c)
        {
            case ':':
            case '?':
                return c;
            default:
            {
                size_t i = c;
                if((*tab)[i] != NULL && (*parse)[i]((*tab)[i], optarg) == -1)
                {
                    return -1;
                }
            }
        }
    }
    return 0;
}

static int
read_ulong(unsigned long* n, const char* s)
{
    char* end;
    unsigned long value = strtoul(s, &end, 0);
    if(end == s || *end != '\0' || (value == ULONG_MAX && errno == ERANGE))
    {
        return -1;
    }
    *n = value;
    return 0;
}

int
main(int argc, char* argv[])
{
    unsigned long count = 25, rule = 30;
    void* opt[UCHAR_MAX] = {['c'] = &count, ['r'] = &rule};
    parser parse[UCHAR_MAX] = {['c'] = read_ulong, ['r'] = read_ulong};
    switch(optparse(argc, argv, ":c:r:", &opt, &parse))
    {
        case ':':
            fprintf(stderr, "Option -%c takes an argument.\n", optopt);
        case '?':
            fprintf(stderr, "Usage: %s [-r rule] [-c count] init\n", argv[0]);
            return EXIT_FAILURE;
        case -1:
            fprintf(stderr, "Failed to parse %s.\n", optarg);
            return EXIT_FAILURE;
    }
    for(int i = optind; i < argc; ++i)
    {
        if(argv[i][strspn(argv[i], "01")] != '\0')
        {
            fprintf(stderr, "Bad initial value: %s.\n", argv[i]);
            continue;
        }
        for(size_t g = 0; g < count; ++g)
        {
            puts(argv[i]);
            auto_step(argv[i], rule);
        }
    }
    return EXIT_SUCCESS;
}

Bon actuellement le code est assez crade mais je simplifierais ça demain.:)

La règle et le nombre de générations sont données en paramètres avec -r et -c respectivement.

La configuration initiale peut contenir autant de cellules que de caractère que vous pouvez passer en argument au programme.

-
Edité par Mad scientist 6 juin 2015 à 4:20:39

  • Partager sur Facebook
  • Partager sur Twitter
Un vrai cours de: (C | C++ | Haskell débutant | Haskell intermédiaire | Rust).
9 juin 2015 à 10:11:40

Sinon pour la nouvelle version, c'est ici (Plus complet, prend en compte les arguments comme --help, --usage, --version, les arguments longs comme --line ou --rule, aussi bien que les arguments courts (-l, -r), est un peu plus propre, mais toujours assez lent, j'essayerais d'améliorer ça)

Comment ça j'avais dit que je ferais ça le lendemain de mon dernier message ? :-°

-
Edité par Mad scientist 8 novembre 2016 à 23:11:59

  • Partager sur Facebook
  • Partager sur Twitter
Un vrai cours de: (C | C++ | Haskell débutant | Haskell intermédiaire | Rust).