Progresul hardware, episodul 24: Incursiunea cea în trecut continuă (Despre lucrul cu numerele)

      Coborând mai mult în timp, mai scriam în 2014 că la 10 august 2010 erau 1733 de numere (înseamnă că perioada 20 august - 16 septembrie a fost mai fructuoasă, m-am dedicat mai îndeaproape numerelor, mărind limita lor de sus și numărul lor). Încă împărțeam, totuși, numerele de fond 1 în două categorii (da, a existat o vreme când până și fondul 1, singur existent, a fost împărțit în ceva), criteriul fiind cel mai mic divizor impar al fiecărui număr - dacă era mai mic de 2 la puterea 64, nu era obligatorie reprezentarea lui cu MIRACL și trecea la numerele mici, pentru care era fișierul BAZNUM.TXT în 10 august, dar dacă era mai mare, tipul hardware (adică firesc, „natural”) long double al C++-ului nu putea să mai dea corect numărul, se făceau trunchieri în memorie până ce divizorul impar ajungea la o valoare rezonabilă, și fiind o legătură cu puterile lui 2, diferența dintre noul număr (cel cu divizorul) și cel original era o putere a lui 2, dar era de ajuns să difere cu 1 ca să nu mai fie ce voiam, de aceea fusesem nevoit să trec la MIRACL, ca furnizor de tipuri de date mai largi decât long double, ca să studiez și numere mai mari, și le puneam rubricate pe abundențe în fișierul BAZGIG.TXT, precum și cele din BAZNUM erau puse pe căprării, organizate pe abundențe.
     
      Știu că pe la 10 august 2010 aveam de-a face cu numere de 50-60 de cifre în BAZGIG, înseamnă că limita lor de sus s-a mărit, pe 16 septembrie puterea de 10 limită era 119, pe 25 131 și pe 1 octombrie 2010 138. În acele ultime șase zile am lucrat de la București la mărirea cu multiperfecte și semiperfecte a stocului, pe atunci încă puteau fi puse într-un mesaj pe Gmail și preluate din el, nu știam nimic despre existența lui TeamViewer. Între timp au mai fost dăți în care să accesez numerele din afară, dar în special cu TeamViewer, lăsându-le acasă în lucru.
     
      Numărul de numere cu divizor impar mic era în 2010, și precis a rămas până astăzi, sub 1000, știu că în luna iulie 2010 fondul 1 era împărțit într-o proporție apropiată de 900 MIRACL + 650 long double. Între 23 iulie și 10 august 2010 am stat de asemenea cu numerele și le-am făcut să ajungă, de la un total între 1500 și 1600 (nu am memorat câte erau exact pe 23 iulie), la 1733.
     
      Am mai scris, tot în 2014, de numărătoarea numerelor de la 1 iulie 2010 (în fapt, nu m-am culcat, ci am stat până la lumina zilei joii de 1 iulie 2010 ca să aranjez numerele în BAZNUM și în fișierul cu numere MIRACL, precis încă nenumit BAZGIG), când erau, cu bună aproximație, 880 mari și 680 mici. În șapte ani și trei luni sigur s-a mai adăugat ceva la ultimele 680, dar nu încât să atingă 1000. Mă tem că în zona divizorilor impari mici nu mai pot să găsesc noutăți, am acoperit de atunci spațiul lor prin căutăturile extensive care au fost.
     
      După 23 iulie în 2010 am descoperit că la MIRACL tipul întreg (Big) era de vreo șapte ori mai rapid decât cel Flash (numere reale), cu care începusem întâi lucrul de la desprinderea de long double. Cum în Borland C++ tipul real long double fusese cel de lucru, am văzut în Flash-ul de la MIRACL continuatorul lui, în mai 2010, când aveam nevoie de numerele reale pentru reprezentarea abundențelor și nu eram în stare să obțin încadrarea fracționării abundenței prin lucrul exclusiv cu numere întregi (naturale, de fapt, că negativele nu au avut și nu au niciodată loc aici).

      În lucrul cu MIRACL, ca mai târziu și la GMP/MPIR, am găsit că pot să folosesc numerele normale (long double, int) în combinație cu cele miracliene, de exemplu numerele prime componente maxim egale cu 2147483647 puteau să rămână într-un tip de date C++ și înmulțirea unui întreg C++ cu unul miraclian putea să dea alt număr miraclian. Nu trebuia, o dată trecut la MIRACL, să folosesc numai Flash pentru tot, ceea ce oferea un plus de rapiditate, altfel Flash-ul fiind lent față de long double, fiind el construit în mod software ca tip numeric de date de precizie arbitrară, pe când long double este tip hardware numeric, comunică mai direct cu regiștrii procesorului și are rapiditate specifică. Iar după 23 iulie 2010 am văzut că Big-ul este mult mai rapid și că pot să-l folosesc pe el pentru numerele mari - făceam teste de viteză unde puneam să se afișeze cu regularitate puncte în linia de comandă, iar punctele de la Big se strângeau mai multe, se adunau mai repede decât la Flash.
     
      Tot în acele zile de ultimă săptămână de iulie, singur cu numerele, am inventat programul (modulul) VECUN.CPP ca să caut numere noi pe baza celor existente, memorate în vector și folosind, cu înmulțire ori împărțire, alt vector, cu coeficienți de legătură (obținuți în timp, cu răbdare, apoi generați programatic) rezultați tot din înmulțiri și împărțiri între numere. Dacă aveam de parcurs 800 de numere și aveam 3000 de coeficienți de legătură, fie luam la înmulțit fiecare număr cu coeficienții, fie la împărțit, unde însă mergeam mai rapid, nu toți coeficienții fiind efectiv tratați de fiecare număr în parte, nefiind ei cu toții divizori ai numărului, în schimb la înmulțire fiecare număr se înmulțea negreșit cu fiecare coeficient și produsului i se verifica suma divizorilor, obținubilă acum din parcurgerea vectorilor de numere prime (long cu Flash sau, mai târziu, long cu Big) și înmulțirea sumelor parțiale făcute de la puterile de factori primi cu care numărul acela (produsul) se împărțea.

