Passer de h=(b-a) /n à n= ? dans un programme

gebrane
Modifié (February 2023) dans Informatique théorique
Bonjour,

Je subdivise [a, b] avec le pas h=(b-a)/n . avec n un entier et a, b et h  des réels 
Supposons connaitre h par exemple 0.01. comment implémenter le n dans un programme c

n = (int) ((b-a) / h) ;

ou 

n = (int) ((b-a) / h)+1 ;

car on a le problème maths si h=(b-a)/n, on ne peut pas dire que n=(b-a)/h.

Le 😄 Farceur


Réponses

  • Je ferais ceil((b-a) / h) (avec b > a) en sachant que le dernier intervalle peut avoir une taille différente de h
    Il ne faut pas respirer la compote, ça fait tousser.

    J'affirme péremptoirement que toute affirmation péremptoire est fausse
  • dp
    dp
    Modifié (February 2023)
    Si comme tu le dis $n$ est un int et que $a$, $b$ et $h$ sont des float alors je ferais juste n=(b-a)/h; car en c le cast se fera automatiquement.
    #include <stdio.h>
    
    int main(void) {
        int n ;
        float h, a, b ;
        a = 3.3;
        b = 8.9 ;
        h = 0.01 ;
    
        n = (b-a) / h;
    
        printf("%d\n", n) ;
    }
    affiche 559.
  • dp j'ai vu d'autres qui mettent  
    n = (b-a) / h +1

    Le 😄 Farceur


  • dp
    dp
    Modifié (February 2023)
    Tout dépend de la précision que tu veux. Tu peux remarquer que dans mon code, le résultat n'est pas juste : 559 au lieu de 560. Par contre, l'inverse est aussi vrai : dans certains cas tu te retrouveras avec le bon résultat et donc avec le risque d'avoir n+1.
    Au pire tu peux gérer les  différents cas en jouant avec fmod(v,1) pour vérifier la partie décimale de ton float et adapter selon celle-ci. Un peu comme cela :
    int cast(float a, float b, float h) {
        float tmp = (b-a)/h ;
        return fmod(tmp,1) < 0.5 ? (int)tmp : (int)tmp+1 ;
    }
    
  • lourrran
    Modifié (February 2023)
    Soit la solution de Mediat-supreme, soit, pour les allergiques à la fonction ceil :
    n = (b-a)/h
    if n*h <(b-a) {
      n++
    }
    Tu me dis, j'oublie. Tu m'enseignes, je me souviens. Tu m'impliques, j'apprends. Benjamin Franklin
  • dp
    dp
    Modifié (February 2023)
    Utiliser ceil est selon moi une erreur. Si le résultat est par exemple 55.4, tu te retrouveras avec 56 et pas 55. De même avec floor qui renverra 55 pour 55.6 au lieu de 56.
  • Voici un comparatif
    #include <stdio.h>

    int main(void) {
        int n  ;
        int N  ;
        float h, a, b, h1, h2 ;
        a = 3.3;
        b = 8.9 ;
        h = 0.01 ;
        n = (b-a) / h ;
        N = ((b-a) / h)+1;
        h1=  (b-a) / n;
        h2=  (b-a) / N;

        printf("%d\n" "%d\n" "%lf\n" "%lf\n" , n, N , h1, h2 ) ;

    Les résultats
    559
    560
    0.010018
    0.010000




    Le 😄 Farceur


  • dp
    dp
    Modifié (February 2023)
    Utilise la fonction cast que je t'ai écrite plus haut et tu auras le bon résultat à chaque fois, ramené à l'entier le plus proche. (ps. attention, j'ai édité pour enlever le = qui était en trop).
  • Je viens de voir ce post

    Le 😄 Farceur


  • Je ne comprends pas un truc selon que je déclare n un entier ou un float

    #include <stdio.h>
    int main(void) {
        int n  ;

        float  a, b, h, N ;
        a = 3.3;
        b = 8.9 ;
        h = 0.01 ;
        n = (b-a) / h ;
        N = (b-a) / h ;
         printf("%d\n" "%lf\n"   , n, N ) ;
    }

    J 'ai les résultats 
    559
    559.999939
    Qu'est-ce qui est précis ?


    dp un truc peux-tu m'expliquer le rôle de int avant main et le rôle de void après main
    int main(void) {
    Le 😄 Farceur


  • dp
    dp
    Modifié (February 2023)
    En informatique il y a toujours des erreurs de précision sur les réels. C'est la raison pour laquelle tu n'obtiens pas 560.0 mais 559.999939. La conversion automatique en int est en réalité une troncature et donc récupère simplement la partie entière de ton réel : 559.
    En C main doit toujours renvoyer un entier : 0 si aucune erreur ne s'est produite, un entier définit s’il y a eu une erreur d'où int main(). Usuellement si une erreur se produit et qu'on n'a pas déjà défini de code on utilise 1 ou -1. int main(void) pour indiquer que main ne prend par d'argument, mais tu peux utiliser int main() si tu veux, cela revient au même.
  • lourrran
    Modifié (February 2023)
    Au vu du besoin initial, on avait a, b et h fixés. Et on cherchait n pour que a+n*h atteigne ou dépasse b.
    Le but n'était pas de recalculer h après coup.
    C'était écrit : 
    Je subdivise [a, b] avec le pas h=(b-a)/n . avec n un entier et a, b et h  des réels 
    Supposons connaitre h par exemple 0.01. comment implémenter le n dans un programme c
    Ici, tu recalcules h, et tu fais tes tests en prenant un cas assez particulier : la division de b-a par h tombe juste  (à part les problèmes de précision).
    À partir des données a=1, b=2 et h= 0.3, tu veux obtenir quelle valeur pour n ? 3 ou 4.
    Tu me dis, j'oublie. Tu m'enseignes, je me souviens. Tu m'impliques, j'apprends. Benjamin Franklin
  • Quand même lourran, on choisit n qui rend fidèlement le h de la formule h=(b-a)/n. d'où la légitimité des tests.

    Le 😄 Farceur


  • Dans ce cas ma fonction cast est ce qu'il te faut car tu obtiendras à chaque fois l'entier le plus proche par distance.
  • gebrane
    Modifié (February 2023)
    dp pour toi c'est facile  :D  , j'essaie de voir le comment pour utiliser cette fonction 
    Le 😄 Farceur


  • dp
    dp
    Modifié (February 2023)
    Bah juste en faisant
    n = cast(a,b,h);
    :D
    Et si jamais tu cherches fmod, il faut inclure math.h
    #include <math.h>
  • On a aussi la formule  round(x)=floor(x+1/2)
  • dp a dit :
    Utiliser ceil est selon moi une erreur. Si le résultat est par exemple 55.4, tu te retrouveras avec 56 et pas 55. 
    C'est l'idée, afin de parcourir tout l'intervalle ; pour découper [0; 1] en intervalles de taille 0.1, il faut 10 subdivisions (facile) et donc le +1 est faux, 
    pour découper [0; 1.03] en intervalles de taille 0.1, c'est clairement impossible, on peut choisir :
    1. Ajuster la taille des subdivisions
    2. Accepter de ne pas aller au bout
    3. Accepter de dépasser
    4. Aller au bout, mais la dernière subdivision est plus petite que les autres
    La 2) s'obtient par un floor, la 3) et la 4) par un ceil, la 1) s'obtient par petit calcul trivial : on calcule n par floor, ceil ou round, puis on pose h = (b-a)/n

    Le +1 systématique est toujours fautif

    Il ne faut pas respirer la compote, ça fait tousser.

    J'affirme péremptoirement que toute affirmation péremptoire est fausse
  • dp
    dp
    Modifié (February 2023)
    Certes. Toutefois je me place toujours dans le cas où @gebrane enseigne tout ceci à ses étudiants et je cherche à lui donner le code le plus simple et facile à comprendre (encore que, à trop me creuser la tête pour trouver un truc "facile" je me suis éloigné de cette voie tandis que @Boécien a fait plus simple), et surtout, dont le comportement se rapproche finalement le plus possible de ce que ces derniers peuvent attendre.
  • tu etais plus rapide
    #include <stdio.h>

    int main(void) 
    {
        int n  ;
        float  a, b, h, N ;
        a = 3.3;
        b = 8.9 ;
        h = 0.01 ;
        
        int cast(float a, float b, float h) {
        float tmp = (b-a)/h ;
        return fmod(tmp,1) < 0.5 ? (int)tmp : (int)tmp+1 ;
        }
        printf("%d\n", cast(a,b,h)) ;
    }
    Le 😄 Farceur


  • dp
    dp
    Modifié (February 2023)
    @gebrane Écris comme tu l'as fait cast est locale à main et ne pourra être utilisée que dans cette dernière (et après avoir été défini). Considère donc plutôt de le faire comme ceci :
    #include <stdio.h>
    #include <math.h>
    
    int cast(float a, float b, float h) {
        float tmp = (b-a)/h ;
        return fmod(tmp,1) < 0.5 ? (int)tmp : (int)tmp+1 ;
        // ou comme @"Boécien" l'a écrit
        // return floor(tmp+1/2) ;
    }
    
    int main(void) {
        int n ;
        float h, a, b ;
        a = 3.3;
        b = 8.9 ;
        h = 0.01 ;
    
        n = cast(a,b,h);
    
        printf("%d\n", n) ;
    
        return 0;
    }
    

  • dans ce exemple ca rend 232 pourquoi? car on est dans la 231,5

    #include <stdio.h>

    int main(void) 
    {
        int n  ;
        float  a, b, h, N ;
        a = 1;
        b = 3.315 ;
        h = 0.01 ;
        
        int cast(float a, float b, float h) {
        float tmp = (b-a)/h ;
        return fmod(tmp,1) < 0.5 ? (int)tmp : (int)tmp+1 ;
        }
        printf("%d\n", cast(a,b,h)) ;
    }
    Je dois comprendre ce genre de questions pour ne pas me faire ridiculisé
    Le 😄 Farceur


  • dp
    dp
    Modifié (February 2023)
    Parce que j'ai choisi avec fmod(tmp,1) < 0.5 de considérer que tous les nombres de [x.50, x+1[ donneraient x+1.
  • Je repose ma question, restée sans réponse : 
    si a=1 et b=2 et h=0.3, on veut obtenir quelle valeur pour n par cet 'outil' .

    J'insiste parce que cette question me paraît essentielle. D'ailleurs, Mediat-Supreme a résolu le truc, en disant : si on veut tel résultat, voici la recette, et si on veut tel autre résultat, voici l'autre recette.

    Un problème bien posé est un problème à moitié résolu.
    Tu me dis, j'oublie. Tu m'enseignes, je me souviens. Tu m'impliques, j'apprends. Benjamin Franklin
  • lourrain tu as donné un mauvais exemple car dans ton cas le (b-a)/h est d 'office un entier, l entier 3
    Le 😄 Farceur


  • Donc la fonction ceil() n'est pas adaptée. Tu veux un arrondi-inférieur, ou peut-être un arrondi à l'entier le plus proche. Tu ne veux pas un arrondi supérieur.
    Tu me dis, j'oublie. Tu m'enseignes, je me souviens. Tu m'impliques, j'apprends. Benjamin Franklin
  • Surtout je ne veux pas sortir de l'intervalle d'étude [0,T], je crois que l'approche de dp est le mieux
    Le 😄 Farceur


  • dp
    dp
    Modifié (February 2023)
    C'est surtout qu'il s'agit de la manière la plus "naturelle" lorsqu'on veut approcher un réel par un entier : prendre l'entier le plus "proche", justement.
  • gebrane
    Modifié (February 2023)

    J'ai implémenté ce cast dans mon programme. Cependant, j'ai une question : comment faire un graphique sous C ? J'ai cherché sur le net et j'ai trouvé gnuplot. Est-ce une façon débile de représenter un ensemble de mesures ?

    Ci-dessous, vous trouverez un programme qui modélise la cinétique d'une réaction d'ordre 2. La concentration obéit à l'équation différentielle $C'(t)=-kC(t)^2$ avec une condition initiale $C(0)$. j'ai programmé le problème  $y'(t)=-y²(t),\quad y(0)=1$ sur [0,T] avec h=0.03mn(1.8s) et T =20mn


    #include <stdio.h>
    #include <math.h>

    double f(double x, double y)
    {
        return -y * y +x ; // j'ai ajouté un x pour rendre difficile la recherche d'une solution analytique
    }
    int cast(double T, double h) {
        double tmp = T/h ;
        return fmod(tmp,1) < 0.5 ? (int)tmp : (int)tmp+1 ;
        }
    int main() {
        double  h = 0.03;
        double  T=20;
        int n ;
        n=cast(T,h);
        double k1,k2,k3,k4, x[n+1], y[n+1];
        x[0] = 0, y[0] = 1;
        for (int i = 0; i <= n - 1; i++) {
            x[i] = i* h;
            k1 =  f(x[i], y[i]);
            k2 =  f(x[i] + 0.5*h, y[i] + 0.5*h*k1);
            k3 =  f(x[i] + 0.5*h, y[i] + 0.5*h*k2);
            k4 =  f(x[i]+h, y[i]+h);
            y[i + 1] = y[i] +  h*(k1 + 2*k2 + 2*k3 + k4)/6;

            }

        FILE *fp;
        fp = fopen("data1.dat", "w");
        for (int i = 0; i < n; i++) {
        fprintf(fp, "%lf %lf %lf\n", x[i], y[i]);
        }
        fclose(fp);
        FILE *gnuplotPipe = popen("\"C:\\Program Files\\gnuplot\\bin\\gnuplot\" -persistent", "w");

        fprintf(gnuplotPipe, "set xlabel 'x'\n");
        fprintf(gnuplotPipe, "set ylabel 'y(x)'\n");
        fprintf(gnuplotPipe, "plot 'data1.dat' using 1:2 with lines title 'Runge Kutta4\n");
        fflush(gnuplotPipe);
        return 0;
    }

    Le 😄 Farceur


  • Bonjour @AD comment faire pour coder le texte d'un programme
    Le 😄 Farceur


  • ce n'est pas de refus ce programme en rust ou ada ou Xcas  ou Java
    Le 😄 Farceur


  • Bah aidé de tout ce que j'ai déjà écrit en rust ici tu peux essayer de le faire toi-même. :) Tu peux aussi m'envoyer des messages privés pour me demander de l'aide si jamais tu coinces.
  • Je collectionne les programmes. je ne vais pas étudier rust pour le moment  sinon je vais me perdre entre  C et rust 
    Je n'oblige personne que cela soit claire
    Le 😄 Farceur


  • double f(double x, double y)
    {
        return -y * y ;
    }
    À quoi sert le $x$ ?
  • gebrane
    Modifié (February 2023)
    car j 'avais en fait programmé des équations du type $y'(x)=-y^2(x)+g(x)$
    je vais ajouter un x dans la fonction pour rendre la recherche d'une solution exacte impossible  :D
    Le 😄 Farceur


  • dp
    dp
    Modifié (February 2023)
    use gnuplot::{Figure, Caption, Color} ;
    
    fn f(_x: f64, y:f64) -> f64 {
        -y*y
    }
    
    fn main() {
        let (h,t): (f64,f64) = (0.03, 20.0) ;
        let n: usize = (t/h) as usize;
    
        let mut k: Vec<f64> = vec![0.0; 4] ;
        let mut x: Vec<f64> = vec![0.0; n] ;
        let mut y: Vec<f64> = vec![1.0; n+1] ;
    
        for i in 0..n {
            x[i]   = h*i as f64 ;
            k[0]   = f(x[i], y[i]);
            k[1]   = f(x[i] + 0.5*h, y[i] + 0.5*h*k[0]);
            k[2]   = f(x[i] + 0.5*h, y[i] + 0.5*h*k[1]);
            k[3]   = f(x[i]+h, y[i]+h);
            y[i+1] = y[i] + h*(k[0]+2.0*k[1]+2.0*k[2]+k[3])/6.0 ;
        }
    
        let mut fig = Figure::new();
        fig.axes2d().lines(&x, &y, &[Caption("Runge Kutta"), Color("blue")]) ;
        fig.show().expect("An error occurs when generating the plot...") ;
    }

  • Il est stylé  rust. Beau à  regarder. J'ai hâte à étudier ce langage.  Merci
    Le 😄 Farceur


  • dp
    dp
    Modifié (February 2023)
    Ce n'est pas pour rien que je t'ai recommandé de l'utiliser avec tes étudiants, ou bien même go qui en réalité serait sans doute mieux pour ces derniers. Plein de soucis (ou avantages, dépend d'où tu te places) du c disparaissent avec rust. Celui-ci te permet de te concentrer sur le fait de programmer ce qu'y t’intéresse plutôt que de passer ton temps à comprendre pourquoi ça ne fonctionne pas comme tu le souhaites…
    Ainsi, par principe de moindre surprise par exemple, pas besoin de s'embêter avec les erreurs de précisions en rust; dans les petits cas comme celui qui nous intéresse, il le gère de manière attendue pour nous.
Connectez-vous ou Inscrivez-vous pour répondre.