c·c++/c 프로그래밍

큰 정수의 제곱근의 정수값 구하기(정수연산 만으로)

바로이순간 2014. 3. 24. 00:36

#include <stdio.h>

int main() {

    long long n;

    long long x, xx, y;

    int i=0;


    printf("큰수입력: ");

    scanf("%lld", &n);

    x=3;  // 출발수로 3을 준다.

    while(1) {

        xx=x;      // 바로 전수를 xx로 준다.

        x=x+x;    // x의 값을 2배로 준다.

        if(x*x<0) break;  // 만약 x의 제곱이 음수라면 while문을 탈출한다.

        if(x*x>n) break;  // 만약 x의 제곱이 n보다 크다면 while문을 탈출한다.

        i+=1;                     // 몇번 실행되었는지 확인해본다.

    }

    // xx의 제곱은 n보다 작거나 같아야 한다.

    // 위에서 n의 입력은 최소한 1이상이라고 가정하였다.

    // 따라서 xx의 제곱이 n보다 크다면 xx의 값을 1로  주고서 밑에서

    // n의 제곱근에 가까운 정수값 xx를 구한다.

    if(xx*xx>n) xx=1;

    while(1) {

        // x는 항상 xx보다 크다. 만약 아주 가까와 진다면 해를 구한것이다.

        // x와 xx의 중간값을 y라고 한다.

        y=(x+xx)/2;

        // 만약 y의 제곱이 n보다 크다면 x에 y값을 대입한다.

        // 만약 y의 제곱이 n보다 크지 않다면 xx에 y값을 대입한다.

        // 만약 y의 제곱이 음수가 되면 y값이 너무 크다는 뜻이다.

        // 이 경우에도 x에 y값을 대입한다.

        if(y*y<0 || y*y>n) {

            x=y;

        } else {

            xx=y;

        }

        // 두 값이 아주 가까우면 while문을 탈출한다.

        // x의 값이 xx의 값보다 1차이 나는 경우에는 탈출한다.

        // x을 제곱하면 n보다 크다. xx를 제곱하면 n보다 작든지 같다.

        if(xx+1>=x) break;

        i+=1;

    }

    //printf("n=%lld xx=%lld x=%lld i=%d\n", n, xx, x, i);

    printf("정수해=%lld\n", xx);


    return 0;

}


===========================================================================


#include <stdio.h>

#include <string.h>

#define MAX 4000


// 0번째 칸에는 자리수를 저장합니다. 만진법을 사용합니다.

// 1번째 칸에는 일의 자리, 2번째 칸에는 만의 자리, 3번째 칸에는 억의 자리

// ... 등을 저장합니다. 최대 3996자리까지 출력이 가능합니다.


void printResult(int []);

void copy(int a[], int b[]) {

    int i, n=a[0];

    for(i=0;i<MAX;i+=1) { b[i]=a[i]; }

}

void clear(int a[]) {

    int i;

    for(i=0;i<MAX;i+=1) { a[i]=0; }

    a[0]=1;

}

int compare(int a[], int b[]) {

    int i=a[0];

    if(a[0]<b[0]) { return -1; }

    if(a[0]>b[0]) { return 1; }

    while(i>0) {

        if(a[i]<b[i]) { return -1;  }

        if(a[i]>b[i]) { return 1; }

        i-=1;

    }

    return 0;

}

void add(int a[], int b[], int r[]) {

    int i, j, x=0;

    clear(r);

    r[0]=a[0];

    if(b[0]>r[0]) { r[0]=b[0]; }

    for(i=1;i<=r[0];i+=1) {       // 수의 각 자리와 밑에서 올라온 수를

        x=a[i]+b[i]+x;             // 더해줍니다.

        r[i]=x%10000;             // 값이 9999보다 크면 제자리의 수만 남고

        x=x/10000;                // 윗자리로 올라갑니다.

    }

    if(x!=0) { 

        r[0]+=1;

        r[r[0]]=x;

    } 

}

void sub(int a[], int b[], int r[]) {

    int i, j, x=0;


    clear(r);

    r[0]=a[0];

    if(b[0]>r[0]) { r[0]=b[0]; }


    for(i=1;i<=r[0];i+=1) { // 수의 각 자리로

        x=a[i]-b[i]+x;      // 결과에 더해줍니다.

        if(x<0) {

            r[i]=x+10000;

            x=-1;

        } else {

            r[i]=x;

            x=0;

        }

    }


    if(x!=0) { 

        for(i=0;i<a[0]+1;i+=1) { printf("%d ", a[i]); } printf("\n");

        for(i=0;i<b[0]+1;i+=1) { printf("%d ", b[i]); } printf("\n");


        printf("*****************ERROR*************\n");

        getchar(); getchar();

    } 

    i=r[0];

    while(r[i]==0) { i-=1; }

    if(i==0) { i=1; }

    r[0]=i;

    //for(i=0;i<10;i+=1) { printf("%d ", r[i]); } printf("\n");

}