Valabil și pentru câtul dintre numărul de bază și coeficientul de legătură, când exista divizibilitate, în plus câturile fiind și numere mai mici, nu doar mai puține, față de produse, așa că la împărțiri se cheamă că făceam căutare în jos și mergea mai repede. Și astăzi, la VECUN, cum sunt aceste ultime zile cu noi fructe la fondul 1, tot la căutarea în jos se merge mai repede, chiar dacă sunt de vreo 10 ori mai mulți coeficienți de legătură folosiți la ea față de cea în sus, mai mari la ea și mai mici la cea în sus.

      Și VECUNG.CPP a fost făcut tot atunci, ca o alterare a lui VECUN (de unde și numele), dar nu țin minte prin ce diferea de VECUN. În mod sigur nu avea aplicabilitatea de astăzi, când îl folosesc la actualizarea marilor depozite numerice dacă vreau folosirea de mepezetele direct, fără vector mare de șiruri de caractere, și cu mai puțină memorie ocupată decât la vectorul de șiruri. Nu, în vara anului 2010 era și el folosit la căutarea numerelor pe principiul unele din altele.
     
      Descoperirea că pot face suma divizorilor pe baza puterilor de factori primi am făcut-o stând târziu în noaptea de 20 mai din 2010, când am accesat câteva pagini dintr-o veche carte străină (lingvistic) de teoria numerelor, unde se dădea tocmai formula de calcul a sumei divizorilor unui număr pe baza factorizării lui prime. Cum numerele prime din numerele-nucleu, semiperfecte și multiperfecte, de la care plecam în căutarea altora aveau o anumită repetitivitate, și neavând o rată foarte mare de creștere în număr atunci când se diversificau de la semi-multi la semi-multi (în 2010 nu am depozitat mai mult de câteva sute, în fapt erau chiar sub o sută de prime în luna mai, când îl părăseam pe long double, și nici astăzi numărul lor nu ajunge la 6000), nu era o problemă să le depozitez într-un vector de numere întregi suficient de mici ca să fie cuprinse într-un tip numeric firesc al lui C++.

