“超长数字减法(不考虑符号,仿人计算) ”的改进

easaca在发表了用Ada语言编写的“超长数字减法”,本人在评论中说明该算法有可改进之处,无乃评论字数不能超过1000字,因此将改进算法贴于此。新算法的特点重复如下:更加Ada-like,采用fixed_string完成,使用character'pos和character'val进行字符与数值之间的转换;使用a'first和a'length来保证串操作的通用性;小数点保留在串中,完全按人工计算的逻辑设计,包括小数点对齐、正负号判别、从后往前运算和借位的处理等等。经对比运算,改进算法的使用时间仅是原算法的1/3。对超长数字加法和乘法也进行了改进,其中乘法的速度提高更明显。

   function long_minus (a, b : in String) return String is
      use Ada.Strings.Fixed;

      dot_pos_a, dot_pos_b : Integer := 0;
      --dot postion in string a and b;

      before_dot_a, before_dot_b : Integer := 0;
      --the number of characters before dot in string a and b;

      after_dot_a, after_dot_b : Integer := 0;
      --the nubmer of chareacters after dot in string a and b;

      len : Integer;
      --the length of the result string;

      before_max, after_max : Integer;
      --before_max: the max number of characters before dot , which is the
      --number of
      --characters before dot in result string;
      --after_max: the max number of characters after dot , which is the
      --number of
      --characters after dot in result string;

      negative_result : Boolean := False;
      --the resut string is negative or not
      r_a : String := to_real (a);
      r_b : String := to_real (b);
   begin

      --determine dot position in string a;
      dot_pos_a    := Index (r_a, ".");
      before_dot_a := dot_pos_a - r_a'First;
      after_dot_a  := r_a'Length - dot_pos_a;

      --determine dot position in string b;
      dot_pos_b    := Index (r_b, ".");
      before_dot_b := dot_pos_b - r_b'First;
      after_dot_b  := r_b'Length - dot_pos_b;

      --determine the sign.
      if before_dot_a < before_dot_b then
         negative_result := True;
      elsif before_dot_a = before_dot_b then
         declare
            after_min : int := int'Min (after_dot_a, after_dot_b);
         begin
            for i in
                  r_a'First .. r_a'First + before_dot_a + 1 + after_min - 1
            loop
               if Character'Pos (r_a (i)) < Character'Pos (r_b (i)) then
                  negative_result := True;
                  exit;
               end if;
            end loop;
         end;
      end if;

      before_max := int'Max (before_dot_a, before_dot_b);
      after_max  := int'Max (after_dot_a, after_dot_b);
      len        := before_max + 1 + after_max;

      declare
         result, aa, bb : String (1 .. len) := (others => '0');
         --result: store the result;
         --aa,bb:temp string, dot position aligned
         --aa: if value(r_a)>value(r_b) then aa=r_a else aa=r_b
         --bb: if value(r_a)>value(r_b) then bb=r_b else bb=r_a

         borrowed : Boolean := False;
         --borrow state

         indented_a_pos : int := before_max - before_dot_a + 1;
         --r_a  is copied to aa or bb with indention so that the dot position
         --are aligned;

         indented_b_pos : int := before_max - before_dot_b + 1;
         --r_b is copied to aa or bb with indention so that the dot position
         --are aligned;

         zero_pos : int := Character'Pos ('0');
      --The position of character '0' in character set. It is used to
      --convert character to int and vice versa.
      begin
         --swap a and b if value(a) < value(b)
         if negative_result then
            bb (indented_a_pos .. indented_a_pos + r_a'Length - 1) := r_a;
            aa (indented_b_pos .. indented_b_pos + r_b'Length - 1) := r_b;
         else
            aa (indented_a_pos .. indented_a_pos + r_a'Length - 1) := r_a;
            bb (indented_b_pos .. indented_b_pos + r_b'Length - 1) := r_b;
         end if;
         borrowed := False;

         -- subtraction digit by digit in reverse order
         for i in reverse result'Range loop
            if aa (i) = '.' then
               result (i) := '.';
            else
               declare
                  aa_digit : int := int'Value ("" & aa (i));
                  bb_digit : int := int'Value ("" & bb (i));
               begin
                  if borrowed then
                     aa_digit := aa_digit - 1;
                  end if;
                  if aa_digit >= bb_digit then
                     result (i) :=
                        Character'Val (aa_digit - bb_digit + zero_pos);
                     borrowed   := False;
                  else
                     result (i) :=
                        Character'Val (10 + aa_digit - bb_digit + zero_pos);
                     borrowed   := True;
                  end if;

               end;

            end if; --if aa(i)='.'
         end loop;
         if negative_result then
            return "-" & result;
         else
            return result;
         end if;

      end;
   end long_minus;

  function long_addition (a, b : in String) return String is
      use Ada.Strings.Fixed;

      dot_pos_a, dot_pos_b : Integer := 0;
      --dot postion in string a and b;

      before_dot_a, before_dot_b : Integer := 0;
      --the number of characters before dot in string a and b;

      after_dot_a, after_dot_b : Integer := 0;
      --the nubmer of chareacters after dot in string a and b;

      len : Integer;
      --the length of the result string;

      before_max, after_max : Integer;
      --before_max: the max number of characters before dot , which is the
      --number of
      --characters before dot in result string;
      --after_max: the max number of characters after dot , which is the
      --number of
      --characters after dot in result string;
      r_a : String := to_real (a);
      r_b : String := to_real (b);
   begin

      --determine dot position in string a;
      dot_pos_a    := Index (r_a, ".");
      before_dot_a := dot_pos_a - r_a'First;
      after_dot_a  := r_a'Length - dot_pos_a;

      --determine dot position in string b;
      dot_pos_b    := Index (r_b, ".");
      before_dot_b := dot_pos_b - r_b'First;
      after_dot_b  := r_b'Length - dot_pos_b;

      before_max := int'Max (before_dot_a, before_dot_b);
      after_max  := int'Max (after_dot_a, after_dot_b);

      len := before_max + 1 + after_max;

      declare
         result, aa, bb : String (1 .. len) := (others => '0');
         --result: store the result;
         --aa,bb:temp string, dot position aligned
         --aa: if value(r_a)>value(r_b) then aa=r_a else aa=r_b
         --bb: if value(r_a)>value(r_b) then bb=r_b else bb=r_a

         carry : Integer := 0;
         --carry state

         indented_a_pos : int := before_max - before_dot_a + 1;
         --a  is copied to aa or bb with indention so that the dot position
         --are aligned;

         indented_b_pos : int := before_max - before_dot_b + 1;
         --b is copied to aa or bb with indention so that the dot position
         --are aligned;

         zero_pos : int := Character'Pos ('0');
      --The position of character '0' in character set. It is used to
      --convert character to int and vice versa.
      begin
         aa (indented_a_pos .. indented_a_pos + r_a'Length - 1) := r_a;
         bb (indented_b_pos .. indented_b_pos + r_b'Length - 1) := r_b;

         carry := 0;

         -- addition digit by digit in reverse order
         for i in reverse result'Range loop
            if aa (i) = '.' then
               result (i) := '.';
            else
               declare
                  aa_digit      : int    := int'Value ("" & aa (i));
                  bb_digit      : int    := int'Value ("" & bb (i));
                  sum_digit     : int    := aa_digit + bb_digit + carry;
                  sum_digit_img : String := int'Image (sum_digit);
               begin
                  result (i) := sum_digit_img (sum_digit_img'Last);
                  if sum_digit >= 10 then
                     carry :=
                       Character'Pos
                            (sum_digit_img (sum_digit_img'Last - 1)) -
                       zero_pos;
                  else
                     carry := 0;
                  end if;

               end;

            end if;
         end loop;
         if carry > 0 then
            return Character'Val (carry + zero_pos) & result;
         else
            return result;
         end if;

      end;
   end long_addition;

   function long_multiply (a, b : in String) return String is
      use Ada.Strings.Fixed;

      dot_pos_a, dot_pos_b : Integer := 0;
      --dot postion in string a and b;

      before_dot_a, before_dot_b : Integer := 0;
      --the number of characters before dot in string a and b;

      after_dot_a, after_dot_b : Integer := 0;
      --the nubmer of chareacters after dot in string a and b;

      len : Integer;
      --the length of the result string;

      before_max, after_max : Integer;
      --before_max: the max number of characters before dot , which is the
      --number of
      --characters before dot in result string;
      --after_max: the max number of characters after dot , which is the
      --number of
      --characters after dot in result string;
      r_a : String := to_real (a);
      r_b : String := to_real (b);
   begin

      --determine dot position in string a;
      dot_pos_a    := Index (r_a, ".");
      before_dot_a := dot_pos_a - r_a'First;
      after_dot_a  := r_a'Length - dot_pos_a;

      --determine dot position in string b;
      dot_pos_b    := Index (r_b, ".");
      before_dot_b := dot_pos_b - r_b'First;
      after_dot_b  := r_b'Length - dot_pos_b;

      before_max := int'Max (before_dot_a, before_dot_b);
      after_max  := int'Max (after_dot_a, after_dot_b);

      len := r_b'Length + r_a'Length + 1;

      declare
         result, resultx : String (1 .. len) := (others => '0');
         --result: store the result;

         carry : Integer := 0;
         --carry state

         zero_pos : int := Character'Pos ('0');
         --The position of character '0' in character set. It is used to
         --convert character to int and vice versa.
         right_aligned_pos : int     := 0;
         result_pos        : Natural := 1;
         aa_digit          : int;
         bb_digit          : int;
         result_digit      : Character;
      begin

         carry             := 0;
         right_aligned_pos := 0;
         -- multiplication digit by digit in reverse order
         for b_index in reverse r_b'Range loop
            if r_b (b_index) /= '.' then
               bb_digit   := int'Value ("" & r_b (b_index));
               result_pos := resultx'Last;
               resultx    := (others => '0');
               carry      := 0;
               for a_index in reverse r_a'Range loop
                  if r_a (a_index) /= '.' then

                     aa_digit := int'Value ("" & r_a (a_index));
                     declare
                        product     : int    := aa_digit * bb_digit + carry;
                        product_img : String := int'Image (product);
                     begin
                        result_digit := product_img (product_img'Last);
                        if product >= 10 then
                           carry :=
                             Character'Pos
                                  (product_img (product_img'Last - 1)) -
                             zero_pos;
                        else
                           carry := 0;
                        end if;

                     end;
                     resultx (result_pos + right_aligned_pos) := result_digit;
                     result_pos                               := result_pos -
                                                                 1;
                  end if;
               end loop;
               if carry > 0 then
                  resultx (result_pos + right_aligned_pos) :=
                     Character'Val (carry + zero_pos);
                  carry                                    := 0;
               end if;
               declare
                  tmp_result : String  := int_long_addition (result, resultx);
                  len_dif    : Natural := tmp_result'Length - result'Length;
               begin
                  result :=
                    tmp_result (tmp_result'First + len_dif .. tmp_result'Last);
               end;

               right_aligned_pos := right_aligned_pos - 1;
            end if;
         end loop;
         declare
            dot_pos            : int := after_dot_b + after_dot_a;
            first_non_zero_pos : int := 0;
         begin
            for i in result'Range loop
               if result (i) /= '0' then
                  first_non_zero_pos := i;
                  exit;
               end if;
            end loop;

            return result (first_non_zero_pos .. result'Length - dot_pos) &
                   "." &
                   result (result'Length - dot_pos + 1 .. result'Length);
         end;

      end;
   end long_multiply;

   function to_real (a : String) return String is
      use Ada.Strings.Fixed;
      pos : Integer := Index (a, ".");
   begin
      if pos = 0 then
         return a & ".0";
      else
         return a;
      end if;
   end to_real;

   function int_long_addition (a, b : String) return String is
      a_len            : Natural               := a'Length;
      b_len            : Natural               := b'Length;
      max_len          : Natural               := int'Max (a_len, b_len);
      a_len_diff       : Natural               := max_len - a_len;
      b_len_diff       : Natural               := max_len - b_len;
      aa, bb, result   : String (1 .. max_len) := (others => '0');
      carry            : Natural               := 0;
      a_digit, b_digit : int;
      zero_pos         : Natural               := Character'Pos ('0');
      sum              : Natural               := 0;
   begin
      aa (aa'First + a_len_diff .. aa'Last) := a;
      bb (bb'First + b_len_diff .. bb'Last) := b;
      for i in reverse result'Range loop
         a_digit := Character'Pos (aa (i)) - zero_pos;
         b_digit := Character'Pos (bb (i)) - zero_pos;
         sum     := a_digit + b_digit + carry;
         declare
            sum_img : String := int'Image (sum);
         begin
            result (i) := sum_img (sum_img'Last);
            if sum >= 10 then
               carry := Character'Pos (sum_img (sum_img'Last - 1)) -
                        zero_pos;
            else
               carry := 0;
            end if;

         end;
      end loop;
      if carry > 0 then
         return Character'Val (carry + zero_pos) & result;
      else
         return result;
      end if;

   end int_long_addition;

 

其中to_real和int_long_addition是两个辅助函数,分别用于将整数转换为实数和超长整数加法。

easaca的超长乘法尚有语法错误,必须修改才能通过编译。

下面是测试程序及运行结果

with long_algebra;   use long_algebra;
with Ada.Text_IO;    use Ada.Text_IO;
with Ada.Calendar;   use Ada.Calendar;
with Ada.Exceptions; use Ada.Exceptions;
with ada.Long_Float_Text_IO;
use ada.Long_Float_Text_IO;
procedure main is
   a1             : String := "8230";
   a2             : String := "82211";
   a3             : String := a1 - a2;
   a4             : String := long_minus (a1, a2);
   star_product:string:=a1*a2;
   long_product:string:=long_multiply(a1,a2);
   start_t, end_t : Time;
begin

   start_t := Clock;
   for i in 1 .. 1000 loop
      a3 := a1 - a2;
   end loop;
   end_t := Clock;
   Put_Line
     ("       " &      """" &      "-" &      """" &      "(" &      a1 &      "," &      a2 &
      ")=" &      a3 &      " time span:" &      Duration'Image (end_t - start_t));
   start_t := Clock;
   for i in 1 .. 1000 loop
      a4 := long_minus (a1, a2);
   end loop;
   end_t := Clock;
   Put_Line
     ("long_minus(" &      a1 &      "," &      a2 &      ")=" &
      a4 &      " time span:" &      Duration'Image (end_t - start_t));

   declare
      a6 : String := a1 + a2;
   begin
      Put_Line ("a1 + a2=" & a6);
   exception
      when E : others =>
         Put_Line ("a1 + a2 exception.");
         Put_Line (Exception_Message (E));
   end;
   declare
      a7 : String := long_addition(a1 , a2);
   begin
      Put_Line ("long_addition(a1,a2)=" & a7);
   exception
      when E : others =>
         Put_Line ("a1 + a2 exception.");
         Put_Line (Exception_Message (E));
   end;
   declare
      a5 : String := a1 * a2;
      a1_r:long_float:=long_float'value(a1);
      a2_r:long_float:=long_float'value(a2);
      a5_r:long_float:=a1_r*a2_r;

   begin
      Put_Line ("a1 * a2=" & a5);
      put("a1_r*a2_r=");
      put(a5_r,10,10,0);
      new_line;
   exception
      when E : others =>
         Put_Line ("a1 * a2 exception.");
         Put_Line (Exception_Message (E));
   end;

   put_line("int_long_add(000133+9999)="&int_long_addition("000133","9999"));
   put_line("long_multiply(a1,a2)="&long_multiply(a1,a2));

   start_t := Clock;
   for i in 1 .. 1000 loop
      star_product := a1* a2;
   end loop;
   end_t := Clock;
   Put_Line
     ("star_product(" &      a1 &      "," &      a2 &      ")=" &
      star_product &      " time span:" &
      Duration'Image (end_t - start_t));

   start_t := Clock;
   for i in 1 .. 1000 loop
      long_product := long_multiply(a1,a2);
   end loop;
   end_t := Clock;
   Put_Line
     ("long_product(" &      a1 &      "," &      a2 &
      ")=" &      long_product &
      " time span:" &      Duration'Image (end_t - start_t));

exception
   when E:others=>
         Put_Line ("in main.");

      Put_Line (Exception_Message (E));

end main;

D:\zhenggen\src\long_algebra\main
       "-"(8230,82211)=-73981 time span: 0.010781325 (easaca减法)
long_minus(8230,82211)=-73981.0 time span: 0.003780305
a1 + a2=90441
long_addition(a1,a2)=90441.0
a1 * a2=676596530
a1_r*a2_r= 676596530.0000000000
int_long_add(000133+9999)=010132
long_multiply(a1,a2)=676596530.00
star_product(8230,82211)=676596530 time span: 0.049297256 (easaca乘法)
long_product(8230,82211)=676596530.00 time span: 0.011648885
[2012-02-07 16:28:10] process terminated successfully (elapsed time: 00.33s)

编译环境:

GPS 4.4.1 (20091215) hosted on i686-pc-mingw32
GNAT GPL 2010 (20100603)

the GNAT Programming Studio

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值