数论——小知识

1.数据类型取值范围

unsigned int 0~42 9496 7295 

int -2147483648~21 4748 3647 

unsigned long 0~4294967295

long -2147483648~2147483647

long long的最大值:922 3372 0368 5477 5807

long long的最小值:-9223372036854775808

unsigned long long的最大值:184 4674 4073 7095 5161

__int64的最大值:922 3372 0368 5477 5807

__int64的最小值:-9223372036854775808

unsigned __int64的最大值:1844 6744 0737 0955 1615

2.  无穷大常量取值是0x3f3f3f3f 或是1<<30(1<<30 表示1左移30位,每左移一位乘以2,所以就是1*2^30=1073741824

3.一维数组初始化为0可以这样写Int a[8]={0};

二维数组初始化为0可以这样写Int a[8][8]={{0}};  两个大括号即可

4.在判断两个浮点数大小

一定要在一边先减去0.000001,如a<b,一定写成a-0.000001<b才行!

5.如何输出float型而后面不加0?

printf("%g", num); \\ printf("%.f",num);

 %g 用来输出实数,它根据数值的大小,自动选f格式或e格式(选择输出时占宽度较小的一种),且不输出无意义的0。即%g是根据结果自动选择科学记数法还是一般的小数记数法 

7.scanf("\n%*c%d",&m);

Scanf 格式中,如果在%后面、格式字符前面加上一个“*”附加说明符,表示跳过该输入,输入的数据不放入变量中。

而输入的 '\n' 是中和掉输出的 \n

也可以换成

getchar();

scanf("%*c%d",&m);

而 *c 表示忽略一位 也可以改成

scanf("%*1d%d",&m);

8.分离整数和小数部分整数划分

要求:编写一个程序,其功能为:从键盘上输入一个浮点数(小数点后有位数),然后分别输出该数的整数部分和小数部分。

样例输入:123.456

样例输出:123 456

#include <stdio.h>  

int main()  

{  

    float x;  

    int a, b;  

    scanf("%f",&x);  

    a=x;  //自动类型转换,取整数部分  

    b=(int)(x*1000)%1000;  //乘1000后对1000取余,得到3位小数点后数字  

    printf("%d %d\n", a, b);  

    return 0;  

}  

9.素数判定函数

int Prime_judge(int x)

{

int i;

if(x==0||x==1)   return 0;

for(i=2;i<=sqrt(x);i++)

if(x%i==0)   return 0;//不是素数

return 1;//是素数

}

10.itoa   

将任意类型的数字转换为字符串。在<stdlib.h>中与之有相反功能的函数是atoi。

非标准C语言扩展函数

char*itoa(int value,char*string,int radix);

int value 被转换的整数,char *string 转换后储存的字符数组,int radix 转换进制数,如2,8,10,16 进制等

11.求最小公倍数和最大公约数

最小公倍数:数论中的一种概念,两个整数公有的倍数成为他们的公倍数,其中一个最小的公倍数是他们的最小公倍数,同样地,若干个整数公有的倍数中最小的正整数称为它们的最小公倍数

求最小公倍数算法:最小公倍数=两整数的乘积÷最大公约数

求最大公约数算法:

(1辗转相除法

#include<stdio.h>

void main()   /*  辗转相除法求最大公约数 */

{

   int m, n, a, b, t, c;

   printf("Input two integer numbers:\n");

   scanf("%d%d", &a, &b);

   m=a;   n=b;

   while(b!=0)  /* 余数不为0,继续相除,直到余数为0 */

   { c=a%b; a=b;  b=c;}

   printf("The greates common divisor:%d\n", a);//最大公约数 GCD

   printf("The least common multiple:%d\n", m*n/a);}//最小公倍数 LCM

 2)递归法:最大公约数最小公倍数

  1. int gcd(int a,int b)  
  2. {  
  3.     if(b==0)return a;  
  4.     gcd(b,a%b);  
  5. }  
12.自然常数e的x次方,表示方法为:exp(x)。
13.试求定积分的近似值(积分限a,b从键盘输入)。

算法分析如下:

    求定积分的近似值常有矩形法与梯形法,其实质都是面积求和。

    矩形法是把所要求的面积垂直x轴分成n个小矩形,然后把这n个小矩形的面积相加,即为所求的定积分的值。

    梯形法是把所要求的面积垂直分成n个小梯形,然后作面积求和。

    这两种近似求值的精度随分割个数n的增加而增加,对于相同的n个数,相对来说,梯形法的精度比矩形法的要高一些。

程序代码如下:

例一:

void main()

{

    int i,n=1000;

    float a,b,h,t1,t2,s1,s2,x;

    printf("请输入积分限a,b:");

    scanf("%f,%f",&a,&b);

    h=(b-a)/n;

    for(s1=0,s2=0,i=1;i<=n;i++)

    {

        x=a+(i-1)*h;

        t1=(float)exp(-x*x/2);t2(float)=exp(-(x+h)*(x+h)/2);

        s1=s1+t1*h;        /*矩形面积累加*/

        s2=s2+(t1+t2)*h/2;        /*梯形面积累加*/

    }

    printf("矩形法算得积分值:%f.\n",s1);

    printf("梯形法算得积分值:%f.\n",s2);

}

程序运行结果如下:

    矩形法算得积分值:0.855821

    梯形法算得积分值:0.855624

    由上面的比较可知,梯形法的精度要高于矩形法。

例二 求函数f(x)=x*x+2*x+1在【0,2】上的定积分。

    #include

    main()

       {    double s=0,h,a,b,f0,f1,n;

             int i;

             printf("Enter n,a,b:");

             scanf("%lf,%lf,%lf",&n,&a,&b);

             h=(b-a)/n;

             f0=a*a+2*a+1;

             for(i=1;i<=n;i++)

                 {  a=a+h;

                     f1=a*a+2*a+1;

                     s+=(f0+f1)*h/2;

                     f0=f1;

                 }

            printf("sum is %f",s);

        }

 

14.二分查找又称折半查找

void g(int k,int m)//在长度为m的数组num找到元素k.

{

int high=m-1,flag=0;//flag做标记

int low=0,mid;

while(low<=high){

mid=(low+high)/2;

if(num[mid]<k)

low=mid+1;

else if(num[mid]>k)

high=mid-1;

else{

flag=1;

printf("YES\n");

break;

}

}

if(flag==0)

printf("NO\n");

}

 

15.输出a和b的比值。如果需要,输出一个既约分数。

分子、分母只有公因数1的分数,或者说分子和分母互质的分数,叫做最简分数,又称既约分数。如:二分之一,三分之二,九分之八,八分之三等等。

int gcd(int a,int b)//求最大公约数

{

while(b!=0)

{

int r=a%b;

a=b;

b=r;

}

return a;

}

if(aa%bb==0)

printf("%d\n",aa/bb);

else

printf("%d/%d\n",aa/gcd(aa,bb),bb/gcd(aa,bb));

16.double型强转int型

应该使用向下取整;+0.5实现四舍五入。

例如:double a=;int b=(int)(a+0.5);

 

17.任意进制转换

语法:conversion(char s1[],char s2[],long d1,long d2);

参数:

s[]: 原进制数字,用字符串表示

s2[]: 转换结果,用字符串表示

d1: 原进制数

d2: 需要转换到的进制数

返回值: null

注意:  

  高于9的位数用大写'A'~'Z'表示,2~16位进制通过验证

源程序:  

  void conversion(char s[],char s2[],long d1,long d2)

{

    long i,j,t,num;

    char c;

    num=0;

    for (i=0;s[i]!='\0';i++)

        {

        if (s[i]<='9'&&s[i]>='0') t=s[i]-'0'; else t=s[i]-'A'+10;

        num=num*d1+t;

        }

    i=0;

    while(1)

        {

        t=num%d2;

        if (t<=9) s2[i]=t+'0'; else s2[i]=t+'A'-10;

        num/=d2;

        if (num==0) break;

        i++;

        }

    for (j=0;j<i/2;j++)

        {c=s2[j];s2[j]=s[i-j];s2[i-j]=c;}

    s2[i+1]='\0';

}

 

18.求排列组合数

语法:result=P(long n,long m); / result=long C(long n,long m);

参数:

m: 排列组合的上系数

n: 排列组合的下系数

返回值: 排列组合数

注意:  

  符合数学规则:m<=n

源程序:  

  long P(long n,long m)

{

    long p=1;

    while(m!=0)

        {p*=n;n--;m--;}

    return p;

}

long C(long n,long m)

{

    long i,c=1;

    i=n;

    while(i!=0)

        {c*=n;n--;i--;}

    while(m!=0)

        {c/=m;m--;}

    return c;

}

 

19.x的二进制长度

语法:result=BitLength(int x);

参数:

x: 测长的x

返回值: x的二进制长度

源程序:  

  int BitLength(int x)

{

    int d = 0;

    while (x > 0) {

        x >>= 1;

        d++;

    }

    return d;

}

 

20.返回x的二进制表示中从低到高的第i位

语法:result=BitAt(int x, int i);

参数:

x: 十进制 x

i: 要求二进制的第i位

返回值: 返回x的二进制表示中从低到高的第i位

注意:  

  最低位为第一位

源程序:  

  int BitAt(int x, int i)

{

    return ( x & (1 << (i-1)) );

}

21 /*********************格式化输入输出*********************************/

%d 十进制有符号整数

%ld/%lld  long/long long

%u 十进制无符号整数

%f 浮点数

%s 字符串

%c 单个字符

%p 指针的值

%e 指数形式的浮点数

%x, %X 无符号以十六进制表示的整数

%o 无符号以八进制表示的整数

%g 自动选择合适的表示法

━━━━━━━━━━━━━━━━━━━━━━━━━━

  说明:

(1). 可以在"%"和字母之间插进数字表示最大场宽。

  例如: %3d 表示输出3位整型数, 不够3位右对齐。

%9.2f 表示输出场宽为9的浮点数, 其中小数位为2, 整数位为6,

  小数点占一位, 不够9位右对齐。

%8s 表示输出8个字符的字符串, 不够8个字符右对齐。

  如果字符串的长度、或整型数位数超过说明的场宽, 将按其实际长度输出。

  但对浮点数, 若整数部分位数超过了说明的整数位宽度, 将按实际整数位输出;

  若小数部分位数超过了说明的小数位宽度, 则按说明的宽度以四舍五入输出。

  另外, 若想在输出值前加一些0, 就应在场宽项前加个0

  例如: %04d 表示在输出一个小于4位的数值时, 将在前面补0使其总宽度

  为4位。

  如果用浮点数表示字符或整型量的输出格式, 小数点后的数字代表最大宽度,

  小数点前的数字代表最小宽度。

  例如: %6.9s 表示显示一个长度不小于6且不大于9的字符串。若大于9,

  第9个字符以后的内容将被删除。

(2). 可以在"%"和字母之间加小写字母l, 表示输出的是长型数。

  例如: %ld 表示输出long整数

%lf 表示输出double浮点数

(3). 可以控制输出左对齐或右对齐, 即在"%"和字母之间加入一个"-" 号可

  说明输出为左对齐, 否则为右对齐。

  例如: %-7d 表示输出7位整数左对齐

%-10s 表示输出10个字符左对齐

22. /*************************立方之和*****************************/

1^3+2^3+.....+n^3=n^2(n+1)^2/4=[n(n+1)/2]^2

23./****************************平方之和***************************************/

1^2+2^2+...n^2=1/6*n*(n+1)*(2*n+1);

24.快速幂求模取余

积的取余等于取余的积取余

代码如下:

int pow(int a,int n,int b)//返回值是an次方对b取余后的值

{

    int result=1;

    a=a%b;//积的取余等于取余的积取余

    while(n>0)

    {

        if(n%2==1)

            result=result*a%b;//n是奇数的话就要多乘一次,原理和前面的二分求幂一样

        n=n/2;//二分

        a=a*a%b;//积的取余等于取余的积取余

    }

    return result;

}

******************************************************************************

25.筛选法求素数

   筛选法求素数小题

用筛法求素数的基本思想是:把从1开始的、某一范围内的正整数从小到大顺序排列, 1不是素数,首先把它筛掉。剩下的数中选择最小的数是素数,然后去掉它的倍数。依次类推,直到筛子为空时结束。

代码如下:

#include<stdio.h>

#include<string.h>

#include<math.h>

#define N 10000//N的大小可以根据需要变化

int a[N];//利用数组的下标记录要判断的数字

void is_sushu()

{

    memset(a,0,sizeof(a));//对数组a进行初始化为0,不是素数的标记为1,剩下为0的就是素数了

    a[1]=1;//1既不是素数也不是合数,先标记为0

    for(int i=2; i<=sqrt(N); i++)

    {

        if(a[i]==0)//如果i是素数

        {

            for(int j=2; j*i<=N; j++) //循环标记的范围是i*j<N

            {

                a[i*j]=1;//如果i是素数,那么i*j肯定不是素数

            }

        }

    }//最终所有非素数都标记为1,素数都标记为0

}

  1. void init()  
  2. {  
  3.     a[1]=1;  
  4.     for(int i=2;i*i<maxn;i++)  
  5.     {  
  6.         if(!a[i])  
  7.         for(int j=i+i;j<maxn;j+=i)  
  8.         a[j]=1;  
  9.     }  
  10. }  

*******************************************************************************

26.巴什博弈:

只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后取光者得胜。

显然,如果n=m+1,那么由于一次最多只能取m个,所以,无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,

后者取胜。因此我们发现了如何取胜的法则:如果n=m+1r+s,(r为任意自然数,sm),那么先取者要拿走s个物品,

如果后取者拿走k(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者

肯定获胜。总之,要保持给对手留下(m+1)的倍数,就能最后获胜。

代码如下:

#include<stdio.h>

int main()

{

int t,sum,n;

scanf("%d",&t);

while(t--)

{

scanf("%d%d",&sum,&n);

if (sum % (n + 1))

    printf("Win\n");

    else

    printf("Lose\n");

}

return 0;

}

******************************************************************************

27.矩形的个数

在一个3*2的矩形中,可以找到61*1的矩形,42*1的矩形31*2的矩形,22*2的矩形,23*1的矩形和13*2的矩形,总共18个矩形。

while(scanf("%f%f",&a,&b)!=EOF)

{

printf("%.0f\n",(a+1)*a*(b+1)*b/4);

28.蔡勒公式(根据日期计算出周几)

W=[C/4]-2C+y+[y/4]+[26(m+1)/10]+d-1 (其中[ ]为取整符号)

W是所求日期的星期数.

如果求得的数大于7,可以直接对7取余,不过周日就输出为0了。

如果求得的数小于0,可以加上7的倍数,直到结果大于零小于7为止

c是公元年份的前两位数字,

y是已知公元年份的后两位数字;

m是月数,

d是日数.

方括[ ]表示只截取该数的整数部分。

所求的月份如果是1月或2,则应视为前一年的13月或14.

所以公式中m 的取值范围不是1-12,而是3-14.

模板代码:

int Change(int year,int month,int day)//根据日期判断出星期几

{

    if(month==1||month==2)

    {

        month+=12;

        year--;

    }

    int c=year/100;

    int y=year%100;

    int m=month;

    int d=day;

    int W=c/4-2*c+y+y/4+26*(m+1)/10+d-1;

    if(W<0)

        return (W+(-W/7+1)*7)%7;

    return W%7;

}

29.数字与数字数组与字符型数字数组的转换问题

数字temp(长度为n)   数字数组num[n-1]  字符型数字数组str[n-1]

 

1.将数字转换成数字数组

memset(num,0,sizeof(num));  

for(i=n-1;i>=0;i--){  

    num[i]=temp%10;  

   temp=temp/10;  

 

2.将数字数组转换成数字

temp=0;  

for(i=0;i<strlen(num);i++)  

       temp=temp*10+num[i];  

 

3.将数字转换成字符型数字数组

  memset(str,0,sizeof(str));  

for(i=n-1;i>=0;i--){  

    str[i]=temp%10+'0';  

    temp=temp/10;  

}  

 

4.将字符型数字数组转换成数字

temp=0;  

for(i=0;i<strlen(str);i++)   

temp=temp*10+(str[i]-'0');  

 

5.将数字数组转换成字符型数字数组

memset(str,0,sizeof(str));  

for(i=0;i<n;i++)  

str[i]=num[i]+'0';  

 

6.将字符型数字数组转换成数字数组

memset(num,0,sizeof(num));  

for(i=0;i<strlen(str);i++)  

num[i]=str[i]-'0';  

 

7.STL

sscanf(str+start,"%d%n",&v,&n);start+=n;return v;  

//此处的意思是从str+start开始讲字符转化为数字赋值给v,一共转化了n个字符(有几个数字字符就转化成///多少数字,)  

 30.    

程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算说穿了,就是直接对整数在内存中的二进制位进行操作。运位算包括位逻辑运算和移位运算,位逻辑运算能够方便地设置或屏蔽内存中某个字节的一位或几位,也可以对两个数按位相加等;移位运算可以对内存中某个二进制数左移或右移几位等。

计算机内部是以补码形式存放数值的。C语言提供了六种位运算

假设ab为整型的数据,并且设a=15(等于二进制数00000000 00001111)b=80(等于二进制数 00000000 01010000)

a的补码:00000000 00001111

 b的补码:00000000 01010000

            ————————

a&b:  00000000 00000000   a&b=0x0

a|b 00000000 01011111   a|b=0x5f

a^b 00000000 01011111   a^b=0x5f

~a  11111111 11110000    ~a=0xfff0

位运算应用口诀

清零取反要用与,某位置一可用或

若要取反和交换,轻轻松松用异或

1.“按位与”运算符&

运算规则:参加运算的两个运算量,如果两个数相应位的值都是1,则该位的结果值为1,否则为0。即:0 & 0 =00 & 1 =01 & 0 =01 & 1 =1

(1)将某些二进制位屏蔽掉(保留一个数据中的某些位)。

如果要使整数k的低四位置零,保留其它位。用位与运算即可,将的高字节与0相与,低字节与1相与;

代码如下:unsigned int_set(unsigned int k)

{k=k&0x1110;

Return(k);}

例】00101010 01010010&11111111 11110000=00101010 01010010

结论:任何二进制位与0能实现置0;与1保持原值不变

2)判断一个数据的某一位是否为1

如判断一个整数a(2个字节)的最高位是否为1,可以设一个与a同类型的测试变量testtest的最高位为1,其余位均为0,即int test=0x8000

【例】   0100010011111110&1000000000000000=0 说明最高位为0

     1100010011111110&1000000000000000=1000000000000000 说明最高位为1

例如一个数 and 1的结果就是取二进制的最末位。这可以用来判断一个整数的奇偶,二进制的最末位为0表示该数为偶数,最末位为1表示该数为奇数.

2.“按位或”运算符|

常用来将源操作数某些位置1,其它位不变。 (mask中特定位置1,其它位为0 s=s|mask)

运算规则:参加运算的两个运算量,如果两个数相应位的值都是0,。即:0 | 0 =00 | 1 =11 | 0 =11 | 1 =1

把一个数据的某些位置为1

如果把a的第10位置为1,而且不要破坏其它位,可以对ab进行“按位或”运算,其中b的第10位置为1,其它位置为0,即int b=0x400

【例】00100000 01010010|00000010 00000000=00100010 01010010

3.“按位异或”运算符^

 运算规则:参加运算的两个运算量,如果两个数的相应位的值不同,则该位的结果值为1,否则为0。即:0 ^ 0 =00 ^ 1 =11 ^ 0 =11 ^ 1 =0

应用举例:

1)把一个数据的某些位翻转,即1变为00变为1

如要把a的奇数位翻转,可以对ab进行“按位异或”运算,其中b的奇数位置为1,偶数位置为0,即int b=0xaaaa

【例】a的补码:00000000 01010010

b的补码: 01010101 01010101

      ^   -------------------

结果的补码:  01010101 00000111

2)交换两个值,不用临时变量。

【例】a=3b=4。想将ab的值互换,可以用以下三条赋值语句实现:

a=a^b;即:a=3^4=7(0011^0100=0111)

b=b^a;即:b=4^7=3(0100^0111=0011)

a=a^b;即:a=7^3=4(0111^0011=0100)

不用temp交换两个整数

void swap(int x , int y)

{

x ^= y;

y ^= x;

x ^= y;

}

编写对字符串进行密钥匙异或加解密程序

有了加密程序(a^b),相应的就应该有解密程序。解密程序是加密程序的逆过程,这里的加密和解密程序是完全相同的,原因是(a^b^b=a

4.“按位取反”运算符~

 移位运算符:

左移、右移运算实现将一个数的各个二进制位向左向右移若干位。

1.左移运算符<<

运算规则:对运算符<<左边的运算量的每一位全部左移右边运算量表示的位数,右边空出的位补0

【例】a<<2表示将a的各位依次向左移2位,a的最高2位移出去舍弃,空出的低2位以0填补。

例:char a=0x21

a<<2的过程 0010 0001〈〈2 = 1000 0100;即 a<<2的值为0x84

左移1位相当于该数乘以2,左移n位相当于该数乘以2n

乘法运算转化成位运算 (在不产生溢出的情况下)

a * (2^n) 等价于 a<< n        

2. 右移运算符>>

运算规则:对运算符>>左边的运算量的每一位全部右移右边运算量表示的位数,右边低位被移出去舍弃掉,空出的高位补0还是补1,分两种情况:

(1)对无符号数进行右移时,空出的高位补0。这种右移称为逻辑右移。

(2)对带符号数进行右移时,空出的高位全部以符号位填补。即正数补0,负数补1。这种右移称为算术右移。

   右移1位相当于除以2,同样,右移n位相当于除以2n

除法运算转化成位运算 (在不产生溢出的情况下)

a / (2^n) 等价于 a>> n

取模运算转化成位运算 (在不产生溢出的情况下)

a % (2^n) 等价于 a & (2^n - 1)

循环移位的实现。

如将一个无符号整数x的各位进行循环左移4位的运算,即把移出的高位填补在空出的低位处。

可以用以下步骤实现:

1)将x左移4位,空出的低4位补0,可通过表达式x<<4实现。

2)将x的左端高4位右移到右端低4位,可通过表达式x>>(16-4)实现。由于x为无符号整数,故空出的左端补0

3)将上述两个表达式的值进行按位或运算,即:

      y=(x<<4) | (x>>(16-4))

   x       0010 1111 0010 0001

x<<4       1111 0010 0001 0000

x>>(16-4)  0000 0000 0000 0010

y          1111 0010 0001 0010

 unsigned rol ( unsigned aint n)

           {   unsigned b

               b=(a<<n) | (a>>(16-n))

               return(b)}

计算绝对值

int abs( int x )

{ int y ;

 y = x >> 31 ;//二进制最高位

 return (x^y)-y ; //or: (x+y)^y

}


猜你喜欢

转载自blog.csdn.net/qq_36914923/article/details/79784464