合成数的高效算法

数的合成指将多个数字合成一个整数,比如将9、5、2、7合成9527。本文主要讨论的是整数的合成,附带提一下字符型数的合成。

一、整数的合成

整数合成指的是输入的数字和输出的整数都是以数的形式(即数据类型全部为int型)存储,而不用字符串。

有两种算法:

1.位值法(看似简单实则复杂低效的算法)

这种算法其实我们都很熟悉,造型是这样的:9*1000+5*100+2*10+7=9527。

只是它的名字可能我们比较陌生。

“位值法”(Positional Value Algorithm)中的位就是位置,值就是数值,即数字在其所在位置上的值,比如9在千位这个位置的位值是9000。这暗示此记数法的最大特点在于把数的“位置“作为衡量数大小的参数,即每个数字在不同的位置代表的权重是不一样的。也因此,它又被称为“位置记数法”(Positional Notation System)。

在这种记数法中,一个数的大小用一组有顺序的数字来表示,数的大小既取决于数字的值,也取决于数字的位置。

用公式来表示:数=∑数字*位置权重。上面算式中的1000、100、10、1就是权重(1没有显式写出),或者叫位权;9、5、2、7被称为因子或乘数。

这种我们今天想当然的记数法并不是所有记数法都采用的,比如罗马记数法就不属于位值法,因为它不遵循“位置决定值”的原则。

为什么说这种算法比较低效呢?

如果已知这个数的位数,此算法并不低效,直接套公式就行了。比如将3、2、7结合成整数327就可以用很简单的代码:

#include <stdio.h>
int main(){
    int num, units, tens, hundreds;
    scanf("%d%d%d", &hundreds, &tens, &units);
    num = hundreds*100 + tens*10 + units*1;
    printf("%d\n", num);
    return 0;
}

或者更复杂一点,输入位数n及每个数字,输出合成的数,代码如下:

#include<stdio.h>
#include<math.h>
int main(){
    int n, digit, num=0;
    scanf("%d", &n);
    int mod=pow(10, n-1); //计算权重
    for(int i=n; i>0; i--){
        scanf("%d", &digit);
        num += digit * mod;
        mod /= 10;
    }
    printf("%d\n", num);
    return 0;
}

关键是如果不知道位数n,就要先求出这个数是几位数,因此降低了效率。比如连续输入多位用空格隔开的数字,当结束输入时,输出这些数字合成的数。这时候就要增加一个计数器来统计总共输入了多少个数字。因为数字个数输入完毕才能求出,因此还要用数组存储输入的每个数字。代码如下:

#include<stdio.h>
#include<math.h>
int main(){
    int n=0, digit[100], num=0, mod=1;
    //输入多位数字
    while(scanf("%d", &digit[n])==1){
        n++;
    }
    //从最后一位数字向前遍历求num
    for(int i=n-1; i>=0; i--){
        num += digit[i] * mod;
        mod *= 10;
    }
    printf("%d\n", num);
    return 0;
}

注意此程序运行时只按回车键并不能结束输入,需要按CTRL+Z。

2.连乘加法(看似复杂实则简单高效的算法)

这种算法的造型看起来就比较复杂了:((9*10+5)*10+2)*10+7 =9527。

这个算式本质上是与位值法相同的,如果把括号去掉,就和位值法一样。

但是,它的算法却完全不同。

换一下造型,就变一种思想。

“连乘加法”是老金自己取的名,之所以叫这个名,是因为这种算法通过连续地乘以10(即向左移动数字)和加上下一位的数字来构建整个数。它本质上是从最高位开始计算的,每增加一位数,就用原数乘以10加上新增加的数,就能得到合成后的数。这种算法的原理在于任何一个整数都能分成两部分:最低位和其他位,于是这个数就可以表示为其他位乘10+最低位。比如ab=a*10+b,abcde=abcd*10+e。

位值法和连乘加法最大的不同在于:位值法是从右向左(低位向高位)计算的,它将一个数看成不断向左增加最高位的数,权重有n个;连乘加法是从左向右(高位向低位)计算的,它将一个数看成不断向右增加个位的数,权重只有两个(10和1);

正因为只有两个权重,使用连乘加法能达到神奇的效果:不用计算位数,也不用数组,核心代码只有一行,就能非常简单地实现数的合成。

代码如下:

#include<stdio.h>
#include<math.h>
int main(){
    int digit, num=0;
    while(scanf("%d", &digit)==1){
        num = num*10+digit;
    }
    printf("%d\n", num);
    return 0;
}

是不是非常简单呢?

需要说明的是,无论位值法还是连乘加法,都属于数学运算方法,这种方法都要注意一个问题:整数溢出(数值超过数据类型所能表示的最大值)。

二、字符型数的合成

为彻底避免整数溢出问题,就要将输入的数变成字符,输出的数变成字符串。这时问题就简单了,只需要把字符逐个保存到一个字符数组中再加一个’\0’即可。代码如下:

#include<stdio.h>
int main(){
    char c, str[100];
    int i=0;
    while(scanf(" %c", &c)==1){
        str[i++]=c;
    }
    str[i]='\0';
    printf("%s\n", str);
    return 0;
}

但是这种方式也要考虑一个问题,就是数组的空间要足够大。另外,实测需要按两次CTRL+Z才能结束输入,不知道为什么?感觉应该是编译器有BUG吧。

最后再附带一句,也有可能要求输入整数(单个数字),输出字符串,或者相反,输入字符输出整数(多位数)。这其实也很简单,涉及到整数和字符的转换,只需要利用数的ASCII码值与数值的对应关系(char型数-48=int型数)即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

金创想

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值