void mul(int a[], int b[], int r[]) {

    int i, j, x;

    clear(r);

    for(i=1;i<=b[0];i+=1) {         // 승수의 각 자리로

        for(j=1;j<=a[0];j+=1) {     // 피승수의 각자리를 곱합니다.

            r[i+j-1]+=b[i]*a[j];    // 결과에 더해줍니다.

            x=r[i+j-1];             // 값이 9999보다 크면 윗자리로 올라갑니다.

            r[i+j-1]=x%10000;       // 제자리의 수만 남고

            r[i+j] +=x/10000;       // 윗자리로 올라갑니다.

        }

    }

    r[0]=a[0]+b[0];   // 결과의 자리수를 구합니다.

    if(r[r[0]]==0) { r[0]-=1; }

}

void div(int a[], int b[], int q[], int r[]) {

    int v1[MAX]={1,};

    int v2[MAX]={1,};

    int one[MAX]={1,1,};

    int two[MAX]={1,};

    int i, j, x, e1, e2, ex;

    double d1, d2, dd;


    copy(a, r); // 피제수를 나머지에 복사한다. 

                // 당분간 나머지가 임시로 남아있는 피제수 노릇을 한다.


    d2=0;       // 제수의 근사값을 d2와 e2로 나타낸다.

    i=b[0];

    j=0;

    while(i>0 && j<4) {

        d2=10000*d2+b[i];

        i-=1; 

        j+=1;

    }

    e2=i;

 

    // 남아있는 피제수가 제수보다 클 동안 계속한다.

    while(compare(r, b)>=0) { 

        // 피제수의 근사값을 d1과 e1으로 나타낸다. 

        d1=0;                 

        i=r[0];

        j=0;

        while(i>0 && j<4) {

            d1=10000*d1+r[i];

            i-=1; 

            j+=1;

        }    

        e1=i;


        // 임시 몫의 근사값을 dd와 ex로 나타낸다

        dd=d1/d2;            


        while(dd>10000) { dd/=10000; e1+=1; }


        ex=e1-e2+1; 


        clear(v1);

        // 임시 몫은 v1이다. dd와 ex로 부터 v1을 구한다.

        v1[0]=ex;            

        i=0;

        while(ex>0&&i<3) {

            x=dd; dd-=x; dd*=10000; v1[ex]=x; ex-=1; i+=1;

        }


        // v1이 클 경우 빼주는 값을 one이라한다.

        clear(one);          

        one[ex+1]=1;

        one[0]=ex+1;


        // v1의 아래 자리는 모두 0으로 채운다. 

        if(i==0) {           

            while(ex>0) { 

                v1[ex]=0; ex-=1; 

            } 

        }


        // 임시몫 v1이 너무 크다면 줄여준다.

        while(1) {

            // 임시 몫 v1과 제수를 곱한 결과를 v2라 한다.

            mul(v1, b, v2);

            // 임시 피제수가 v2보다 크다면 정상적으로 진행한다.

            if(compare(r,v2)>=0) { break; }

            // 만약 임시 피제수보다 v2가 크다면 위에서 구한 임시 몫 v1이 과도하게 크다.

            // v1에서 one을 빼준다.

            sub(v1, one, two);

            copy(two, v1);

        } 

        // 임시 피제수에서 v2를 뺀 결과를 임시 피제수로 한다.

        sub(r, v2, two);

        copy(two, r);

        // q에 v1을 더한 결과를 q로 한다.

        add(q, v1, two);

        copy(two, q);

    }   

    // q의 가장 큰 자리가 0일 경우는 1 자리를 줄여준다.

    while(q[q[0]]==0) { q[0]-=1; }

    if(q[0]<1) { q[0]=1; }

    // r에는 나머지가 남아 있게 된다.

}

// 출력은 다시 큰 수 부터 출력해 준다.

void printResult(int result[]) {

    int i, x;

    x=result[0];

    printf("%d", result[x]);

    for(i=1;i<x;i+=1) {

        printf("%04d", result[x-i]);

    }

    printf("\n");

}