Mai târziu, pe măsură ce numerele mari tot creșteau, au început să apară niște divizori primi care erau mai mari de 2^32, dar nu țin minte când în calendar. 2 la puterea 32 și nu 64, pentru că depindeam de întregii cu semn de 32 de biți ai lui Borland C++, nu aveam la dispoziție un unsigned long long (alias gmp_ui) pe 64 de biți, ca la GNU C astăzi, și nici tipul long double nu era de folosit pentru numerele prime componente. Așa că dacă un număr mare se împărțea la un număr prim mai mare decât 2147483647, primul acela trebuia să stea într-un vector miraclian - cred că a apucat să fie Flash, până ce în iulie am trecut la Big cu numerele mari, nefiind binevenită împărțirea heterogenă, de tipul Flash (marele) la Big (cel prim). În Windows, 32-bit cu semn, cu MIRACL și Borland C++ 5.5.1 Command Line Tools (2009-2012), limita de mărime în reprezentarea normală, C++-iană, a unui număr natural, a fost 2147483647, nu 18446744073709551615.
     
      Descoperirea cu cel mai mare divizor impar am folosit-o în primăvara anului 2010, când încă încercam să storc de la tipul long double restul de numere disponibile, nefiind încă dispus să accept că trebuie, dacă mai vreau numere, să mă încumăt cu o bibliotecă arbitrară (pe care mi-o doream tot relativ rapidă, după prima impresie nesatisfăcută despre MIRACL din 13 martie). Calculul sumei divizorilor numărului, acum îl făceam (începutul lunii mai) luând întâi deoparte puterea lui 2 la care se împărțea numărul și făcând suma parțială care este 2 * ea (puterea de 2) - 1; aveam și cel mai mare divizor impar al numărului, de obținut imediat împărțind numărul la putere, și lui trebuia să-i fac de asemenea suma divizorilor, parcurgându-i zona de la radical în jos și adunând divizorul găsit sub radical cu cel corespunzător de peste radical. Deja zona acoperită era mai mică decât înainte (când luam numărul în întregul lui și-l parcurgeam de la radical în jos cu tot cu 2), încât se făcea mai repede coborârea; nu mai era nevoie de verificat numere pare, deci nici 2, și suma divizorilor celui mai mare divizor impar al numărului trebuia înmulțită cu cea parțială de la puterea lui 2 a numărului, ca așa să iasă suma toată. Era o variantă eficientizată a calculului sumei divizorilor pe principiul parcurgerii radical-jos, dar pentru numerele prea mari nu mergea.

Și oricum, era și aici de durată, dacă alegeam de fapt să calculez așa sumele mai multor numere la rând, care numere să fie ele însele divizorii altui număr mai mare pe care să-l fi parcurs de la radical în jos. Că așa căutam numere noi în perioada noiembrie 2009 - mai 2010: luam câte un număr mare (ca multiperfect ori semiperfect) și îi porneam la parcurs divizorii, de la radical în jos, nefăcând de fiecare dată extracția radicalului și comparând cu un număr mic pentru capătul parcurgerii; fiecare divizor găsit sub radical, cu tot cu perechea de peste radical, era considerat un potențial nou număr cu fracția abundenței maxim egală cu 10 (fără șeptari, ne amintim că îi lăsam deoparte) și eu trebuia să îi calculez suma divizorilor. Și lui, și perechii de sus (care devenea tot mai mare și se îngreuna coborârea sub radical, durând zile și nopți). Apoi să verific cum se împarte ea la număr, cu marje de erori specifice virgulei mobile reale, încercând să am un rezultat corect pe ecran, pentru că numărul cu abundență fracționată corespunzător trebuia pus pe ecran.
     
      Și profitam de faptul că AMIBIOS-ul plăcii ASRock dădea voie la overclocking, punând procesorul AMD să meargă cu vreo 10 procente mai repede, pentru numere.

Comentarii

Postări populare