Newsgroup: it.comp.lang.perl
Titolo: Problema con BigInt

Salve , ho scritto alcune llinee di codice che leggono un valore
numerico molto grande da un file e di questo numero ne fa il logaritmo
in base x.
Tutto funziona correttamente, ma ho scoperto che quando il numero da
passare alla finzione logaritno inizia con 1 il sistema si blocca.
P.es LOG (base 321) di
758769876576243675697560876587376163649613985764856978 è ok
mentre LOG (base 321) di
1758769876576243675697560876587376163649613985764856978  mi blocca
tutto l'eseguibile.
Per fare questo tipo di calcoli utilizzo le librerie BigFloat e BigInt
Qualcono può suggerirmi una soluzione?
Grazie
Marco


Potresti inviare uno script dove si evidenzia il problema di cui
parli?
Ciao e grazie,
   Flavio.

Menecmo wrote:

A me funziona correttamente*.
Prova ad eseguire il codice in calce (che utilizza proprio i tuoi dati 
di test): se ti funziona, vuol dire che il tuo errore non dipende da 
BigInt ma da altro (lettura del file, passaggio dei dati ecc.)
Se invece non ti dovesse funzionare, come ha già detto Flavio, per 
provare a capirci qualcosa dovresti riportare perlomeno:
* la porzione rilevante del codice;
* il messaggio d'errore;
* la versione di Math::BigInt (e delle eventuali librerie di backend 
adoperate).
(*)
In effetti un bug nel metodo blog() di Math::BigInt (1.89) credo di 
averlo trovato, ma riguarda soltanto la precisione dei calcoli e non 
provoca alcun /blocco/ (e non inficia lo snippet sottostante).
---
use strict;
use warnings;
use Math::BigFloat;
$| = 1;
my $arg  = '1758769876576243675697560876587376163649613985764856978';
my $base = Math::BigFloat->new(321);
my $log  = Math::BigFloat->new($arg);
print $log->blog($base, 70),             "\n"; # Log in base $base
print $base->bpow($log, 70)->bfround(0), "\n"; # L'inverso è uguale...
print $arg, "\n";                              # ... ad $arg? OK!

 - Mostra testo citato -

 $log=Math::BigFloat::blog($x,$base);  #eseguo il logaritmo
Questo è certamente sbagliato.
Poi non so se sia proprio questa la causa del tuo problema, ma è 
possibile (leggi oltre).
blog() non è un metodo di classe, ed i suoi argomenti sono il primo la 
base del logaritmo e il secondo la /accuracy/ (entrambi opzionali).
Certo però che è veramente strano che in quel modo possa mai aver 
funzionato, e la causa dei /blocchi/ ipotizzo possa essere semplicemente 
che gli hai passato una accuracy molto grande (come secondo parametro di 
blog(), credendo fosse la base del logaritmo) e che pertanto il 
programma è rimasto in esecuzione per lungo tempo (la accuracy incide 
sui tempi di calcolo ovviamente).
Prova a sostituire quelle due righe con:
$base = $w; # se è piccola, altrimenti conserva la tua istruzione.
$log = Math::BigFloat->new($x)->blog($base, 70);
Se la tua base è piccola (32-bit) non c'è bisogno di trasformarla in un 
oggetto Big*, con un qualche guadagno in termini velocistici.
Prova anche ad eseguire il codice che ti ho postato, ed eventualmente 
posta a tua volta codice che possa essere rieseguito senza necessità di 
avere i file.
Per il resto, se posso darti un consiglio, non omettere *mai*:
use strict;
use warnings;
ed usa sempre variabili lessicali (my).

 Per il resto, se posso darti un consiglio, non omettere *mai*:
Ti ringrazio del consiglio, sono un neofita


Ti invio questo semplice codice
use Math::BigInt;
use Math::BigFloat;
$x=Math::BigInt->new(1);
$base=Math::BigInt->new(1);
$log=Math::BigInt->new(1);
print "inserisci il valore molto grande di cui vuoi trovare il
logaritmo\n";
$x=<STDIN>;
chop ($x);
print "inserisci la base maggiore di 1 (
p.es
 321)\n";
$w=<STDIN>;
chop ($W);
$base=Math::BigFloat->new($w);
$log=Math::BigFloat::blog($x,$base);
#$log = Math::BigFloat->new($x)->blog($base, 70);
print"Risultato = $log\n";
Se utilizzo variabili di 30-40 cifre, funziona anche con l'1 davanti,
ma quando supero le 100 fifre si blocca.
l'unico modo per sbloccarlo è togliere l'1 davanti.
Ti posterei il numero che utilizzo ma essendo abbastanza grande non
vorrei che contravvenise alla netiquette
Ciao
Marco