void convert(char a[], int b[]) {

    int i, j, k, x;

    

    i=strlen(a)-1;

    x=0;

    j=1;

    k=1;

    while(i>=0) {

        x+=k*(a[i]-'0');

        k*=10;

        i-=1;

        if(k==10000) {

            k=1;

            b[j]=x;

            j+=1;

            x=0;

        }

    }

    if(k>1) {

        b[j]=x;

        j+=1;

    }

    b[0]=j-1;

}

int main() {

    int n[MAX]={1,};  // 피제수

    int x[MAX]={1,};  // 제수

    int xx[MAX]={1,};  // 몫

    int y[MAX]={1,};  // 나머지

    int t[MAX]={1,};

    int q[MAX]={1,};

    int r[MAX]={1,};

    int one[MAX]={1,1,};

    int two[MAX]={1,2,};


    char m1[MAX];

    char m2[MAX];

    int i, i2, z;


    //printf("입력1: ");

    //scanf("%s", m1);

    // 입력된 수를 뒤집어 준다.

    //convert(m1, n);

    n[0]=800;

    n[800]=2;


    i=0;

    x[1]=3;

    while(1) {

        copy(x, xx);

        add(xx, xx, x);

        mul(x, x, t);

        if(compare(t, n)>0) break;

        i+=1;   

    }


    while(1) {

        add(x, xx, t);

        clear(y);

        clear(r);

        div(t, two, y, r);

        mul(y, y, r);


        if(compare(r, n)>0) {

            copy(y, x);

        } else {

            copy(y, xx);

        }


        add(xx, one, r);


        if(compare(r, x)>=0) break;

        i+=1;

    }

    printResult(xx);


    return 0;

}


다음과 같이 하면 충분한 정밀도를 가지는 루트2나 루트3을 구할 수 있다.

g:\cprog>a
14142135623730950488016887242096980785696718753769480731766797379907324784621070
38850387534327641572735013846230912297024924836055850737212644121497099935831413
22266592750559275579995050115278206057147010955997160597027453459686201472851741
86408891986095523292304843087143214508397626036279952514079896872533965463318088
29640620615258352395054745750287759961729835575220337531857011354374603408498847
16038689997069900481503054402779031645424782306849293691862158057846311159666871
30130156185689872372352885092648612494977154218334204285686060146824720771435854
87415565706967765372022648544701585880162075847492265722600208558446652145839889
39443709265918003113882464681570826301005948587040031864803421948972782906410450
72636881313739855256117322040245091227700226941127573627280495738108967504018369
86836845072579936472906076299694138047565482372899718032680247442062926912485905
21810044598421505911202494413417285314781058036033710773091828693147101711116839
16581726889419758716582152128229518488472089694633862891562882765952635140542267
65323969461751129160240871551013515045538128756005263146801712740265396947024030
05174953188629256313851881634780015693691768818523786840522878376293892143006558
69568685964595155501644724509836896036887323114389415576651040883914292338113206
05243362948531704991577175622854974143899918802176243096520656421182731672625753
95947172559346372386322614827426222086711558395999265211762526989175409881593486
40083457085181472231814204070426509056532333398436457865796796519267292399875366
6172159825788602633636178274959942194037777536814262177387991945513972312740668

g:\cprog>a
17320508075688772935274463415058723669428052538103806280558069794519330169088000
37081146186757248575675626141415406703029969945094998952478811655512094373648528
09323190230558206797482010108467492326501531234326690332288665067225466892183797
12270471316603678615880190499865373798593894676503475065760507566183481296061009
47602187190325083145829523959832997789824508288714463832917347224163984587855397
66795806381835366611084317378089437831610208830552490167002352071114428869599095
63657970871684980728994932964842830207864086039887386975375823173178313959929830
07838702877053913369563312103707264019249106768231199288375641141422016742752102
37299427083105989845947598766428889779614783795839022885485290357603385280806438
19723446610596897228728652641538226646984200211954841552784411812865345070351916
50016689294415480846071277143999762926834629577438361895110127148638746976545982
45178855097537901388066496191196222295711055524292372319219773826256163146884203
28537166829386496119170497388363954959381457576718533736331259108996554246248347
87197605235997769192323570220305302840385915414971072429559206706202509520175963
18587276635997528366343108015066585371064732853862592226058222051040368027029750
47987280794616581004170526819400190957334621759438936702493204226910343698124637
20111185261084268910299720311202100063507176374582405203847555197279933797614906
10789498554422332600401885130363156114488684728158928816324518726506664538487759
91625766428721112408420680167635171001029431807155151909616424609070394081292169
0351749296136400413967043104125363232703092257732796029237659774553709546911574