zoukankan      html  css  js  c++  java
  • [算法 笔记]大数相乘

      今天去参加腾讯笔试,其中有一道选答题:大数相乘问题。在编写代码的过程,我突然发现以前写的原始的大数相乘是一个很简陋的源码。所以,下午找个时间重新写了一份。

      大数相乘:两个超出整型限制的两个数相乘,例如,两个50位的正数相乘。

      最简陋的方式,就是按照乘法的计算过程来模拟计算:

           1 2

        × 3 6

       ---------- ---- 其中,上标数字为进位数值。

         71 2  --- 在这个计算过程中,2×6=12。本位保留2,进位为1.这里是一个简单的计算过程,如果在高位也需要进位的情况下,如何处理?

        3 6

        -----------

        413  2

      开始比较简陋的源码就是基本模拟上述的乘法过程:

     1 int compute_value( const char *lhs, int lhs_start_index,
     2                    const char *rhs, int rhs_start_index,
     3                    char *result )
     4 {
     5     int i = 0, j = 0, res_i = 0;
     6     int tmp_i = 0;
     7     int carry = 0;
     8 
     9     for ( i = lhs_start_index; lhs[i] != ''; ++i, ++tmp_i )
    10     {
    11         res_i = tmp_i;  // 在每次计算时,结果存储的位需要增加。如上述模拟过程中,第二行。
    12         carry = 0;
    13 
    14         for ( j = rhs_start_index; rhs[j] != ''; ++j )
    15         {
    16             int tmp_lhs = lhs[i] - '0';
    17             int tmp_rhs = rhs[j] - '0';
    18             carry += ( result[res_i] - '0' );       // 这里需要注意,因为每次计算并不能保证以前计算结果的进位都消除,因此这里是加号。
    19             carry += ( tmp_lhs * tmp_rhs );      // 并且以前的计算结果也需要考虑。
    20             result[res_i++] = ( carry % 10 + '0' );
    21             carry /= 10;
    22         }
    23 
    24         while ( carry )  // 当乘数的一次计算完成,可能存在有的进位没有处理。因此,这里对其进行处理。
    25         {
    26             result[res_i++] = ( carry % 10 + '0' );
    27             carry /= 10;
    28         }
    29     }
    30     result[res_i] = '';
    31 
    32     return res_i;
    33 }

      上述源码能够完成基本的运算,比如非负数,非小数等情况。如果在传递的参数是不规则或不正确,例如,“  -1234”, "+1234", “- 123”。这里的处理有点类似于atoi函数(http://blog.csdn.net/v_july_v/article/details/9024123)的处理。

      因此,大数相乘是一个陷阱多多的函数,其中一个容易被忽略的陷阱就是:按照正常人的习惯,高数位存放在最左端,个位放在右端,例如 1223的字符串为“1223”,但是在上述函数计算过程中,是从左到右计算的。这是一个非常容易被忽略的错误,也是最致命的错误之一。

      整体思路如下:

      1. 检查参数的合理性;

      2. 判断传递参数是正负数;

      3. 判断传递参数是否为小数;

      4. 翻转并计算数值;

      5. 如果存在小数,需要将结果中小数点的放置;

      6. 如果计算结果为负值,则将结果设置为负值。

      全部源码如下:

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 #include <assert.h>
      5 #include <ctype.h>
      6 
      7 // 翻转data[start...end-1]
      8 void reverse_data( char *data, int start, int end  )
      9 {
     10     char temp = '0';
     11 
     12     assert( data != NULL && start < end );
     13     while ( start < end )
     14     {
     15         temp = data[start];
     16         data[start] = data[--end];
     17         data[end] = temp;
     18         ++start;
     19     }
     20 }
     21 
     22 /**< 判断数据中数值的合法性,以及非空格字符的起始位置。
     23  *  1. 是否均有正确的正负号,例如"+123", "-123"等
     24  *  2. 字符串中字符是否均是数字字符。
     25  *  3. 字符串开始部分放入空格是合法的,例如,”  +123“等
     26  *  4. 只有一个'.'标记
     27  **< 参数:
     28  * @data: 表示传入字符;
     29  * @nonspace_index:非空格起点
     30  **< 返回值:若数据是合法数值,则返回1;否则返回0;
     31  */
     32 int check_logic( const char *data, int *nonspace_index )
     33 {
     34     int flag = 1;
     35     int start = 0;
     36     int point_cnt = 0;
     37 
     38     assert( data != NULL );
     39     /* PS. if data is not space(' ', '
    '), isspace() return 0. */
     40     for ( ; isspace( data[start] )!= 0
     41             && data[start] != ''; ++start );
     42 
     43     // 判断数据是否为负数
     44     *nonspace_index = start;
     45     if ( data[start] == '-' || data[start] == '+' )
     46     {
     47         ++start;
     48     }
     49 
     50     /* PS. if ch is digit character, isdigit() return 1; otherwise return 0. */
     51     for ( ; data[start] != ''; ++start )
     52     {
     53         if ( isdigit( data[start] ) || data[start] == '.' )
     54         {
     55             // 判断数据为小数的格式是否正确。
     56             if ( data[start] == '.' && point_cnt == 0 )
     57             {
     58                 ++point_cnt;
     59             }
     60             else if ( point_cnt > 1 )
     61             {
     62                 break;
     63             }
     64         }
     65     }
     66 
     67     // 若小数点后面无数据,则不合法
     68     if ( data[start] != '' )
     69     {
     70         flag = 0;
     71     }
     72 
     73     return flag;
     74 }
     75 
     76 /**< notice: 传入到该函数的数据已经被翻转后的数值。即,最左边为个位,最右边为最高位
     77  **< return: 结果数据的长度。
     78  */
     79 int compute_value( const char *lhs, int lhs_start_index,
     80                    const char *rhs, int rhs_start_index,
     81                    char *result )
     82 {
     83     int i = 0, j = 0, res_i = 0;
     84     int tmp_i = 0;
     85     int carry = 0;
     86 
     87     for ( i = lhs_start_index; lhs[i] != ''; ++i, ++tmp_i )
     88     {
     89         res_i = tmp_i;
     90         carry = 0;
     91 
     92         for ( j = rhs_start_index; rhs[j] != ''; ++j )
     93         {
     94             int tmp_lhs = lhs[i] - '0';
     95             int tmp_rhs = rhs[j] - '0';
     96             carry += ( result[res_i] - '0' );
     97             carry += ( tmp_lhs * tmp_rhs );
     98             result[res_i++] = ( carry % 10 + '0' );
     99             carry /= 10;
    100         }
    101 
    102         while ( carry )
    103         {
    104             result[res_i++] = ( carry % 10 + '0' );
    105             carry /= 10;
    106         }
    107     }
    108     result[res_i] = '';
    109 
    110     return res_i;
    111 }
    112 
    113 int has_point( char *data, int index, int *point_index )
    114 {
    115     int start = index;
    116 
    117     for ( ; data[start] != ''; ++start )
    118     {
    119         if ( data[start] == '.' )
    120         {
    121             *point_index = start;
    122             break;
    123         }
    124     }
    125 
    126     return ( data[start] != '' );
    127 }
    128 
    129 int is_neg( char *data, int *index )
    130 {
    131     int flag = 0;
    132     int start = *index;
    133     if ( data[start] == '-' || data[start] == '+' )
    134     {
    135         if ( data[start] == '-' )
    136             flag = 1;
    137         ++start;
    138     }
    139 
    140     *index = start;
    141     return flag;
    142 }
    143 
    144 void copy_c( char * dest, const char *src )
    145 {
    146     while ( *src != '' )
    147     {
    148         if ( *src != '.' )
    149             *dest++ = *src;
    150         src++;
    151     }
    152 }
    153 
    154 int compute_decimals( char *lhs, int lhs_point_index,
    155                       char *rhs, int rhs_point_index,
    156                       char *result  )
    157 {
    158     int lhs_length = strlen( lhs );
    159     int rhs_length = strlen( rhs );
    160     int result_point_index = lhs_length + rhs_length;
    161     int result_length = 0, i = 0;
    162     char *tmp_lhs = NULL;
    163     char *tmp_rhs = NULL;
    164 
    165     // 计算在结果中放置小数点的位置,根据的是两个小数部分长度之和
    166     // 例如,rhs = "12.345", lhs = "3.45", result = "xxx.xxxxx"
    167     result_point_index -= ( lhs_point_index + rhs_point_index );
    168 
    169     // 分配并拷贝
    170     if ( lhs_point_index )
    171     {
    172         tmp_lhs = (char *)malloc( sizeof(char) * lhs_length );
    173         assert( tmp_lhs != NULL );
    174         copy_c( tmp_lhs, lhs );
    175         tmp_lhs[lhs_length - 1] = '';
    176     }
    177     else
    178     {
    179         tmp_lhs = lhs;
    180     }
    181 
    182     if ( rhs_point_index )
    183     {
    184         tmp_rhs = (char *)malloc( sizeof(char) * rhs_length );
    185         assert( tmp_rhs != NULL );
    186         copy_c( tmp_rhs, rhs );
    187         tmp_rhs[rhs_length - 1] = '';
    188     }
    189     else
    190     {
    191         tmp_rhs = rhs;
    192     }
    193 
    194     // tmp_lhs比lhs少一个小数点
    195     reverse_data( tmp_lhs, 0, lhs_length - 1 );
    196     reverse_data( tmp_rhs, 0, rhs_length - 1 );
    197     result_length = compute_value( tmp_lhs, 0, tmp_rhs, 0, result );
    198     for ( i = result_length; i > result_point_index; --i )
    199     {
    200         result[i] = result[i - 1];
    201     }
    202 
    203     result[result_point_index] = '.';
    204     ++result_length;
    205     result[result_length] = '';
    206 
    207     // 释放资源
    208     if ( lhs_point_index )
    209     {
    210         free( tmp_lhs ), tmp_lhs = NULL;
    211     }
    212 
    213     if ( rhs_point_index )
    214     {
    215         free( tmp_rhs ), tmp_rhs = NULL;
    216     }
    217 
    218     return result_length;
    219 }
    220 
    221 // 返回结果数值的长度
    222 int big_number_multiply( char *lhs, char *rhs, char *result )
    223 {
    224     int lhs_start_index = 0, lhs_point_index = 0;
    225     int rhs_start_index = 0, rhs_point_index = 0;
    226     int result_is_neg = 0;
    227     int result_length = 0;
    228 
    229     assert( lhs != NULL && rhs != NULL && result != NULL );
    230     // 检查数据的合法性
    231     if ( !(check_logic( lhs, &lhs_start_index )
    232             && check_logic( rhs, &rhs_start_index )) )
    233     {
    234         return -1;
    235     }
    236 
    237     // 检查数据是否为负数
    238     result_is_neg = is_neg( lhs, &lhs_start_index );
    239     if ( is_neg( rhs, &rhs_start_index) )
    240     {
    241         result_is_neg = result_is_neg == 1 ?  0 : 1;
    242     }
    243 
    244     // 检查是否两个数值中存在一个小数或两者都是小数
    245     if ( !( has_point( lhs, lhs_start_index, &lhs_point_index )
    246             && has_point( rhs, rhs_start_index, &rhs_point_index ) ) )
    247     {
    248         reverse_data( lhs, lhs_start_index, strlen(lhs) );
    249         reverse_data( rhs, rhs_start_index, strlen(rhs) );
    250         result_length = compute_value( lhs, lhs_start_index,
    251                                        rhs, rhs_start_index, result );
    252         reverse_data( lhs, lhs_start_index, strlen(lhs) );
    253         reverse_data( rhs, rhs_start_index, strlen(rhs) );
    254     }
    255     else // 一个数值中有小数部分
    256     {
    257         result_length = compute_decimals(
    258                           lhs + lhs_start_index, lhs_point_index - lhs_start_index + 1,
    259                           rhs + rhs_start_index, rhs_point_index - rhs_start_index + 1,
    260                           result );
    261     }
    262     if ( result_is_neg )
    263         result[result_length++] = '-';
    264     reverse_data( result, 0, result_length );
    265     result[result_length] = '';
    266 
    267     return result_length;
    268 }
    269 
    270 int main()
    271 {
    272     char lhs[] = "-1.235";
    273     char rhs[] = "    3.456";
    274     char result[40];
    275 
    276     memset( result, '0', sizeof(result) );
    277 
    278     big_number_multiply( lhs, rhs, result );
    279     printf( "%s
    ", result );
    280     return 0;
    281 }
    View Code

      这里我仅仅提供了一个简单的测试,欢迎大家来指导!!

  • 相关阅读:
    金融资产的票面利率与实际利率
    对于确定承诺的外汇风险,既属于公允价值套期,又属于现金流量套期,怎么区分呢?
    套期工具(公允价值套期与现金流量套期)
    R语言使用 LOWESS技术图分析逻辑回归中的函数形式
    R语言ROC曲线下的面积
    R语言Poisson回归的拟合优度检验
    R语言在逻辑回归中求R square R方
    R平方/相关性取决于预测变量的方差
    stata具有异方差误差的区间回归
    R语言用于线性回归的稳健方差估计
  • 原文地址:https://www.cnblogs.com/life91/p/3389890.html
Copyright © 2011-2022 走看看