23. juli 2008 - 22:40Der er
14 kommentarer og 1 løsning
Lommeregner - tal som bits eller tekststrenge
Hej eksperter.
Jeg er gået i gang med overvejelserne om min alt-i-en-lommeregner, som jo af navnets antydning skal kunne alt. Og det er jo ikke så lille et krav faktisk. Jeg vil gerne have, at min lommeregner kan regne med flere milliarder cifre lange tal, og jeg har da overvejet to ting:
* At lave koderne for tallene som binære koder i et gigantisk array. * At skrive tallet som en tekststreng.
Men kan det ikke passe, at de tegn, der indgår i en tekststreng, alle fylder 16 bit? Hvis jeg har regnet rigtigt, kan jeg i længden ved meget store tal komme med på en 53. del ved brug af de binære koder. 16 * ceil(log_2(n)) / ceil(log(n)) nærmer sig 53,15, når n bliver stor nok.
Og beregninger med et tal skrevet som et kæmpe array med bits kan vel ikke tage længere tid end de normale beregninger, det tager med de normale tal, eftersom computeren vel alligevel omskriver tallene til bits?
Hvis mine informationer er rigtige, og jeg har regnet rigtigt, så kan det godt betale sig. Men hvad så når jeg skal omskrive fra de binære koder til strenge, som kan læses af os menneskelige, så skal der jo regnes en hulens masse. Og når det handler om at lægge et sjovt tal til en lang tekststreng, skal jeg jo så at sige næsten behandle hvert ciffer i tallet. Og det skal jeg jo gøre mange, mange gange. Eksempelvis koden 101001001 skal jo regne 2+16+128+512 ud, og hvert af disse tal skal udregnes ud fra en potens af to. Og når det er gjort, skal strengen også vises, og så husker programmet både på den binære kode og tekststrengen, og så fylder det pludseligt alt for meget. Hvad kan jeg gøre for at lette på det? Jeg vil jo gerne udnytte, at bits fylder ingenting, når nu jeg skal arbejde med gigantiske tal.
Du sparer masser af plads ved at gemme binaert fremfor decimalt, men det koster mere at konvertere for input og output. Det har vaeret kendt i 40-50 aar. Det er den klassiske diskussion om BCD versus rigtige binaere integers om igen (BCD bruger dog kun 4 bit per ciffer og er derfor noget mere konkurrence dygtig end en char per ciffer !!).
Mit fokus ligger begge steder, for jeg har en lommeregner allerede, og i en notesblok kan jeg skrive lige så store tal, jeg har lyst til. Men jeg har brug for en lommeregner, der kan regne med kæmpestore tal - og den skal jo selvfølgelig være præcis. Det nytter ikke, at den forsøger at lægge to til 1.8946151081151e+300, fordi det ændrer slet ikke på det store tal alligevel.
Jeg har også overvejet, om man kunne splitte et tal som 12345674891234567489 op i et array af 1234567489 og 1234567489. Så kunne man jo faktisk gemme meget store tal i en Int64[], og det ville ikke tage nogen ressourcer overhovedet at konvertere hele arrayet til en stor streng, der kan regnes på.
Hvis du har J# runtime installeret kan du tilføje en ref til vjslib.dll og kode som:
using System;
using java.math;
namespace E { public class Program { public static void Main(string[] args) { BigInteger v = new BigInteger("1"); for(int i = 1; i < 100; i++) { v = v.multiply(BigInteger.valueOf(i)); Console.WriteLine("fac(" + i + ") = " + v); } Console.ReadKey(); } } }
For regning med strenge lavede jeg i 2003 følgende i Pascal til et spørgsmål:
program BigInt;
{$APPTYPE CONSOLE}
uses SysUtils;
function numval(c : char) : integer;
begin numval := ord(c) - ord('0'); end;
function strval(v : integer) : char;
begin strval := chr(ord('0') + v); end;
function add(v1,v2 : string) : string;
var res : string; ix1, ix2, tmp, carry : integer;
begin res := ''; carry := 0; ix1 := length(v1); ix2 := length(v2); while (carry > 0) or (ix1 > 0) or (ix2 > 0) do begin tmp := carry; if(ix1 > 0) then begin tmp := tmp + numval(v1[ix1]); ix1 := ix1 - 1; end; if(ix2 > 0) then begin tmp := tmp + numval(v2[ix2]); ix2 := ix2 - 1; end; res := strval(tmp mod 10) + res; carry := tmp div 10; end; add := res; end;
function mulpow10(v : string; pow10 : integer) : string;
var res : string; i : integer;
begin res := v; for i := 1 to pow10 do begin res := res + '0'; end; mulpow10 := res; end;
function mulone(v : string; num : integer) : string;
var res : string; ix, tmp, carry : integer;
begin res := ''; carry := 0; ix := length(v); while (carry > 0) or (ix > 0) do begin tmp := carry; if(ix > 0) then begin tmp := tmp + num * numval(v[ix]); ix := ix - 1; end; res := strval(tmp mod 10) + res; carry := tmp div 10; end; mulone := res; end;
function mul(v1,v2 : string) : string;
var res : string; i : integer;
begin res := '0'; for i := 1 to length(v2) do begin res := add(res,mulpow10(mulone(v1,numval(v2[i])),length(v2)-i)); end; mul := res; end;
var i : integer; fac : string;
begin (* functionality test *) writeln(add('1234','12345678')); writeln(mulpow10('123',2)); writeln(mulone('123456',2)); writeln(mul('12345','12345')); (* performance test *) fac := '1'; for i := 1 to 500 do begin fac := mul(fac, inttostr(i)); writeln(i,' ',fac); end; end.
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.