Con il programmino che ti ho inviato ho iniziato (a base costante 321)
a variare $x.
La velocità di calcolo diminuisce esponenzialmente  all'aumentare
delle cifre fino a bloccare la mia macchina a circa 1400 cifre (quando
il munero inizia con 1).
Se allo stesso numero tolgo l'1 davanti, il risultato è pressochè
istantaneo
Ciao
Marco

 Ti ringrazio del consiglio, sono un neofita
Però a quanto pare ne hai un sacco di informazioni riservate ;-)

 vorrei che contravvenise alla netiquette
Posta pure il codice con le costanti dentro, non c'č problema.
Mettici direttamente i valori che ti danno problemi (base e argomento 
del logaritmo, diciamo il piů piccolo valore che comincia ad andare 
lento e che invece, sostituito (non tolto) l'uno davanti, va veloce).
Quello che contravviene alla netiquette č postare un programma interattivo.
(Scherzo ;-)
E anche sapere la versione di Math::BigInt aiuterebbe.
Se non la sai esegui questo dalla command line:
$ perl -MMath::BigInt -e 'print $Math::BigInt::VERSION'
(se sei su Windows sostituisci i singoli apici con i doppi).

BigInt vers 1.88
Perl vers   5.10.0 Built 1002
Come base del Logaritmo gli do 321 e come valora si rallenta quando
metto il numero qui sotto.
se il primo 1 lo sostituisco (p es con 2) mantenedo inalterato il
retso il sistema diventa velocissimo
Ecco il numerone:
160558644268690751031190747154857184941791903444572604430311278503046661272854308690382204634216367681901487498847220728811870577514542064913098016073745962147398323094204534948035315669022066844223113540332019619834638903995214854853731877878705953901755507040568185749217218950904211339761388493740790352137557532577728135662012836867854227446984384997351268063492451798184632234429810606015908200801140428678977881645462205642473167787905403326440519603128575024308016065569351609550777183515587323812717829040592995257065568468995974089974940945922043540590528122906657405455300216542418632217752312028943993021195925073312781680445381188638063912529450756834011654272150225541764346401206673539989602112295885010451320654649499510577698303691331180518883587187331941709434879727028604238735896522541599625130879032814188521584816623623909071904525133032766023279436317858969465244875439983226278065664335150611754813186759088579183890576367756159762558041366224079883861441441829493034656274291415729987722727013660655644369936919266343909191794137684373621848093318685159099588733750365838770372305245970657981489364451106229528483443977162968948006648176346855574617774887448307568704404456038159791291129788623082964239858607721289319024852803191287620060851002475223285677238703324667097287809696000247968650049187212325136130753128250
Ciao
Marco

 [snip]
In effetti c'è una certa differenza di velocità (anche se sulla mia 
macchina ci mette comunque poco anche con l'1 davanti).
Da cosa dipenda non lo so*, ma c'è un workaround.
Dovresti installare:
Math::GMP
Math::BigInt::GMP
e poi caricare nel tuo programma Math::Float in questo modo:
use Math::BigFloat only => 'GMP';
oppure *alternativamente* installare:
Math::Pari
Math::BigInt::Pari
e poi nel tuo codice:
use Math::BigFloat only => 'Pari';
In questo modo BigInt usa un backend compilato dal C anziché quello 
PurePerl, con enormi guadagni prestazionali. Il problema è che se sei su 
Windows, Math::GMP (o Math::Pari) te lo devi compilare a manella, cosa 
non semplice, e prima ti devi compilare e installare la libreria GMP (o 
Pari), che è ancora più complicato.
L'unica soluzione che vedo, sotto Windows, è installarsi Cygwin:
http://cygwin.com/
(che ha tutte le predette librerie già pacchettizzate).
Sotto Linux non ci sarebbe nessun problema ovviamente.
(*)
Potrebbe anche trattarsi di un bug, o perlomeno di una cosa 
ottimizzabile: puoi provare ad aprire un bug report.
P.S.
Il caricamento di Math::BigInt nel tuo codice lo puoi eliminare, così 
come le istanziazioni dei relativi oggetti, perché non lo stai 
adoperando (in quanto vai a sovrascrivere con delle assegnazioni gli 
oggetti BigInt).

 blog($x,$base,accuracy)
Ah, ho capito dove l'hai trovato :-)
E' una funzione di Math::Big
http://search.cpan.org/~tels/Math-Big-1.12/lib/Math/Big.pm
non di Math::BigInt!
Peraltro la funzione esportata da Math::Big si chiama log() e non blog().
Volendo puoi utilizzarla, ma è una cosa diversa da Math::BigInt ;-)
(Comunque la velocità sarebbe la stessa).

