Conversione Decimale-Binario

di il
8 risposte

Conversione Decimale-Binario

Buongiorno,ho un problema da risolvere,il problema è il seguente:

Dato il numero "numero positivo intero con 40+ cifre",calcolare quanti "1" ha la sua conversione in binario.

Ora,dato che in c++ il numero massimo positivo rappresentabile è in formato unsigned long long int,e ha un limite, che è evidentemente superato dal numero in questione,ho avuto non pochi problemi a scrivere una soluzione.

Ho fatto varie ricerche, e provato varie idee ma dopo giorni non riesco a proprio a trovare niente che funzioni,perciò vorrei sapere se qualcuno ha un'idea,o se magari c'è un metodo specifico per farlo che mi sfugge.

Una mia idea era anche quella di usare le stringhe/char,oppure di inserire le cifre in un vettore e lavorare sulle singole cifre,però in quest'ultimo caso mi manca proprio l'idea dell'algoritmo da implementare,sempre se esiste...
Aspetto idee,grazie.

8 Risposte

  • Re: Conversione Decimale-Binario

    Ciao
    un idea che potresti usare e quella di dividere il numero iniziale in fattori cosi da poterlo ridurre a dimensioni accettabili.
    nel malaugurato caso che il numero iniziale sia primo allora ti consiglio di scompattarlo nel seguente modo:
    sottrai al numero iniziale I° cifra + 39 zeri e ti segni l'operazione effettuata
    verifichi se il numero restante e fattorabile se non lo è continui con la sottrazione II° cifra + 38 zeri
    e cosi via fino a quando o non sei riuscito a fattorizzare il numero o tramite le sottrazioni non sei riuscito a ridurre le dimensioni ad un numero accettabile da poter convertire.
    poi sommi tutti gli "1" delle operazioni effettuate.
    ti faccio una piccola tabella cosi puoi capire lo schema degli "1" delle operazioni di sottrazione.
    1000 = 1111101000
    10000 = 10011100010000
    100000 = 11000011010100000
    1000000 = ?11110100001001000000?
    .............
    .............
    1.000.000.000 = 111011100110101100101000000000

    spero di esserti stato di aiuto
  • Re: Conversione Decimale-Binario

    Ciao,in questi giorni ho scritto un programma,e funziona abbastanza bene con numeri che hanno cifre comprese tra 0 e 30/31 con numeri più grandi ci mette troppo tempo(anche ore).
    Il codice è una prima stesura,funziona ma vorrei dei consigli per migliorarlo,in particolare la velocità di esecuzione,ad esempio velocizzando le funzioni sostituendole con altre equivalenti ma con un costo (in tempo) minore.
    l'idea comunque è questa:
    es.
    1-Numero:100 (Inserito in una stringa)
    2-Con una funzione lo trasformo in un vettore contenente ogni sua singola cifra es.V=(1,0,0)
    3.Chiamo una funzione decTobin_vec che dovrà restituire un vettore col numero iniziale in binario:
    Questa funzione (utilizzandone altre),divide ogni cifra del numero,prese singolarmente dal vettore
    V=(1,0,0),in binario,assegnando ad ogni numero binario lo stesso numero di cifre.
    es.Vdec=(1,0,0)-> V[0]bin=(00000001) , V[1]bin=(00000000) , V[2]bin=(00000000) *Sempre 8 cifre,nel programma uso un costante VECPARTITION=128

    Poi inserisco tutti i risultati in un unico vettore con un pushback, es.W=(000000010000000000000000)

    4)Da questo punto in poi l'idea è la seguente:

    Ndec=100=(1*10^2+0*10^1+0*10^0)=(1*2^2*5^2+0*2^1*5^1+0*2^0*5^0)=(00000001*00000100*00011001+00000000*00000010*00000101+00000000*00000001*00000001)

    -I vettori con le cifre del numero li ho già e con una funzione slice selezione ogni volta le seguenti 8,e una alla volta le moltiplico per il fattore 10(2*5,calcolato con pow(),e a sua volta convertito in un vettore di numeri binari),corrispondente.

    -E in fine sommo tutto.
    -Il risultato è il vettore di 0 e 1 corrispondente al numero decimale iniziale.

    -Ogni somma e moltiplicazione è fatta con delle funzioni che fanno le operazioni con i vettori di numeri binari come se le facessimo noi su un foglio quindi seguono le stesse regole,per ovviare alla limitazione della rappresentazione con unsigned long long.

    -Oltre fattori maggiori a 5^22 c'era un errore sulla rappresentazione del numero quindi con cifre del numero decimale iniziale maggiore a 23,il fattore successivo viene fatto con le opportune somme in binario di 5^22.

    Gradirei altre idee,consigli,suggerimenti,miglioramenti,per migliorare la qualità del codice,la velocità e per consentire di rappresentare in minor tempo valori maggiori,grazie.

    Il codice forse è poco chiaro nel caso sarei disponibile per chiarimenti per chi fosse interessato ad aiutarmi.
    
    #include <iostream>
    #include <utility>
    #include <vector>
    #include <cmath>
    #include <algorithm>
    #include <chrono>
    
    void decTobin_vec(std::vector<unsigned short int> &N, std::vector<unsigned short int> &n);
    
    std::vector<unsigned short int> *decTobin(unsigned long long int cifra);
    
    std::vector<unsigned short int> *
    mult_bin_vec(std::vector<unsigned short int> &b1, std::vector<unsigned short int> &b2);
    
    std::vector<unsigned short int> *sum_bin_vec(std::vector<unsigned short int> &b1, std::vector<unsigned short int> &b2);
    
    std::vector<unsigned short int> *stringTovec(const std::string &numstring);
    
    unsigned short const int VECPARTITION = 128;
    
    int main() {
        auto start = std::chrono::steady_clock::now();
        //std::vector<unsigned short int> N = *stringTovec("938571980750874560846238947123908412358019875");
        // <<Questo ci dovrebbe mettere circa 2 ore per convertirsi>>
    
        std::vector<unsigned short int> N = *stringTovec("999999999999999999999999999999999");
        //Ogni cifra aumenta il tempo di esecuzione di circa un fattore 5x
    
        std::vector<unsigned short int> n = {};
    
        for (unsigned short i : N)
            std::cout << i;
        std::cout << std::endl << std::endl;
        std::cout << "Size:" << N.size() << std::endl;
    
        decTobin_vec(N, n);
    
        bool found = false;
    
        for (int i = 0; i < n.size(); i++) {
            if (n.at(static_cast<unsigned long long int>(i)) == 0 && !found) {
                n.erase(n.begin() + i);
                i--;
                continue;
            } else {
                found = true;
                std::cout << n.at(static_cast<unsigned long long int>(i));
            }
        }
    
        std::cout << std::endl;
    
        auto end = std::chrono::steady_clock::now();
        auto diff = end - start;
    
        std::cout << std::endl;
        std::cout << "Time of Execution:";
        std::cout << std::chrono::duration<double, std::milli>(diff).count() << "ms" << std::endl;
    
        return 0;
    }
    
    template<typename T>
    std::vector<T> *slice(std::vector<T> &v, int m, int n) {
        auto *vec = new std::vector<T>{};
        for (int i = m; i <= n; i++) {
            vec->push_back(v.at(i));
        }
        return vec;
    }
    
    void decTobin_vec(std::vector<unsigned short int> &N, std::vector<unsigned short int> &n) {
        std::cout << "Work in Progress..." << std::endl;
        std::cout << std::endl;
        for (unsigned short i : N) {
            std::vector<unsigned short int> *v = nullptr;
            v = decTobin(i);
            for (auto j = 0; j < v->size(); j++)
                n.push_back(v->at(static_cast<unsigned long long int>(j)));
        }
    
        auto *conv = new std::vector<unsigned short int>{};
    
        for (unsigned long long int e = N.size() - 1, i = 0, j = i + VECPARTITION - 1;
             j < n.size(); i += VECPARTITION, j += VECPARTITION, e--) {
    
            std::vector<unsigned short int> *tmp = slice(n, static_cast<int>(i), static_cast<int>(j));
    
            auto pow2 = new std::vector<unsigned short int>{};
            auto pow5 = new std::vector<unsigned short int>{};
    
            if (e <= 22) {
                pow2 = decTobin(static_cast<unsigned long long int>(pow(2, e)));
                pow5 = decTobin(static_cast<unsigned long long int>(pow(5, e)));
            } else if (e > 22) {
    
                for (int exp = 22, c = 0; c < pow(2, (e - exp)); c++) {
                    pow2 = sum_bin_vec(*decTobin(static_cast<unsigned long long int>(pow(2, 22))), *pow2);
                }
                for (int exp = 22, c = 0; c < pow(5, (e - exp)); c++) {
                    pow5 = sum_bin_vec(*decTobin(static_cast<unsigned long long int>(pow(5, 22))), *pow5);
                }
            }
    
            std::vector<unsigned short int> *convtmp = mult_bin_vec(*pow2, *pow5);
    
            convtmp = mult_bin_vec(*convtmp, *tmp);
            conv = sum_bin_vec(*conv, *convtmp);
        }
    
        n.clear();
    
        for (int i = 0; i < conv->size(); i++)
            n.push_back(conv->at(static_cast<unsigned long long int>(i)));
    }
    
    std::vector<unsigned short int> *decTobin(unsigned long long int cifra) {
    
        auto *v = new std::vector<unsigned short int>{};
    
        while (cifra >= 1) {
            v->insert(v->begin(), static_cast<unsigned short &&>(cifra % 2));
            cifra /= 2;
        }
        while (v->size() != VECPARTITION)
            v->insert(v->begin(), 0);
        return v;
    }
    
    std::vector<unsigned short int> *sum_bin_vec(std::vector<unsigned short int> &b1, std::vector<unsigned short int> &b2) {
    
        auto *result = new std::vector<unsigned short int>{};
        unsigned short int bit = 0;
        unsigned short int bit_sum = 0;
    
        while (b1.size() != b2.size())
            b1.size() > b2.size() ? b2.insert(b2.begin(), 0) : b1.insert(b1.begin(), 0);
    
        for (auto i = static_cast<int> (b1.size() - 1), j = static_cast<int>( b2.size()) - 1; i >= 0, j >= 0; i--, j--) {
            bit_sum = b1.at(static_cast<unsigned long long int>(i)) + b2.at(static_cast<unsigned long long int>(j)) + bit;
            if (bit_sum == 2) {
                bit = 1;
                bit_sum = 0;
            } else if (bit_sum == 3) {
                bit = 1;
                bit_sum = 1;
            } else {
                bit = 0;
            }
            result->insert(result->begin(), bit_sum);
        }
        if (bit == 1)
            result->insert(result->begin(), bit);
    
        return result;
    }
    
    std::vector<unsigned short int> *
    mult_bin_vec(std::vector<unsigned short int> &b1, std::vector<unsigned short int> &b2) {
    
        auto *result = new std::vector<unsigned short int>{};
        auto *sum = new std::vector<unsigned short int>{};
    
        if (b1.size() > b2.size())
            if ((abs(static_cast<int>((b1.size()) - (b2.size()))) % 2))
                b1.insert(b1.begin(), 0);
    
        if (b2.size() > b1.size())
            if ((abs(static_cast<int>((b1.size()) - (b2.size()))) % 2))
                b2.insert(b2.begin(), 0);
    
        while (b1.size() != b2.size()) {
            if (b1.size() > b2.size()) {
                b2.insert(b2.begin(), 0);
            } else if (b1.size() < b2.size()) {
                b1.insert(b1.begin(), 0);
            }
        }
        for (auto i = static_cast<int>(b2.size() - 1); i >= 0; i--) {
            auto j = static_cast<int>(b1.size() - 1);
            for (j; j >= 0; j--) {
                sum->push_back(
                        b2.at(static_cast<unsigned long long int>(i)) * b1.at(static_cast<unsigned long long int>(j)));
            }
        }
    
        int count = 0;
    
        for (int i = 0, j = static_cast<int>(i + b1.size() - 1);
             j < sum->size() - 1; i += b1.size() * 2, j += b1.size() * 2) {
    
            std::vector<unsigned short int> *v = slice(*sum, i, j);
            std::vector<unsigned short int> *w = slice(*sum, j + 1, static_cast<int>(j + b1.size()));
    
            std::reverse(v->begin(), v->end());
            std::reverse(w->begin(), w->end());
    
            for (int k = 0; k < count; k += 2)
                v->push_back(0);
    
            for (int k = 0; k < count + 1; k += 2)
                w->push_back(0);
    
            result = sum_bin_vec(*sum_bin_vec(*v, *w), *result);
    
            count += 4;
        }
        return result;
    }
    
    std::vector<unsigned short int> *stringTovec(const std::string &numstring) {
        auto *vec = new std::vector<unsigned short int>{};
    
        for (const char i : numstring)
            vec->push_back(static_cast<unsigned short &&>(std::strtol(&i, nullptr, 0)));
    
        return vec;
    }
    
  • Re: Conversione Decimale-Binario

    Allora la tua idea su come risolvere il problema:

    Luke220898 ha scritto:


    Dato il numero "numero positivo intero con 40+ cifre",calcolare quanti "1" ha la sua conversione in binario.
    e un po' troppo macchinosa con grossi numeri.
    usa una libreria apposita per il trattamento dei grandi numeri ed effettua la conversione standard ponendo il numero binario in una stringa
    e poi conti gli 1 binari
    la libreria che ti consiglio e la seguente: C++ BigInt class
    la puoi scaricare dal seguente sito:
    https://sourceforge.net/projects/cpp-bigint
    saluti e spero di esserti stato d'aiuto
  • Re: Conversione Decimale-Binario

    Ok,grazie,userò la libreria,comunque se qualcuno avesse dei suggerimenti per migliorare il codice sono ben accetti.
  • Re: Conversione Decimale-Binario

    Premesso che non ho letto in modo approfondito i precedenti post, la mia idea è molto semplice e si basa su una funzione che ritorna una stringa contente il quoziente della divisione intera tra 2 numeri, di cui il dividendo è costituito da una stringa e il divisore da un intero.
    Ecco il codice:
    #include <iostream>
    
    using namespace std;
    
    string divisione_intera(const string &dividendo, const unsigned int &divisore)
    {
        string quoziente;
        unsigned int a = 0;
        for(unsigned int i = 0; i < dividendo.size(); ++i)
        {
            a = a * 10 + (dividendo[i] - '0');
            if(quoziente.size() || a / divisore)
            {
                quoziente.push_back(a / divisore + '0');
            }
            a = a % divisore;
        }
        if(!quoziente.size())
        {
            quoziente.push_back('0');
        }
        return quoziente;
    }
    
    int main()
    {
        unsigned int cont = 0;
        string v;
        cout << "INSERIRE UN INTERO POSITIVO: ";
        cin >> v;
        while(true)
        {
            if((v[v.size() - 1] - '0') % 2)
            {
                ++cont;
            }
            if(v.size() == 1 && v[0] == '0')
            {
                break;
            }
            v = divisione_intera(v, 2);
        }
        cout << "LA CIFRA 1 SI RIPETE " << cont << " VOLTE!";
    }
    Non l'ho testato per bene, quindi se c'è qualcosa che non va fatemelo sapere.
  • Re: Conversione Decimale-Binario

    Nippolo,sembra funzionare bene il tuo codice,grazie,però potresti spiegarmi bene la tua idea e come ti è venuta in mente?
    Cioè non capisco come fai a contare gli 1 senza convertire il numero in binario,grazie.
  • Re: Conversione Decimale-Binario

    Nippolo,sembra funzionare bene il tuo codice,grazie,però potresti spiegarmi bene la tua idea e come ti è venuta in mente?
    Per convertire un numero in base 10 in una base diversa, bisogna effettuare divisioni successive (finché il quoziente non è uguale 0) e considerare i vari resti ottenuti.
    Il problema con numeri da 40+ cifre è che non possono essere contenuti in un int e quindi l'unica alternativa, come hai detto tu stesso, è quella di usare delle stringhe. A questo punto l'unica cosa che ci separa dalla soluzione del problema è effettuare delle divisioni su numeri contenuti in una stringa... a tal proposito ho implementato la funzione divisione_intera() che non fa altro che svolgere la divisione così come facciamo su carta.
    Cioè non capisco come fai a contare gli 1 senza convertire il numero in binario,grazie.
    Come già detto, nella conversione da decimale ad un'altra base, bisogna considerare i vari resti ottenuti nelle divisioni. Nel caso di conversione in binario il resto può essere 0 (dividendo, e quindi ultima cifra, pari) o 1 (dividendo, e quindi ultima cifra, dispari). Con la parte di codice:
    if((v[v.size() - 1] - '0') % 2)
            {
                ++cont;
            }
    faccio proprio questo, ossia se l'ultima cifra è dispari allora il numero in binario conterrà in quella posizione un 1.
  • Re: Conversione Decimale-Binario

    Ho voluto provare anch'io l'idea di Nippolo del replicare un calcolo tipo quelli che si fanno "a mano", su un foglietto, però l'ho fatto in C e senza il trucco del controllare solo l'ultima cifra (non avevo letto per intero le sue risposte). Mi è venuta fuori una roba un po' lunghetta e che farà storcere il naso a chi se ne intende, però sembra funzionare...
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define MAX_CIFRE   1000
    
    char *agnirts( char *s ) {
        if( NULL != s ) {
            long i, lMezzi, l = strlen(s);
    
            for( lMezzi=l/2, i=0; i<lMezzi; ++i ) {
                char aux = s[i];
                s[i] = s[l-i-1];
                s[l-i-1] = aux;
            }
        }
    
        return s;
    }
    
    void elimina_zeri_iniziali( char *s ) {
        if( NULL != s ) {
            char *p = s;
    
            while( '0' == *p ) ++p;
    
            if( s != p ) {
                while( *p ) {
                    *s = *p;
                    ++s; ++p;
                }
    
                *s = '\0';
            }
        }
    }
    
    void diviso_due( char *s, int *r ) {
        if( NULL != s ) {
            size_t i, l = strlen( s );
            int dividendo, resto = 0;
    
            for( i=0; i<l; ++i ) {
                dividendo = 10*resto + s[i]-'0';
                resto = dividendo%2;
                s[i] = (dividendo/=2) + '0';
            }
    
            elimina_zeri_iniziali( s );
            if( NULL != r ) *r = resto;
        }
    }
    
    char *binario( const char *s ) {
        if( NULL == s ) return NULL;
    
        size_t lnb=0, lns=strlen(s);
    
        char *ns = (char*) malloc( lns+1 );
        char *nb = (char*) calloc( lnb+1, 1 );
    
        if( NULL != ns && NULL != nb ) {
            int r; // r: resto
    
            strcpy( ns, s );
    
            while( 1 ) {
                diviso_due( ns, &r );
    
                char *aux = (char*) realloc( nb, lnb+2 );
    
                if( aux ) {
                    nb = aux;
                    nb[lnb] = r+'0';
                    ++lnb;
                    nb[lnb] = '\0';
                }
                else {
                    free( ns ); ns = NULL;
                    free( nb ); nb = NULL;
                    return NULL;
                }
    
                if( '\0' == *ns ) {
                    agnirts( nb );
                    break;
                }
            }
        }
        else {
            if( ns ) { free( ns ); ns = NULL; }
            if( nb ) { free( nb ); nb = NULL; }
            return NULL;
        }
    
        free( ns );
        return nb;
    }
    
    int main() {
        char nd[MAX_CIFRE+24];
        char *nb = NULL;
        size_t lIn;
    
        printf( "Immetti un intero positivo con non piu' di %d cifre: ", MAX_CIFRE );
        fgets( nd, MAX_CIFRE+24, stdin );
        lIn = strlen( nd );
    
        if( lIn <= MAX_CIFRE ) {
            nd[lIn-1] = '\0';
    
            nb = binario( nd );
    
            if( nb ) {
                printf( "\nDecimale: %s\nBinario:  %s\n", nd, nb );
                free( nb );
            }
            else {
                printf( "Errore! Conversione fallita.\n" );
            }
        }
        else {
            printf( "Errore! Il numero ha troppe cifre.\n" );
        }
    
        return 0;
    }
    
    Non calcola la quantità di 1, ma quello è banale.
Devi accedere o registrarti per scrivere nel forum
8 risposte