Ti ringrazio tantissimo per l'aiuto che mi stai fornendo.
Tieni presente che ho iniziato a postare perchè ero preso dalla
disperazione di non sapere come andare avanti.
Adesso invece ho due o tre soluzioni da praticare.
Per il momento ho tentato con
Math::Big::log (o blog)
Ma come avevi pronosticato i tempi sono sempre quelli.
Ho il perl package manager e pensavo di farmi aiutare da lui ma i
pacchetti cui fai riferimento non sono in lista
I pacchetti che misevono sono o Math::GMP oppure Math::Pari o al
limite cygwin giusto?
Alla fine tra tutte queste alternative qualcosa di buono deve spuntare
fuori.
Domani non ci posso lavorare perciò non controllare aggiornamenti del
post, ma domenica ci lavorerò tutto il giorno.
Intanto inizio con lo scaricare le documentazioni di installazione
Ti ringrazio ancora per l'aiuto
Ciao
\Marco

 Intanto inizio con lo scaricare le documentazioni di installazione
Allora buon lavoro per domanica!
(Poi una volta ci racconterai perché ti tocca calcolare il logaritmo di 
numeri a mille cifre ;-)

 
http://cygwin.com/
Senza che perdiamo tempo con Cygwin, mi viene in mente una banalissima 
(ma efficientissima) soluzione /matematica/.
Possiamo fare così: quando abbiamo un argomento che comincia per 1, lo 
moltiplichiamo per 2, ne calcoliamo il logaritmo, e poi da esso 
sottraiamo il logaritmo di 2 (nella stessa base), in quanto vale 
l'uguaglianza:
log(2*x) = log(x) + log(2)
Inoltre moltiplicando per 2 un intero che comincia per 1, dovremmo 
sempre ottenere un intero che comincia per un numero != 1.
In Perl si tradurrebbe così:
---
my $base_in = <leggi la base>;
my $arg_in  = <leggi l'argomento>;
my $arg_comincia_per_1 = $arg_in =~ /^1/;
my $base  = Math::BigFloat->new($base_in);
my $scale = Math::BigFloat->new(2)->blog($base, 70);
my $log   = Math::BigFloat->new($arg_in, 70);
$log->bmul(2)      if $arg_comincia_per_1;
$log->blog($base);
$log->bsub($scale) if $arg_comincia_per_1;
print $log, "\n";
---
Va come una scheggia ;-)
(E sembra non ci siano neanche problemi di stabilità numerica).
Ovviamente ricontrolla bene il tutto perché le mie reminiscenze sui 
logaritmi sono datate qualche millennio (per non parlare di quelle sulle 
moltiplicazioni ;-)

Ovviamente funziona che è una scheggia.....
ho fatto il controllo con numeri molto più piccoli e che iniziano con
1 ed il risultato è identico, varia solo il tempo di esecuzione la tua
macchina ci mette ci tre o quattro secondi, quella che ti ho inviato
io invece viaggia sui due minuti.
Grazie ancora.
In realtà la soluzione del calcolo per aggirare l'ostacolo la volevo
realizzare anch'io ,ma avrei fatto la strada sbagliata poichè anzichè
moltiplicare per 2 avrei diviso per due, e non conoscendo bene le
proprietà dei logaritmi come te avrei fatto tutta una serie di
compensazioni sui valori ottenuti.
Quando metto quelle tue linee di codice accanto a quello che ho fatto
io si riconosce subito che ci è passato un professionista da quelle
parti
Grazie ancora
\Marco

 Grazie ancora
Di nulla, anzi, scusami tu per avere inizialmente dubitato, quando
invece avevi perfettamente ragione.
E complimenti per aver rilevato quello che, se non proprio un bug, è
perlomeno un'implementazione largamente subottimale.
Anzi, se vuoi segnalarlo:
http://rt.cpan.org/Public/Dist/Display.html?Name=Math-BigInt
(altrimenti lo farò io tra qualche tempo).
Ciao!